mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-01 22:55:23 +00:00
b54c4bdc95
from the prior available width. Some real-world test cases sped up 1-2 orders of magnitude for resize reflow. Initial reflow is unchanged because we still need to get pass1 metrics on all content. fixed nsCSSBlockFrame.cpp to take margins into account when computing parent's available width fixed distribution of excess space to table cells when table is bigger than the content it holds. not quite finished yet, but better.
1454 lines
49 KiB
C++
1454 lines
49 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* The contents of this file are subject to the Netscape Public License
|
|
* Version 1.0 (the "NPL"); you may not use this file except in
|
|
* compliance with the NPL. You may obtain a copy of the NPL at
|
|
* http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
|
* for the specific language governing rights and limitations under the
|
|
* NPL.
|
|
*
|
|
* The Initial Developer of this code under the NPL is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
|
* Reserved.
|
|
*/
|
|
#include "nsTableRowFrame.h"
|
|
#include "nsIRenderingContext.h"
|
|
#include "nsIPresContext.h"
|
|
#include "nsIStyleContext.h"
|
|
#include "nsStyleConsts.h"
|
|
#include "nsIContent.h"
|
|
#include "nsIContentDelegate.h"
|
|
#include "nsTableFrame.h"
|
|
#include "nsTableColFrame.h"
|
|
#include "nsTableCellFrame.h"
|
|
#include "nsCellLayoutData.h"
|
|
#include "nsColLayoutData.h"
|
|
#include "nsIView.h"
|
|
#include "nsIPtr.h"
|
|
#include "nsIReflowCommand.h"
|
|
|
|
NS_DEF_PTR(nsIStyleContext);
|
|
|
|
const nsIID kTableRowFrameCID = NS_TABLEROWFRAME_CID;
|
|
|
|
#ifdef NS_DEBUG
|
|
static PRBool gsDebug1 = PR_FALSE;
|
|
static PRBool gsDebug2 = PR_FALSE;
|
|
//#define NOISY
|
|
//#define NOISY_FLOW
|
|
#else
|
|
static const PRBool gsDebug1 = PR_FALSE;
|
|
static const PRBool gsDebug2 = PR_FALSE;
|
|
#endif
|
|
|
|
/* ----------- RowReflowState ---------- */
|
|
|
|
struct RowReflowState {
|
|
// Our reflow state
|
|
const nsReflowState& reflowState;
|
|
|
|
// The body's available size (computed from the body's parent)
|
|
nsSize availSize;
|
|
|
|
// Flags for whether the max size is unconstrained
|
|
PRBool unconstrainedWidth;
|
|
PRBool unconstrainedHeight;
|
|
|
|
// Running x-offset
|
|
nscoord x;
|
|
|
|
// Height of tallest cell (excluding cells with rowspan > 1)
|
|
nscoord maxCellHeight; // just the height of the cell frame
|
|
nscoord maxCellVertSpace; // the maximum MAX(cellheight + topMargin + bottomMargin)
|
|
|
|
nsTableFrame *tableFrame;
|
|
|
|
|
|
RowReflowState( nsIPresContext* aPresContext,
|
|
const nsReflowState& aReflowState)
|
|
: reflowState(aReflowState)
|
|
{
|
|
availSize.width = reflowState.maxSize.width;
|
|
availSize.height = reflowState.maxSize.height;
|
|
x=0;
|
|
unconstrainedWidth = PRBool(reflowState.maxSize.width == NS_UNCONSTRAINEDSIZE);
|
|
unconstrainedHeight = PRBool(reflowState.maxSize.height == NS_UNCONSTRAINEDSIZE);
|
|
maxCellHeight = 0;
|
|
maxCellVertSpace = 0;
|
|
tableFrame = nsnull;
|
|
}
|
|
|
|
~RowReflowState() {
|
|
}
|
|
};
|
|
|
|
|
|
|
|
|
|
/* ----------- nsTableRowpFrame ---------- */
|
|
|
|
nsTableRowFrame::nsTableRowFrame(nsIContent* aContent,
|
|
nsIFrame* aParentFrame)
|
|
: nsContainerFrame(aContent, aParentFrame),
|
|
mTallestCell(0),
|
|
mCellMaxTopMargin(0),
|
|
mCellMaxBottomMargin(0),
|
|
mMinRowSpan(1)
|
|
{
|
|
}
|
|
|
|
nsTableRowFrame::~nsTableRowFrame()
|
|
{
|
|
}
|
|
|
|
void nsTableRowFrame::ResetMaxChildHeight()
|
|
{
|
|
mTallestCell=0;
|
|
mCellMaxTopMargin=0;
|
|
mCellMaxBottomMargin=0;
|
|
}
|
|
|
|
void nsTableRowFrame::SetMaxChildHeight(nscoord aChildHeight, nscoord aTopMargin, nscoord aBottomMargin)
|
|
{
|
|
if (mTallestCell<aChildHeight)
|
|
mTallestCell = aChildHeight;
|
|
|
|
if (mCellMaxTopMargin<aTopMargin)
|
|
mCellMaxTopMargin = aTopMargin;
|
|
|
|
if (mCellMaxBottomMargin<aBottomMargin)
|
|
mCellMaxBottomMargin = aBottomMargin;
|
|
}
|
|
|
|
NS_METHOD nsTableRowFrame::Paint(nsIPresContext& aPresContext,
|
|
nsIRenderingContext& aRenderingContext,
|
|
const nsRect& aDirtyRect)
|
|
{
|
|
// for debug...
|
|
/*
|
|
if (nsIFrame::GetShowFrameBorders()) {
|
|
aRenderingContext.SetColor(NS_RGB(0,0,128));
|
|
aRenderingContext.DrawRect(0, 0, mRect.width, mRect.height);
|
|
}
|
|
*/
|
|
|
|
PaintChildren(aPresContext, aRenderingContext, aDirtyRect);
|
|
return NS_OK;
|
|
}
|
|
|
|
// aDirtyRect is in our coordinate system
|
|
// child rect's are also in our coordinate system
|
|
/** overloaded method from nsContainerFrame. The difference is that
|
|
* we don't want to clip our children, so a cell can do a rowspan
|
|
*/
|
|
void nsTableRowFrame::PaintChildren(nsIPresContext& aPresContext,
|
|
nsIRenderingContext& aRenderingContext,
|
|
const nsRect& aDirtyRect)
|
|
{
|
|
nsIFrame* kid = mFirstChild;
|
|
while (nsnull != kid) {
|
|
nsIView *pView;
|
|
|
|
kid->GetView(pView);
|
|
if (nsnull == pView) {
|
|
nsRect kidRect;
|
|
kid->GetRect(kidRect);
|
|
nsRect damageArea;
|
|
PRBool overlap = damageArea.IntersectRect(aDirtyRect, kidRect);
|
|
if (overlap) {
|
|
// Translate damage area into kid's coordinate system
|
|
nsRect kidDamageArea(damageArea.x - kidRect.x,
|
|
damageArea.y - kidRect.y,
|
|
damageArea.width, damageArea.height);
|
|
aRenderingContext.PushState();
|
|
aRenderingContext.Translate(kidRect.x, kidRect.y);
|
|
kid->Paint(aPresContext, aRenderingContext, kidDamageArea);
|
|
if (nsIFrame::GetShowFrameBorders()) {
|
|
aRenderingContext.SetColor(NS_RGB(255,0,0));
|
|
aRenderingContext.DrawRect(0, 0, kidRect.width, kidRect.height);
|
|
}
|
|
aRenderingContext.PopState();
|
|
}
|
|
} else {
|
|
NS_RELEASE(pView);
|
|
}
|
|
kid->GetNextSibling(kid);
|
|
}
|
|
}
|
|
|
|
void nsTableRowFrame::SetRowIndex (int aRowIndex)
|
|
{
|
|
mRowIndex = aRowIndex;
|
|
}
|
|
|
|
/** returns the height of the tallest child in this row (ignoring any cell with rowspans) */
|
|
nscoord nsTableRowFrame::GetTallestChild() const
|
|
{
|
|
return mTallestCell;
|
|
}
|
|
|
|
nscoord nsTableRowFrame::GetChildMaxTopMargin() const
|
|
{
|
|
return mCellMaxTopMargin;
|
|
}
|
|
|
|
nscoord nsTableRowFrame::GetChildMaxBottomMargin() const
|
|
{
|
|
return mCellMaxBottomMargin;
|
|
}
|
|
|
|
PRInt32 nsTableRowFrame::GetMaxColumns() const
|
|
{
|
|
int sum = 0;
|
|
nsTableCellFrame *cell=(nsTableCellFrame *)mFirstChild;
|
|
while (nsnull!=cell) {
|
|
sum += cell->GetColSpan();
|
|
cell->GetNextSibling((nsIFrame *&)cell);
|
|
}
|
|
return sum;
|
|
}
|
|
|
|
/* GetMinRowSpan is needed for deviant cases where every cell in a row has a rowspan > 1.
|
|
* It sets mMinRowSpan, which is used in FixMinCellHeight and PlaceChild
|
|
*/
|
|
void nsTableRowFrame::GetMinRowSpan()
|
|
{
|
|
PRInt32 minRowSpan=-1;
|
|
nsIFrame *frame=mFirstChild;
|
|
while (nsnull!=frame)
|
|
{
|
|
PRInt32 rowSpan = ((nsTableCellFrame *)frame)->GetRowSpan();
|
|
if (-1==minRowSpan)
|
|
minRowSpan = rowSpan;
|
|
else if (minRowSpan>rowSpan)
|
|
minRowSpan = rowSpan;
|
|
frame->GetNextSibling(frame);
|
|
}
|
|
mMinRowSpan = minRowSpan;
|
|
}
|
|
|
|
void nsTableRowFrame::FixMinCellHeight()
|
|
{
|
|
nsIFrame *frame=mFirstChild;
|
|
while (nsnull!=frame)
|
|
{
|
|
PRInt32 rowSpan = ((nsTableCellFrame *)frame)->GetRowSpan();
|
|
if (mMinRowSpan==rowSpan)
|
|
{
|
|
nsRect rect;
|
|
frame->GetRect(rect);
|
|
if (rect.height > mTallestCell)
|
|
mTallestCell = rect.height;
|
|
}
|
|
frame->GetNextSibling(frame);
|
|
}
|
|
}
|
|
|
|
// Position and size aKidFrame and update our reflow state. The origin of
|
|
// aKidRect is relative to the upper-left origin of our frame, and includes
|
|
// any left/top margin.
|
|
void nsTableRowFrame::PlaceChild(nsIPresContext* aPresContext,
|
|
RowReflowState& aState,
|
|
nsIFrame* aKidFrame,
|
|
const nsRect& aKidRect,
|
|
nsSize* aMaxElementSize,
|
|
nsSize* aKidMaxElementSize)
|
|
{
|
|
if (PR_TRUE==gsDebug1)
|
|
printf ("row: placing cell at %d, %d, %d, %d\n",
|
|
aKidRect.x, aKidRect.y, aKidRect.width, aKidRect.height);
|
|
|
|
// Place and size the child
|
|
aKidFrame->SetRect(aKidRect);
|
|
|
|
// update the running total for the row width
|
|
aState.x += aKidRect.width;
|
|
|
|
// Update the maximum element size
|
|
PRInt32 rowSpan = ((nsTableCellFrame*)aKidFrame)->GetRowSpan();
|
|
if (nsnull != aMaxElementSize)
|
|
{
|
|
aMaxElementSize->width += aKidMaxElementSize->width;
|
|
if ((mMinRowSpan==rowSpan) && (aKidMaxElementSize->height>aMaxElementSize->height))
|
|
{
|
|
aMaxElementSize->height = aKidMaxElementSize->height;
|
|
}
|
|
}
|
|
|
|
if (mMinRowSpan == rowSpan)
|
|
{
|
|
// Update maxCellHeight
|
|
if (aKidRect.height > aState.maxCellHeight)
|
|
aState.maxCellHeight = aKidRect.height;
|
|
|
|
// Update maxCellVertSpace
|
|
nsMargin margin;
|
|
|
|
if (aState.tableFrame->GetCellMarginData((nsTableCellFrame *)aKidFrame, margin) == NS_OK)
|
|
{
|
|
nscoord height = aKidRect.height + margin.top + margin.bottom;
|
|
|
|
if (height > aState.maxCellVertSpace)
|
|
aState.maxCellVertSpace = height;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Reflow the frames we've already created
|
|
*
|
|
* @param aPresContext presentation context to use
|
|
* @param aState current inline state
|
|
* @return true if we successfully reflowed all the mapped children and false
|
|
* otherwise, e.g. we pushed children to the next in flow
|
|
*/
|
|
PRBool nsTableRowFrame::ReflowMappedChildren(nsIPresContext* aPresContext,
|
|
RowReflowState& aState,
|
|
nsSize* aMaxElementSize)
|
|
{
|
|
#ifdef NS_DEBUG
|
|
VerifyLastIsComplete();
|
|
#endif
|
|
#ifdef NOISY
|
|
ListTag(stdout);
|
|
printf(": reflow mapped (childCount=%d) [%d,%d,%c]\n",
|
|
mChildCount,
|
|
mFirstContentOffset, mLastContentOffset,
|
|
(mLastContentIsComplete ? 'T' : 'F'));
|
|
#ifdef NOISY_FLOW
|
|
{
|
|
nsTableRowFrame* flow = (nsTableRowFrame*) mNextInFlow;
|
|
while (flow != 0) {
|
|
printf(" %p: [%d,%d,%c]\n",
|
|
flow, flow->mFirstContentOffset, flow->mLastContentOffset,
|
|
(flow->mLastContentIsComplete ? 'T' : 'F'));
|
|
flow = (nsTableRowFrame*) flow->mNextInFlow;
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
NS_PRECONDITION(nsnull != mFirstChild, "no children");
|
|
|
|
PRInt32 childCount = 0;
|
|
nsIFrame* prevKidFrame = nsnull;
|
|
|
|
// Remember our original mLastContentIsComplete so that if we end up
|
|
// having to push children, we have the correct value to hand to
|
|
// PushChildren.
|
|
PRBool lastContentIsComplete = mLastContentIsComplete;
|
|
|
|
nsSize kidMaxElementSize;
|
|
nsSize* pKidMaxElementSize = (nsnull != aMaxElementSize) ? &kidMaxElementSize : nsnull;
|
|
PRBool result = PR_TRUE;
|
|
nscoord maxCellTopMargin = 0;
|
|
nscoord maxCellBottomMargin = 0;
|
|
|
|
for (nsIFrame* kidFrame = mFirstChild; nsnull != kidFrame; ) {
|
|
nsSize kidAvailSize(aState.availSize);
|
|
if (0>=kidAvailSize.height)
|
|
kidAvailSize.height = 1; // XXX: HaCk - we don't handle negative heights yet
|
|
|
|
nsReflowMetrics desiredSize(pKidMaxElementSize);
|
|
desiredSize.width=desiredSize.height=desiredSize.ascent=desiredSize.descent=0;
|
|
nsReflowStatus status;
|
|
|
|
nsMargin kidMargin;
|
|
aState.tableFrame->GetCellMarginData((nsTableCellFrame *)kidFrame,kidMargin);
|
|
if (kidMargin.top > maxCellTopMargin)
|
|
maxCellTopMargin = kidMargin.top;
|
|
if (kidMargin.bottom > maxCellBottomMargin)
|
|
maxCellBottomMargin = kidMargin.bottom;
|
|
|
|
// Figure out the amount of available size for the child (subtract
|
|
// off the top margin we are going to apply to it)
|
|
if (PR_FALSE == aState.unconstrainedHeight)
|
|
{
|
|
kidAvailSize.height -= kidMargin.top + kidMargin.bottom;
|
|
}
|
|
// Subtract off for left and right margin
|
|
// XXX Why bother doing this if we always set the available width
|
|
// based on the column width below?
|
|
if (PR_FALSE == aState.unconstrainedWidth) {
|
|
kidAvailSize.width -= kidMargin.left + kidMargin.right;
|
|
}
|
|
|
|
// Compute the x-origin for the child, taking into account straddlers (cells from prior
|
|
// rows with rowspans > 1)
|
|
nscoord x = kidMargin.left;
|
|
PRInt32 cellColIndex = ((nsTableCellFrame *)kidFrame)->GetColIndex();
|
|
for (PRInt32 colIndex=0; colIndex<cellColIndex; colIndex++)
|
|
{
|
|
x += aState.tableFrame->GetColumnWidth(colIndex);
|
|
}
|
|
|
|
// at this point, we know the column widths.
|
|
// so we get the avail width from the known column widths
|
|
nsCellLayoutData *cellData = aState.tableFrame->GetCellLayoutData((nsTableCellFrame *)kidFrame);
|
|
PRInt32 cellStartingCol = ((nsTableCellFrame *)kidFrame)->GetColIndex();
|
|
PRInt32 cellColSpan = ((nsTableCellFrame *)kidFrame)->GetColSpan();
|
|
nscoord availWidth = 0;
|
|
for (PRInt32 numColSpan=0; numColSpan<cellColSpan; numColSpan++)
|
|
availWidth += aState.tableFrame->GetColumnWidth(cellStartingCol+numColSpan);
|
|
kidAvailSize.width = availWidth;
|
|
if (availWidth != ((nsTableCellFrame *)kidFrame)->GetPriorAvailWidth())
|
|
{
|
|
// Reflow the child
|
|
kidFrame->WillReflow(*aPresContext);
|
|
kidFrame->MoveTo(x, kidMargin.top);
|
|
nsReflowState kidReflowState(kidFrame, aState.reflowState, kidAvailSize,
|
|
eReflowReason_Resize);
|
|
status = ReflowChild(kidFrame, aPresContext, desiredSize, kidReflowState);
|
|
|
|
if (gsDebug1)
|
|
{
|
|
if (nsnull!=pKidMaxElementSize)
|
|
printf("reflow of cell returned result = %s with desired=%d,%d, min = %d,%d\n",
|
|
NS_FRAME_IS_COMPLETE(status)?"complete":"NOT complete",
|
|
desiredSize.width, desiredSize.height,
|
|
pKidMaxElementSize->width, pKidMaxElementSize->height);
|
|
else
|
|
printf("reflow of cell returned result = %s with desired=%d,%d, min = nsnull\n",
|
|
NS_FRAME_IS_COMPLETE(status)?"complete":"NOT complete",
|
|
desiredSize.width, desiredSize.height);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
nsRect cellRect;
|
|
kidFrame->GetRect(cellRect);
|
|
desiredSize.width = cellRect.width;
|
|
desiredSize.height = cellRect.height;
|
|
status = NS_FRAME_COMPLETE; // XXX: in paginated world, this doesn't work!
|
|
}
|
|
|
|
// Did the child fit?
|
|
if ((kidFrame != mFirstChild) &&
|
|
((kidAvailSize.height <= 0) ||
|
|
(desiredSize.height > kidAvailSize.height)))
|
|
{
|
|
// The child's height is too big to fit at all in our remaining space,
|
|
// and it's not our first child.
|
|
|
|
// Since we are giving the next-in-flow our last child, we
|
|
// give it our original mLastContentIsComplete, too (in case we
|
|
// are pushing into an empty next-in-flow)
|
|
PushChildren(kidFrame, prevKidFrame, lastContentIsComplete);
|
|
SetLastContentOffset(prevKidFrame);
|
|
|
|
// Our mLastContentIsComplete was already set by the last kid we
|
|
// reflowed reflow's status
|
|
result = PR_FALSE;
|
|
break;
|
|
}
|
|
|
|
// Place the child after taking into account it's margin and attributes
|
|
nscoord specifiedHeight = 0;
|
|
nscoord cellHeight = desiredSize.height;
|
|
nsIStyleContextPtr kidSC;
|
|
kidFrame->GetStyleContext(aPresContext, kidSC.AssignRef());
|
|
const nsStylePosition* kidPosition = (const nsStylePosition*)
|
|
kidSC->GetStyleData(eStyleStruct_Position);
|
|
switch (kidPosition->mHeight.GetUnit()) {
|
|
case eStyleUnit_Coord:
|
|
specifiedHeight = kidPosition->mHeight.GetCoordValue();
|
|
break;
|
|
|
|
case eStyleUnit_Inherit:
|
|
// XXX for now, do nothing
|
|
default:
|
|
case eStyleUnit_Auto:
|
|
break;
|
|
}
|
|
if (specifiedHeight>cellHeight)
|
|
cellHeight = specifiedHeight;
|
|
|
|
nscoord cellWidth = desiredSize.width;
|
|
// begin special Nav4 compatibility code
|
|
if (0==cellWidth)
|
|
{
|
|
cellWidth = aState.tableFrame->GetColumnWidth(cellColIndex);
|
|
}
|
|
// end special Nav4 compatibility code
|
|
|
|
// Adjust the running x-offset
|
|
aState.x = x;
|
|
|
|
// Place the child
|
|
nsRect kidRect (x, kidMargin.top, cellWidth, cellHeight);
|
|
|
|
PlaceChild(aPresContext, aState, kidFrame, kidRect, aMaxElementSize,
|
|
pKidMaxElementSize);
|
|
childCount++;
|
|
|
|
// Remember where we just were in case we end up pushing children
|
|
prevKidFrame = kidFrame;
|
|
|
|
// Update mLastContentIsComplete now that this kid fits
|
|
mLastContentIsComplete = NS_FRAME_IS_COMPLETE(status);
|
|
|
|
/* Rows should not create continuing frames for cells
|
|
* unless they absolutely have to!
|
|
* check to see if this is absolutely necessary (with new params from troy)
|
|
* otherwise PushChildren and bail.
|
|
*/
|
|
// Special handling for incomplete children
|
|
if (NS_FRAME_IS_NOT_COMPLETE(status)) {
|
|
// XXX It's good to assume that we might still have room
|
|
// even if the child didn't complete (floaters will want this)
|
|
nsIFrame* kidNextInFlow;
|
|
|
|
kidFrame->GetNextInFlow(kidNextInFlow);
|
|
PRBool lastContentIsComplete = mLastContentIsComplete;
|
|
if (nsnull == kidNextInFlow) {
|
|
// No the child isn't complete, and it doesn't have a next in flow so
|
|
// create a continuing frame. This hooks the child into the flow.
|
|
nsIFrame* continuingFrame;
|
|
nsIStyleContext* kidSC;
|
|
kidFrame->GetStyleContext(aPresContext, kidSC);
|
|
kidFrame->CreateContinuingFrame(aPresContext, this, kidSC,
|
|
continuingFrame);
|
|
NS_RELEASE(kidSC);
|
|
|
|
// Insert the frame. We'll reflow it next pass through the loop
|
|
nsIFrame* nextSib;
|
|
|
|
kidFrame->GetNextSibling(nextSib);
|
|
continuingFrame->SetNextSibling(nextSib);
|
|
kidFrame->SetNextSibling(continuingFrame);
|
|
if (nsnull == nextSib) {
|
|
// Assume that the continuation frame we just created is
|
|
// complete, for now. It will get reflowed by our
|
|
// next-in-flow (we are going to push it now)
|
|
lastContentIsComplete = PR_TRUE;
|
|
}
|
|
}
|
|
// We've used up all of our available space so push the remaining
|
|
// children to the next-in-flow
|
|
nsIFrame* nextSibling;
|
|
|
|
kidFrame->GetNextSibling(nextSibling);
|
|
if (nsnull != nextSibling) {
|
|
PushChildren(nextSibling, kidFrame, lastContentIsComplete);
|
|
SetLastContentOffset(prevKidFrame);
|
|
}
|
|
result = PR_FALSE;
|
|
break;
|
|
}
|
|
|
|
// Add back in the left and right margins, because one row does not
|
|
// impact another row's width
|
|
if (PR_FALSE == aState.unconstrainedWidth) {
|
|
kidAvailSize.width += kidMargin.left + kidMargin.right;
|
|
}
|
|
|
|
// Get the next child
|
|
kidFrame->GetNextSibling(kidFrame);
|
|
|
|
}
|
|
|
|
SetMaxChildHeight(aState.maxCellHeight,maxCellTopMargin, maxCellBottomMargin); // remember height of tallest child who doesn't have a row span
|
|
|
|
// Update the child count
|
|
mChildCount = childCount;
|
|
|
|
#ifdef NS_DEBUG
|
|
NS_POSTCONDITION(LengthOf(mFirstChild) == mChildCount, "bad child count");
|
|
|
|
nsIFrame* lastChild;
|
|
PRInt32 lastIndexInParent;
|
|
|
|
LastChild(lastChild);
|
|
lastChild->GetContentIndex(lastIndexInParent);
|
|
NS_POSTCONDITION(lastIndexInParent == mLastContentOffset, "bad last content offset");
|
|
#endif
|
|
|
|
#ifdef NS_DEBUG
|
|
VerifyLastIsComplete();
|
|
#endif
|
|
#ifdef NOISY
|
|
ListTag(stdout);
|
|
printf(": reflow mapped %sok (childCount=%d) [%d,%d,%c]\n",
|
|
(result ? "" : "NOT "),
|
|
mChildCount,
|
|
mFirstContentOffset, mLastContentOffset,
|
|
(mLastContentIsComplete ? 'T' : 'F'));
|
|
#ifdef NOISY_FLOW
|
|
{
|
|
nsTableRowFrame* flow = (nsTableRowFrame*) mNextInFlow;
|
|
while (flow != 0) {
|
|
printf(" %p: [%d,%d,%c]\n",
|
|
flow, flow->mFirstContentOffset, flow->mLastContentOffset,
|
|
(flow->mLastContentIsComplete ? 'T' : 'F'));
|
|
flow = (nsTableRowFrame*) flow->mNextInFlow;
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Try and pull-up frames from our next-in-flow
|
|
*
|
|
* @param aPresContext presentation context to use
|
|
* @param aState current inline state
|
|
* @return true if we successfully pulled-up all the children and false
|
|
* otherwise, e.g. child didn't fit
|
|
*/
|
|
PRBool nsTableRowFrame::PullUpChildren(nsIPresContext* aPresContext,
|
|
RowReflowState& aState,
|
|
nsSize* aMaxElementSize)
|
|
{
|
|
#ifdef NS_DEBUG
|
|
VerifyLastIsComplete();
|
|
#endif
|
|
#ifdef NOISY
|
|
ListTag(stdout);
|
|
printf(": pullup (childCount=%d) [%d,%d,%c]\n",
|
|
mChildCount,
|
|
mFirstContentOffset, mLastContentOffset,
|
|
(mLastContentIsComplete ? 'T' : 'F'));
|
|
#ifdef NOISY_FLOW
|
|
{
|
|
nsTableRowFrame* flow = (nsTableRowFrame*) mNextInFlow;
|
|
while (flow != 0) {
|
|
printf(" %p: [%d,%d,%c]\n",
|
|
flow, flow->mFirstContentOffset, flow->mLastContentOffset,
|
|
(flow->mLastContentIsComplete ? 'T' : 'F'));
|
|
flow = (nsTableRowFrame*) flow->mNextInFlow;
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
nsTableRowFrame* nextInFlow = (nsTableRowFrame*)mNextInFlow;
|
|
nsSize kidMaxElementSize;
|
|
nsSize* pKidMaxElementSize = (nsnull != aMaxElementSize) ? &kidMaxElementSize : nsnull;
|
|
nsSize kidAvailSize(aState.availSize);
|
|
#ifdef NS_DEBUG
|
|
PRInt32 kidIndex = NextChildOffset();
|
|
#endif
|
|
nsIFrame* prevKidFrame;
|
|
|
|
LastChild(prevKidFrame);
|
|
|
|
// This will hold the prevKidFrame's mLastContentIsComplete
|
|
// status. If we have to push the frame that follows prevKidFrame
|
|
// then this will become our mLastContentIsComplete state. Since
|
|
// prevKidFrame is initially our last frame, it's completion status
|
|
// is our mLastContentIsComplete value.
|
|
PRBool prevLastContentIsComplete = mLastContentIsComplete;
|
|
|
|
PRBool result = PR_TRUE;
|
|
|
|
while (nsnull != nextInFlow) {
|
|
nsReflowMetrics desiredSize(pKidMaxElementSize);
|
|
desiredSize.width=desiredSize.height=desiredSize.ascent=desiredSize.descent=0;
|
|
nsReflowStatus status;
|
|
|
|
// Get the next child
|
|
nsIFrame* kidFrame = nextInFlow->mFirstChild;
|
|
|
|
// Any more child frames?
|
|
if (nsnull == kidFrame) {
|
|
// No. Any frames on its overflow list?
|
|
if (nsnull != nextInFlow->mOverflowList) {
|
|
// Move the overflow list to become the child list
|
|
// NS_ABORT();
|
|
nextInFlow->AppendChildren(nextInFlow->mOverflowList);
|
|
nextInFlow->mOverflowList = nsnull;
|
|
kidFrame = nextInFlow->mFirstChild;
|
|
} else {
|
|
// We've pulled up all the children, so move to the next-in-flow.
|
|
nextInFlow->GetNextInFlow((nsIFrame*&)nextInFlow);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// See if the child fits in the available space. If it fits or
|
|
// it's splittable then reflow it. The reason we can't just move
|
|
// it is that we still need ascent/descent information
|
|
nsSize kidFrameSize(0,0);
|
|
nsSplittableType kidIsSplittable;
|
|
|
|
kidFrame->GetSize(kidFrameSize);
|
|
kidFrame->IsSplittable(kidIsSplittable);
|
|
|
|
if ((kidFrameSize.height > aState.availSize.height) &&
|
|
NS_FRAME_IS_NOT_SPLITTABLE(kidIsSplittable)) {
|
|
result = PR_FALSE;
|
|
mLastContentIsComplete = prevLastContentIsComplete;
|
|
break;
|
|
}
|
|
|
|
// we're in a constrained situation, so get the avail width from the known column widths
|
|
nsCellLayoutData *cellData = aState.tableFrame->GetCellLayoutData((nsTableCellFrame *)kidFrame);
|
|
PRInt32 cellStartingCol = ((nsTableCellFrame *)kidFrame)->GetColIndex();
|
|
PRInt32 cellColSpan = ((nsTableCellFrame *)kidFrame)->GetColSpan();
|
|
nscoord availWidth = 0;
|
|
for (PRInt32 numColSpan=0; numColSpan<cellColSpan; numColSpan++)
|
|
availWidth += aState.tableFrame->GetColumnWidth(cellStartingCol+numColSpan);
|
|
NS_ASSERTION(0<availWidth, "illegal width for this column");
|
|
kidAvailSize.width = availWidth;
|
|
nsReflowState kidReflowState(kidFrame, aState.reflowState, kidAvailSize,
|
|
eReflowReason_Resize);
|
|
kidFrame->WillReflow(*aPresContext);
|
|
status = ReflowChild(kidFrame, aPresContext, desiredSize, kidReflowState);
|
|
if (nsnull!=pKidMaxElementSize)
|
|
{
|
|
if (gsDebug1)
|
|
{
|
|
if (nsnull!=pKidMaxElementSize)
|
|
printf("reflow of cell returned result = %s with desired=%d,%d, min = %d,%d\n",
|
|
NS_FRAME_IS_COMPLETE(status)?"complete":"NOT complete",
|
|
desiredSize.width, desiredSize.height,
|
|
pKidMaxElementSize->width, pKidMaxElementSize->height);
|
|
else
|
|
printf("reflow of cell returned result = %s with desired=%d,%d, min = nsnull\n",
|
|
NS_FRAME_IS_COMPLETE(status)?"complete":"NOT complete",
|
|
desiredSize.width, desiredSize.height);
|
|
}
|
|
}
|
|
|
|
// Did the child fit?
|
|
if ((desiredSize.height > aState.availSize.height) && (nsnull != mFirstChild)) {
|
|
// The child is too tall to fit in the available space, and it's
|
|
// not our first child
|
|
result = PR_FALSE;
|
|
mLastContentIsComplete = prevLastContentIsComplete;
|
|
break;
|
|
}
|
|
|
|
// Adjust the running x-offset, taking into account intruders (cells from prior rows with rowspans > 1)
|
|
aState.x = 0;
|
|
PRInt32 cellColIndex = ((nsTableCellFrame *)kidFrame)->GetColIndex();
|
|
for (PRInt32 colIndex=0; colIndex<cellColIndex; colIndex++)
|
|
aState.x += aState.tableFrame->GetColumnWidth(colIndex);
|
|
// Place the child
|
|
nsRect kidRect (aState.x, 0, desiredSize.width, desiredSize.height);
|
|
PlaceChild(aPresContext, aState, kidFrame, kidRect, aMaxElementSize, pKidMaxElementSize);
|
|
|
|
// Remove the frame from its current parent
|
|
kidFrame->GetNextSibling(nextInFlow->mFirstChild);
|
|
nextInFlow->mChildCount--;
|
|
// Update the next-in-flows first content offset
|
|
if (nsnull != nextInFlow->mFirstChild) {
|
|
nextInFlow->SetFirstContentOffset(nextInFlow->mFirstChild);
|
|
}
|
|
|
|
// Link the frame into our list of children
|
|
kidFrame->SetGeometricParent(this);
|
|
nsIFrame* kidContentParent;
|
|
|
|
kidFrame->GetContentParent(kidContentParent);
|
|
if (nextInFlow == kidContentParent) {
|
|
kidFrame->SetContentParent(this);
|
|
}
|
|
if (nsnull == prevKidFrame) {
|
|
mFirstChild = kidFrame;
|
|
SetFirstContentOffset(kidFrame);
|
|
} else {
|
|
prevKidFrame->SetNextSibling(kidFrame);
|
|
}
|
|
kidFrame->SetNextSibling(nsnull);
|
|
mChildCount++;
|
|
|
|
// Remember where we just were in case we end up pushing children
|
|
prevKidFrame = kidFrame;
|
|
prevLastContentIsComplete = mLastContentIsComplete;
|
|
|
|
// Is the child we just pulled up complete?
|
|
mLastContentIsComplete = NS_FRAME_IS_COMPLETE(status);
|
|
if (NS_FRAME_IS_NOT_COMPLETE(status)) {
|
|
// No the child isn't complete
|
|
nsIFrame* kidNextInFlow;
|
|
|
|
kidFrame->GetNextInFlow(kidNextInFlow);
|
|
if (nsnull == kidNextInFlow) {
|
|
// The child doesn't have a next-in-flow so create a
|
|
// continuing frame. The creation appends it to the flow and
|
|
// prepares it for reflow.
|
|
nsIFrame* continuingFrame;
|
|
nsIStyleContext* kidSC;
|
|
kidFrame->GetStyleContext(aPresContext, kidSC);
|
|
kidFrame->CreateContinuingFrame(aPresContext, this, kidSC,
|
|
continuingFrame);
|
|
NS_RELEASE(kidSC);
|
|
NS_ASSERTION(nsnull != continuingFrame, "frame creation failed");
|
|
|
|
// Add the continuing frame to our sibling list and then push
|
|
// it to the next-in-flow. This ensures the next-in-flow's
|
|
// content offsets and child count are set properly. Note that
|
|
// we can safely assume that the continuation is complete so
|
|
// we pass PR_TRUE into PushChidren in case our next-in-flow
|
|
// was just drained and now needs to know it's
|
|
// mLastContentIsComplete state.
|
|
kidFrame->SetNextSibling(continuingFrame);
|
|
|
|
PushChildren(continuingFrame, kidFrame, PR_TRUE);
|
|
|
|
// After we push the continuation frame we don't need to fuss
|
|
// with mLastContentIsComplete beause the continuation frame
|
|
// is no longer on *our* list.
|
|
}
|
|
|
|
// If the child isn't complete then it means that we've used up
|
|
// all of our available space.
|
|
result = PR_FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Update our last content offset
|
|
if (nsnull != prevKidFrame) {
|
|
NS_ASSERTION(IsLastChild(prevKidFrame), "bad last child");
|
|
SetLastContentOffset(prevKidFrame);
|
|
}
|
|
|
|
// We need to make sure the first content offset is correct for any empty
|
|
// next-in-flow frames (frames where we pulled up all the child frames)
|
|
nextInFlow = (nsTableRowFrame*)mNextInFlow;
|
|
if ((nsnull != nextInFlow) && (nsnull == nextInFlow->mFirstChild)) {
|
|
// We have at least one empty frame. Did we succesfully pull up all the
|
|
// child frames?
|
|
if (PR_FALSE == result) {
|
|
// No, so we need to adjust the first content offset of all the empty
|
|
// frames
|
|
AdjustOffsetOfEmptyNextInFlows();
|
|
#ifdef NS_DEBUG
|
|
} else {
|
|
// Yes, we successfully pulled up all the child frames which means all
|
|
// the next-in-flows must be empty. Do a sanity check
|
|
while (nsnull != nextInFlow) {
|
|
NS_ASSERTION(nsnull == nextInFlow->mFirstChild, "non-empty next-in-flow");
|
|
nextInFlow->GetNextInFlow((nsIFrame*&)nextInFlow);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#ifdef NS_DEBUG
|
|
VerifyLastIsComplete();
|
|
#endif
|
|
|
|
#ifdef NOISY
|
|
ListTag(stdout);
|
|
printf(": pullup %sok (childCount=%d) [%d,%d,%c]\n",
|
|
(result ? "" : "NOT "),
|
|
mChildCount,
|
|
mFirstContentOffset, mLastContentOffset,
|
|
(mLastContentIsComplete ? 'T' : 'F'));
|
|
#ifdef NOISY_FLOW
|
|
{
|
|
nsTableRowFrame* flow = (nsTableRowFrame*) mNextInFlow;
|
|
while (flow != 0) {
|
|
printf(" %p: [%d,%d,%c]\n",
|
|
flow, flow->mFirstContentOffset, flow->mLastContentOffset,
|
|
(flow->mLastContentIsComplete ? 'T' : 'F'));
|
|
flow = (nsTableRowFrame*) flow->mNextInFlow;
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Create new frames for content we haven't yet mapped
|
|
*
|
|
* @param aPresContext presentation context to use
|
|
* @param aState current inline state
|
|
* @return NS_FRAME_COMPLETE if all content has been mapped and NS_FRAME_NOT_COMPLETE
|
|
* if we should be continued
|
|
*/
|
|
nsReflowStatus
|
|
nsTableRowFrame::ReflowUnmappedChildren( nsIPresContext* aPresContext,
|
|
RowReflowState& aState,
|
|
nsSize* aMaxElementSize)
|
|
{
|
|
#ifdef NS_DEBUG
|
|
VerifyLastIsComplete();
|
|
#endif
|
|
nsIFrame* kidPrevInFlow = nsnull;
|
|
nsReflowStatus result = NS_FRAME_NOT_COMPLETE;
|
|
|
|
// If we have no children and we have a prev-in-flow then we need to pick
|
|
// up where it left off. If we have children, e.g. we're being resized, then
|
|
// our content offset should already be set correctly...
|
|
if ((nsnull == mFirstChild) && (nsnull != mPrevInFlow)) {
|
|
nsTableRowFrame* prev = (nsTableRowFrame*)mPrevInFlow;
|
|
NS_ASSERTION(prev->mLastContentOffset >= prev->mFirstContentOffset, "bad prevInFlow");
|
|
|
|
mFirstContentOffset = prev->NextChildOffset();
|
|
if (!prev->mLastContentIsComplete) {
|
|
// Our prev-in-flow's last child is not complete
|
|
prev->LastChild(kidPrevInFlow);
|
|
}
|
|
}
|
|
|
|
mLastContentIsComplete = PR_TRUE;
|
|
|
|
// Place our children, one at a time, until we are out of children
|
|
nsSize kidMaxElementSize(0,0);
|
|
nsSize* pKidMaxElementSize = (nsnull != aMaxElementSize) ? &kidMaxElementSize : nsnull;
|
|
PRInt32 kidIndex = NextChildOffset();
|
|
nsIFrame* prevKidFrame;
|
|
|
|
nscoord maxTopMargin = 0;
|
|
nscoord maxBottomMargin = 0;
|
|
|
|
LastChild(prevKidFrame); // XXX remember this...
|
|
|
|
for (;;) {
|
|
|
|
NS_ASSERTION(NS_UNCONSTRAINEDSIZE==aState.availSize.width, "bad size");
|
|
|
|
// Get the next content object
|
|
nsIContent* cell = mContent->ChildAt(kidIndex);
|
|
if (nsnull == cell) {
|
|
result = NS_FRAME_COMPLETE;
|
|
break;
|
|
}
|
|
|
|
// Make sure we still have room left
|
|
if (aState.availSize.height <= 0) {
|
|
// Note: return status was set to NS_FRAME_NOT_COMPLETE above...
|
|
NS_RELEASE(cell); // cell: REFCNT--(a)
|
|
break;
|
|
}
|
|
|
|
nsIFrame* kidFrame;
|
|
nsSize kidAvailSize(aState.availSize);
|
|
// Create a child frame -- always an nsTableCell frame
|
|
nsIStyleContext* kidStyleContext = aPresContext->ResolveStyleContextFor(cell, this, PR_TRUE);
|
|
if (nsnull == kidPrevInFlow) {
|
|
nsIContentDelegate* kidDel = nsnull;
|
|
kidDel = cell->GetDelegate(aPresContext);
|
|
nsresult rv = kidDel->CreateFrame(aPresContext, cell,
|
|
this, kidStyleContext, kidFrame);
|
|
NS_RELEASE(kidDel);
|
|
} else {
|
|
kidPrevInFlow->CreateContinuingFrame(aPresContext, this,
|
|
kidStyleContext, kidFrame);
|
|
}
|
|
// Determine the width we have to work with. By default, it's unconstrained
|
|
const nsStylePosition* cellPosition = (const nsStylePosition*)
|
|
(kidStyleContext->GetStyleData(eStyleStruct_Position));
|
|
if (eStyleUnit_Coord==cellPosition->mWidth.GetUnit())
|
|
{
|
|
kidAvailSize.width = cellPosition->mWidth.GetCoordValue();
|
|
}
|
|
// free the kid's style context
|
|
NS_RELEASE(kidStyleContext);
|
|
|
|
|
|
nsMargin margin(0,0,0,0);
|
|
nscoord topMargin = 0;
|
|
|
|
if (aState.tableFrame->GetCellMarginData((nsTableCellFrame *)kidFrame, margin) == NS_OK)
|
|
{
|
|
topMargin = margin.top;
|
|
}
|
|
|
|
maxTopMargin = PR_MAX(margin.top,maxTopMargin);
|
|
maxBottomMargin = PR_MAX(margin.bottom,maxBottomMargin);
|
|
|
|
// Link child frame into the list of children
|
|
if (nsnull != prevKidFrame) {
|
|
prevKidFrame->SetNextSibling(kidFrame);
|
|
} else {
|
|
mFirstChild = kidFrame; // our first child
|
|
SetFirstContentOffset(kidFrame);
|
|
}
|
|
mChildCount++;
|
|
|
|
// If we're constrained compute the origin for the child frame
|
|
nscoord x = margin.left;
|
|
|
|
// Try to reflow the child into the available space. It might not
|
|
// fit or might need continuing.
|
|
nsReflowMetrics desiredSize(pKidMaxElementSize);
|
|
desiredSize.width=desiredSize.height=desiredSize.ascent=desiredSize.descent=0;
|
|
nsReflowStatus status;
|
|
|
|
// Inform the child it's being reflowed
|
|
kidFrame->WillReflow(*aPresContext);
|
|
kidFrame->MoveTo(x, topMargin);
|
|
nsReflowState kidReflowState(kidFrame, aState.reflowState, kidAvailSize,
|
|
eReflowReason_Initial);
|
|
// Reflow the child into the available space (always unconstrained space)
|
|
status = ReflowChild(kidFrame, aPresContext, desiredSize, kidReflowState);
|
|
nsCellLayoutData *kidLayoutData = new nsCellLayoutData((nsTableCellFrame *)kidFrame, &desiredSize, pKidMaxElementSize);
|
|
((nsTableCellFrame *)kidFrame)->SetCellLayoutData(kidLayoutData);
|
|
if (gsDebug1)
|
|
{
|
|
if (nsnull!=pKidMaxElementSize)
|
|
printf("reflow of cell returned result = %s with desired=%d,%d, min = %d,%d\n",
|
|
NS_FRAME_IS_COMPLETE(status)?"complete":"NOT complete",
|
|
desiredSize.width, desiredSize.height,
|
|
pKidMaxElementSize->width, pKidMaxElementSize->height);
|
|
else
|
|
printf("reflow of cell returned result = %s with desired=%d,%d, min = nsnull\n",
|
|
NS_FRAME_IS_COMPLETE(status)?"complete":"NOT complete",
|
|
desiredSize.width, desiredSize.height);
|
|
}
|
|
NS_RELEASE(cell); // cell: REFCNT--
|
|
|
|
// Did the child fit?
|
|
if ((desiredSize.height > aState.availSize.height) && (nsnull != mFirstChild)) {
|
|
// The child is too wide to fit in the available space, and it's
|
|
// not our first child. Add the frame to our overflow list
|
|
NS_ASSERTION(nsnull == mOverflowList, "bad overflow list");
|
|
mOverflowList = kidFrame;
|
|
prevKidFrame->SetNextSibling(nsnull);
|
|
break;
|
|
}
|
|
|
|
// Update the running x-offset
|
|
aState.x = x;
|
|
|
|
// Place the child
|
|
nsRect kidRect (x, topMargin, desiredSize.width, desiredSize.height);
|
|
PlaceChild(aPresContext, aState, kidFrame, kidRect, aMaxElementSize, pKidMaxElementSize);
|
|
|
|
prevKidFrame = kidFrame;
|
|
kidIndex++;
|
|
|
|
// Did the child complete?
|
|
if (NS_FRAME_IS_NOT_COMPLETE(status)) {
|
|
// If the child isn't complete then it means that we've used up
|
|
// all of our available space
|
|
mLastContentIsComplete = PR_FALSE;
|
|
break;
|
|
}
|
|
kidPrevInFlow = nsnull;
|
|
}
|
|
|
|
SetMaxChildHeight(aState.maxCellHeight, maxTopMargin, maxBottomMargin); // remember height of tallest child who doesn't have a row span
|
|
|
|
// Update the content mapping
|
|
NS_ASSERTION(IsLastChild(prevKidFrame), "bad last child");
|
|
SetLastContentOffset(prevKidFrame);
|
|
#ifdef NS_DEBUG
|
|
PRInt32 len = LengthOf(mFirstChild);
|
|
NS_ASSERTION(len == mChildCount, "bad child count");
|
|
#endif
|
|
#ifdef NS_DEBUG
|
|
VerifyLastIsComplete();
|
|
#endif
|
|
return result;
|
|
}
|
|
|
|
// Recover the reflow state to what it should be if aKidFrame is about
|
|
// to be reflowed
|
|
nsresult nsTableRowFrame::RecoverState(RowReflowState& aState,
|
|
nsIFrame* aKidFrame)
|
|
{
|
|
// Walk the list of children looking for aKidFrame
|
|
nsIFrame* prevKidFrame = nsnull;
|
|
for (nsIFrame* frame = mFirstChild; frame != aKidFrame;) {
|
|
PRInt32 rowSpan = ((nsTableCellFrame*)frame)->GetRowSpan();
|
|
if (mMinRowSpan == rowSpan) {
|
|
nsRect rect;
|
|
frame->GetRect(rect);
|
|
|
|
// Update maxCellHeight
|
|
if (rect.height > aState.maxCellHeight) {
|
|
aState.maxCellHeight = rect.height;
|
|
}
|
|
|
|
// Update maxCellVertHeight
|
|
nsMargin margin;
|
|
|
|
if (aState.tableFrame->GetCellMarginData((nsTableCellFrame *)frame, margin) == NS_OK)
|
|
{
|
|
nscoord height = rect.height + margin.top + margin.bottom;
|
|
if (height > aState.maxCellVertSpace) {
|
|
aState.maxCellVertSpace = height;
|
|
}
|
|
}
|
|
}
|
|
|
|
// XXX We also need to recover the max element size...
|
|
|
|
// Remember the frame that precedes aKidFrame
|
|
prevKidFrame = frame;
|
|
frame->GetNextSibling(frame);
|
|
}
|
|
|
|
if (nsnull != prevKidFrame) {
|
|
nsRect rect;
|
|
|
|
// Set our running x-offset
|
|
prevKidFrame->GetRect(rect);
|
|
aState.x = rect.XMost();
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult nsTableRowFrame::IncrementalReflow(nsIPresContext* aPresContext,
|
|
RowReflowState& aState,
|
|
const nsReflowState& aReflowState,
|
|
nsSize* aMaxElementSize)
|
|
{
|
|
nsresult status;
|
|
|
|
// XXX Deal with the case where the reflow command is targeted at us
|
|
nsIFrame* target;
|
|
aReflowState.reflowCommand->GetTarget(target);
|
|
if (this == target) {
|
|
NS_NOTYETIMPLEMENTED("unexpected reflow command");
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
// Get the next frame in the reflow chain
|
|
nsIFrame* kidFrame;
|
|
aReflowState.reflowCommand->GetNext(kidFrame);
|
|
|
|
// Recover our reflow state
|
|
RecoverState(aState, kidFrame);
|
|
|
|
// Figure out the amount of available space for the child
|
|
nsMargin kidMargin;
|
|
nsSize kidAvailSize(aState.availSize);
|
|
|
|
aState.tableFrame->GetCellMarginData((nsTableCellFrame *)kidFrame,kidMargin);
|
|
|
|
if (PR_FALSE == aState.unconstrainedHeight)
|
|
{
|
|
kidAvailSize.height -= kidMargin.top + kidMargin.bottom;
|
|
}
|
|
|
|
// Compute the x-origin for the child, taking into account straddlers (cells from prior
|
|
// rows with rowspans > 1)
|
|
nscoord x = kidMargin.left;
|
|
PRInt32 cellColIndex = ((nsTableCellFrame *)kidFrame)->GetColIndex();
|
|
for (PRInt32 colIndex=0; colIndex<cellColIndex; colIndex++)
|
|
{
|
|
x += aState.tableFrame->GetColumnWidth(colIndex);
|
|
}
|
|
|
|
// at this point, we know the column widths.
|
|
// so we get the avail width from the known column widths
|
|
nsCellLayoutData *cellData = aState.tableFrame->GetCellLayoutData((nsTableCellFrame *)kidFrame);
|
|
PRInt32 cellStartingCol = ((nsTableCellFrame *)kidFrame)->GetColIndex();
|
|
PRInt32 cellColSpan = ((nsTableCellFrame *)kidFrame)->GetColSpan();
|
|
nscoord availWidth = 0;
|
|
for (PRInt32 numColSpan=0; numColSpan<cellColSpan; numColSpan++)
|
|
availWidth += aState.tableFrame->GetColumnWidth(cellStartingCol+numColSpan);
|
|
kidAvailSize.width = availWidth;
|
|
|
|
// Pass along the reflow command. Reflow the child with an unconstrained
|
|
// width and get its maxElementSize
|
|
nsSize kidMaxElementSize;
|
|
nsReflowMetrics desiredSize(&kidMaxElementSize);
|
|
nsReflowState kidReflowState(kidFrame, aReflowState, kidAvailSize);
|
|
kidFrame->WillReflow(*aPresContext);
|
|
kidFrame->MoveTo(x, kidMargin.top);
|
|
|
|
// XXX Unfortunately we need to reflow the child several times.
|
|
// The first time is for the incremental reflow command. We can't pass in
|
|
// a max width of NS_UNCONSTRAINEDSIZE, because the max width must match
|
|
// the width of the previous reflow...
|
|
status = ReflowChild(kidFrame, aPresContext, desiredSize, kidReflowState);
|
|
|
|
// Now do the regular pass 1 reflow and gather the max width and max element
|
|
// size.
|
|
// XXX It would be nice if we could skip this step and the next step if the
|
|
// column width isn't dependent on the max cell width...
|
|
kidReflowState.reason = eReflowReason_Resize;
|
|
kidReflowState.reflowCommand = nsnull;
|
|
kidReflowState.maxSize.width = NS_UNCONSTRAINEDSIZE;
|
|
status = ReflowChild(kidFrame, aPresContext, desiredSize, kidReflowState);
|
|
|
|
// Update the cell layout data. Note that we need to do this for both
|
|
// the data the cell maintains AND the data the table maintains...
|
|
nsCellLayoutData *kidLayoutData = ((nsTableCellFrame *)kidFrame)->GetCellLayoutData();
|
|
|
|
kidLayoutData->SetDesiredSize(&desiredSize);
|
|
kidLayoutData->SetMaxElementSize(&kidMaxElementSize);
|
|
|
|
kidLayoutData = aState.tableFrame->GetCellLayoutData((nsTableCellFrame*)kidFrame);
|
|
kidLayoutData->SetDesiredSize(&desiredSize);
|
|
kidLayoutData->SetMaxElementSize(&kidMaxElementSize);
|
|
|
|
// Now reflow the cell again this time constraining the width
|
|
// XXX Ignore for now the possibility that the column width has changed...
|
|
kidReflowState.maxSize.width = availWidth;
|
|
status = ReflowChild(kidFrame, aPresContext, desiredSize, kidReflowState);
|
|
|
|
// Place the child after taking into account it's margin and attributes
|
|
nscoord specifiedHeight = 0;
|
|
nscoord cellHeight = desiredSize.height;
|
|
nsIStyleContextPtr kidSC;
|
|
kidFrame->GetStyleContext(aPresContext, kidSC.AssignRef());
|
|
const nsStylePosition* kidPosition = (const nsStylePosition*)
|
|
kidSC->GetStyleData(eStyleStruct_Position);
|
|
switch (kidPosition->mHeight.GetUnit()) {
|
|
case eStyleUnit_Coord:
|
|
specifiedHeight = kidPosition->mHeight.GetCoordValue();
|
|
break;
|
|
|
|
case eStyleUnit_Inherit:
|
|
// XXX for now, do nothing
|
|
default:
|
|
case eStyleUnit_Auto:
|
|
break;
|
|
}
|
|
if (specifiedHeight>cellHeight)
|
|
cellHeight = specifiedHeight;
|
|
|
|
nscoord cellWidth = desiredSize.width;
|
|
// begin special Nav4 compatibility code
|
|
if (0==cellWidth)
|
|
{
|
|
cellWidth = aState.tableFrame->GetColumnWidth(cellColIndex);
|
|
}
|
|
// end special Nav4 compatibility code
|
|
|
|
// Adjust the running x-offset
|
|
aState.x = x;
|
|
|
|
// Now place the child
|
|
nsRect kidRect (x, kidMargin.top, cellWidth, cellHeight);
|
|
|
|
PlaceChild(aPresContext, aState, kidFrame, kidRect, aMaxElementSize,
|
|
&kidMaxElementSize);
|
|
|
|
// Now iterate over the remaining cells, and update our max cell
|
|
// height and our running x-offset
|
|
//
|
|
// We don't know to re-position any of the child frames that follow, because
|
|
// the column width hasn't changed...
|
|
nscoord maxCellTopMargin = 0;
|
|
nscoord maxCellBottomMargin = 0;
|
|
kidFrame->GetNextSibling((nsIFrame*&)kidFrame);
|
|
while (nsnull != kidFrame) {
|
|
PRInt32 rowSpan = ((nsTableCellFrame*)kidFrame)->GetRowSpan();
|
|
nsRect rect;
|
|
kidFrame->GetRect(rect);
|
|
if (mMinRowSpan == rowSpan) {
|
|
// Update maxCellHeight
|
|
if (rect.height > aState.maxCellHeight) {
|
|
aState.maxCellHeight = rect.height;
|
|
}
|
|
|
|
// Update maxCellVertHeight
|
|
nsMargin margin;
|
|
|
|
if (aState.tableFrame->GetCellMarginData((nsTableCellFrame *)kidFrame, margin) == NS_OK)
|
|
{
|
|
nscoord height = rect.height + margin.top + margin.bottom;
|
|
if (height > aState.maxCellVertSpace) {
|
|
aState.maxCellVertSpace = height;
|
|
}
|
|
|
|
// XXX Currently we assume cells have margins, but that isn't correct...
|
|
if (margin.top > maxCellTopMargin) {
|
|
maxCellTopMargin = margin.top;
|
|
}
|
|
if (margin.bottom > maxCellBottomMargin) {
|
|
maxCellBottomMargin = margin.bottom;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set our running x-offset
|
|
aState.x = rect.XMost();
|
|
|
|
// Get the next cell frame
|
|
kidFrame->GetNextSibling((nsIFrame*&)kidFrame);
|
|
}
|
|
|
|
SetMaxChildHeight(aState.maxCellHeight,maxCellTopMargin, maxCellBottomMargin);
|
|
return status;
|
|
}
|
|
|
|
/** Layout the entire row.
|
|
* This method stacks cells horizontally according to HTML 4.0 rules.
|
|
*/
|
|
NS_METHOD
|
|
nsTableRowFrame::Reflow(nsIPresContext* aPresContext,
|
|
nsReflowMetrics& aDesiredSize,
|
|
const nsReflowState& aReflowState,
|
|
nsReflowStatus& aStatus)
|
|
{
|
|
if (gsDebug1==PR_TRUE)
|
|
printf("nsTableRowFrame::Reflow - aMaxSize = %d, %d\n",
|
|
aReflowState.maxSize.width, aReflowState.maxSize.height);
|
|
#ifdef NS_DEBUG
|
|
PreReflowCheck();
|
|
#endif
|
|
|
|
RowReflowState state(aPresContext, aReflowState);
|
|
mContentParent->GetContentParent((nsIFrame*&)(state.tableFrame));
|
|
|
|
if (eReflowReason_Incremental == aReflowState.reason) {
|
|
aStatus = IncrementalReflow(aPresContext, state, aReflowState,
|
|
aDesiredSize.maxElementSize);
|
|
} else {
|
|
// Initialize out parameter
|
|
if (nsnull != aDesiredSize.maxElementSize) {
|
|
aDesiredSize.maxElementSize->width = 0;
|
|
aDesiredSize.maxElementSize->height = 0;
|
|
}
|
|
|
|
// Initialize our internal data
|
|
ResetMaxChildHeight();
|
|
|
|
PRBool reflowMappedOK = PR_TRUE;
|
|
|
|
aStatus = NS_FRAME_COMPLETE;
|
|
|
|
// Check for an overflow list
|
|
MoveOverflowToChildList();
|
|
|
|
// Reflow the existing frames
|
|
if (nsnull != mFirstChild) {
|
|
reflowMappedOK = ReflowMappedChildren(aPresContext, state, aDesiredSize.maxElementSize);
|
|
if (PR_FALSE == reflowMappedOK) {
|
|
aStatus = NS_FRAME_NOT_COMPLETE;
|
|
}
|
|
}
|
|
|
|
// Did we successfully relow our mapped children?
|
|
if (PR_TRUE == reflowMappedOK) {
|
|
// Any space left?
|
|
if (state.availSize.height <= 0) {
|
|
// No space left. Don't try to pull-up children or reflow unmapped
|
|
if (NextChildOffset() < mContent->ChildCount()) {
|
|
aStatus = NS_FRAME_NOT_COMPLETE;
|
|
}
|
|
} else if (NextChildOffset() < mContent->ChildCount()) {
|
|
// Try and pull-up some children from a next-in-flow
|
|
if (PullUpChildren(aPresContext, state, aDesiredSize.maxElementSize)) {
|
|
// If we still have unmapped children then create some new frames
|
|
if (NextChildOffset() < mContent->ChildCount()) {
|
|
aStatus = ReflowUnmappedChildren(aPresContext, state, aDesiredSize.maxElementSize);
|
|
GetMinRowSpan();
|
|
FixMinCellHeight();
|
|
}
|
|
} else {
|
|
// We were unable to pull-up all the existing frames from the
|
|
// next in flow
|
|
aStatus = NS_FRAME_NOT_COMPLETE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Return our desired rect
|
|
aDesiredSize.width = state.x;
|
|
aDesiredSize.height = state.maxCellVertSpace;
|
|
|
|
#ifdef NS_DEBUG
|
|
PostReflowCheck(aStatus);
|
|
#endif
|
|
|
|
if (gsDebug1==PR_TRUE)
|
|
{
|
|
if (nsnull!=aDesiredSize.maxElementSize)
|
|
printf("nsTableRowFrame::RR returning: %s with aDesiredSize=%d,%d, aMES=%d,%d\n",
|
|
NS_FRAME_IS_COMPLETE(aStatus)?"Complete":"Not Complete",
|
|
aDesiredSize.width, aDesiredSize.height,
|
|
aDesiredSize.maxElementSize->width, aDesiredSize.maxElementSize->height);
|
|
else
|
|
printf("nsTableRowFrame::RR returning: %s with aDesiredSize=%d,%d, aMES=NSNULL\n",
|
|
NS_FRAME_IS_COMPLETE(aStatus)?"Complete":"Not Complete",
|
|
aDesiredSize.width, aDesiredSize.height);
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
NS_METHOD
|
|
nsTableRowFrame::CreateContinuingFrame(nsIPresContext* aPresContext,
|
|
nsIFrame* aParent,
|
|
nsIStyleContext* aStyleContext,
|
|
nsIFrame*& aContinuingFrame)
|
|
{
|
|
if (gsDebug1==PR_TRUE) printf("nsTableRowFrame::CreateContinuingFrame\n");
|
|
nsTableRowFrame* cf = new nsTableRowFrame(mContent, aParent);
|
|
if (nsnull == cf) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
PrepareContinuingFrame(aPresContext, aParent, aStyleContext, cf);
|
|
aContinuingFrame = cf;
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult nsTableRowFrame::NewFrame( nsIFrame** aInstancePtrResult,
|
|
nsIContent* aContent,
|
|
nsIFrame* aParent)
|
|
{
|
|
NS_PRECONDITION(nsnull != aInstancePtrResult, "null ptr");
|
|
if (nsnull == aInstancePtrResult) {
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
nsIFrame* it = new nsTableRowFrame(aContent, aParent);
|
|
if (nsnull == it) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
*aInstancePtrResult = it;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_METHOD
|
|
nsTableRowFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr)
|
|
{
|
|
NS_PRECONDITION(0 != aInstancePtr, "null ptr");
|
|
if (NULL == aInstancePtr) {
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
if (aIID.Equals(kTableRowFrameCID)) {
|
|
*aInstancePtr = (void*) (this);
|
|
return NS_OK;
|
|
}
|
|
return nsContainerFrame::QueryInterface(aIID, aInstancePtr);
|
|
}
|
|
|
|
// For Debugging ONLY
|
|
NS_METHOD nsTableRowFrame::MoveTo(nscoord aX, nscoord aY)
|
|
{
|
|
if ((aX != mRect.x) || (aY != mRect.y)) {
|
|
mRect.x = aX;
|
|
mRect.y = aY;
|
|
|
|
nsIView* view;
|
|
GetView(view);
|
|
|
|
// Let the view know
|
|
if (nsnull != view) {
|
|
// Position view relative to it's parent, not relative to our
|
|
// parent frame (our parent frame may not have a view).
|
|
nsIView* parentWithView;
|
|
nsPoint origin;
|
|
GetOffsetFromView(origin, parentWithView);
|
|
view->SetPosition(origin.x, origin.y);
|
|
NS_IF_RELEASE(parentWithView);
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_METHOD nsTableRowFrame::SizeTo(nscoord aWidth, nscoord aHeight)
|
|
{
|
|
mRect.width = aWidth;
|
|
mRect.height = aHeight;
|
|
|
|
nsIView* view;
|
|
GetView(view);
|
|
|
|
// Let the view know
|
|
if (nsnull != view) {
|
|
view->SetDimensions(aWidth, aHeight);
|
|
}
|
|
return NS_OK;
|
|
}
|