the printing of headers and footers. Printing of selection is implemented by the frames figuring out if they are in the selection and painting if they or not they they don't paint. This also only allows the printing of the first page of selections, alothough it is well documented where this is implemeted so it can be removed. Bugs 63426, 31218, 61075 r=dcone,kmcclusk,erik,buster sr=waterson git-svn-id: svn://10.0.0.236/trunk@85624 18797224-902f-48f8-a5cc-f745e15eee43
1688 lines
64 KiB
C++
1688 lines
64 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.1 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS
|
|
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
|
* implied. See the License for the specific language governing
|
|
* rights and limitations under the License.
|
|
*
|
|
* The Original Code is mozilla.org code.
|
|
*
|
|
* The Initial Developer of the Original Code is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All
|
|
* Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*/
|
|
#include "nsTableRowFrame.h"
|
|
#include "nsIRenderingContext.h"
|
|
#include "nsIPresShell.h"
|
|
#include "nsIPresContext.h"
|
|
#include "nsIStyleContext.h"
|
|
#include "nsStyleConsts.h"
|
|
#include "nsIHTMLAttributes.h"
|
|
#include "nsHTMLAtoms.h"
|
|
#include "nsIContent.h"
|
|
#include "nsTableFrame.h"
|
|
#include "nsTableCellFrame.h"
|
|
#include "nsIView.h"
|
|
#include "nsIReflowCommand.h"
|
|
#include "nsCSSRendering.h"
|
|
#include "nsHTMLIIDs.h"
|
|
#include "nsLayoutAtoms.h"
|
|
#include "nsHTMLParts.h"
|
|
#include "nsTableColGroupFrame.h"
|
|
#include "nsTableColFrame.h"
|
|
#include "nsCOMPtr.h"
|
|
// the following header files are required for style optimizations that work only when the child content is really a cell
|
|
#include "nsIHTMLTableCellElement.h"
|
|
// end includes for style optimizations that require real content knowledge
|
|
|
|
|
|
struct nsTableCellReflowState : public nsHTMLReflowState
|
|
{
|
|
nsTableCellReflowState(nsIPresContext* aPresContext,
|
|
const nsHTMLReflowState& aParentReflowState,
|
|
nsIFrame* aFrame,
|
|
const nsSize& aAvailableSpace,
|
|
nsReflowReason aReason);
|
|
|
|
nsTableCellReflowState(nsIPresContext* aPresContext,
|
|
const nsHTMLReflowState& aParentReflowState,
|
|
nsIFrame* aFrame,
|
|
const nsSize& aAvailableSpace);
|
|
|
|
void FixUp(const nsSize& aAvailSpace);
|
|
};
|
|
|
|
nsTableCellReflowState::nsTableCellReflowState(nsIPresContext* aPresContext,
|
|
const nsHTMLReflowState& aParentRS,
|
|
nsIFrame* aFrame,
|
|
const nsSize& aAvailSpace,
|
|
nsReflowReason aReason)
|
|
:nsHTMLReflowState(aPresContext, aParentRS, aFrame, aAvailSpace, aReason)
|
|
{
|
|
FixUp(aAvailSpace);
|
|
}
|
|
|
|
nsTableCellReflowState::nsTableCellReflowState(nsIPresContext* aPresContext,
|
|
const nsHTMLReflowState& aParentRS,
|
|
nsIFrame* aFrame,
|
|
const nsSize& aAvailSpace)
|
|
:nsHTMLReflowState(aPresContext, aParentRS, aFrame, aAvailSpace)
|
|
{
|
|
FixUp(aAvailSpace);
|
|
}
|
|
|
|
void nsTableCellReflowState::FixUp(const nsSize& aAvailSpace)
|
|
{
|
|
// fix the mComputed values during a pass 2 reflow since the cell can be a percentage base
|
|
if (NS_UNCONSTRAINEDSIZE != aAvailSpace.width) {
|
|
if (NS_UNCONSTRAINEDSIZE != mComputedWidth) {
|
|
mComputedWidth = aAvailSpace.width - mComputedBorderPadding.left - mComputedBorderPadding.right;
|
|
mComputedWidth = PR_MAX(0, mComputedWidth);
|
|
}
|
|
if (NS_UNCONSTRAINEDSIZE != mComputedHeight) {
|
|
if (NS_UNCONSTRAINEDSIZE != aAvailSpace.height) {
|
|
mComputedHeight = aAvailSpace.height - mComputedBorderPadding.top - mComputedBorderPadding.bottom;
|
|
mComputedHeight = PR_MAX(0, mComputedHeight);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 'old' is old cached cell's desired size
|
|
// 'new' is new cell's size including style constraints
|
|
static PRBool
|
|
TallestCellGotShorter(nscoord aOld,
|
|
nscoord aNew,
|
|
nscoord aTallest)
|
|
{
|
|
return ((aNew < aOld) && (aOld == aTallest));
|
|
}
|
|
|
|
/* ----------- nsTableRowpFrame ---------- */
|
|
|
|
nsTableRowFrame::nsTableRowFrame()
|
|
: nsHTMLContainerFrame(),
|
|
mAllBits(0)
|
|
{
|
|
mBits.mMinRowSpan = 1;
|
|
mBits.mRowIndex = 0;
|
|
ResetTallestCell(0);
|
|
#ifdef DEBUG_TABLE_REFLOW_TIMING
|
|
mTimer = new nsReflowTimer(this);
|
|
#endif
|
|
}
|
|
|
|
nsTableRowFrame::~nsTableRowFrame()
|
|
{
|
|
#ifdef DEBUG_TABLE_REFLOW_TIMING
|
|
nsTableFrame::DebugReflowDone(this);
|
|
#endif
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsTableRowFrame::Init(nsIPresContext* aPresContext,
|
|
nsIContent* aContent,
|
|
nsIFrame* aParent,
|
|
nsIStyleContext* aContext,
|
|
nsIFrame* aPrevInFlow)
|
|
{
|
|
nsresult rv;
|
|
|
|
// Let the the base class do its initialization
|
|
rv = nsHTMLContainerFrame::Init(aPresContext, aContent, aParent, aContext,
|
|
aPrevInFlow);
|
|
|
|
if (aPrevInFlow) {
|
|
// Set the row index
|
|
nsTableRowFrame* rowFrame = (nsTableRowFrame*)aPrevInFlow;
|
|
|
|
SetRowIndex(rowFrame->GetRowIndex());
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
// Helper function. It marks the table frame as dirty and generates
|
|
// a reflow command
|
|
nsresult
|
|
nsTableRowFrame::AddTableDirtyReflowCommand(nsIPresContext* aPresContext,
|
|
nsIPresShell& aPresShell,
|
|
nsIFrame* aTableFrame)
|
|
{
|
|
nsFrameState frameState;
|
|
nsIFrame* tableParentFrame;
|
|
nsIReflowCommand* reflowCmd;
|
|
nsresult rv;
|
|
|
|
// Mark the table frame as dirty
|
|
aTableFrame->GetFrameState(&frameState);
|
|
frameState |= NS_FRAME_IS_DIRTY;
|
|
aTableFrame->SetFrameState(frameState);
|
|
|
|
// Target the reflow comamnd at its parent frame
|
|
aTableFrame->GetParent(&tableParentFrame);
|
|
rv = NS_NewHTMLReflowCommand(&reflowCmd, tableParentFrame,
|
|
nsIReflowCommand::ReflowDirty);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
// Add the reflow command
|
|
rv = aPresShell.AppendReflowCommand(reflowCmd);
|
|
NS_RELEASE(reflowCmd);
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsTableRowFrame::AppendFrames(nsIPresContext* aPresContext,
|
|
nsIPresShell& aPresShell,
|
|
nsIAtom* aListName,
|
|
nsIFrame* aFrameList)
|
|
{
|
|
// Append the frames
|
|
mFrames.AppendFrames(nsnull, aFrameList);
|
|
|
|
// Add the new cell frames to the table
|
|
nsTableFrame *tableFrame = nsnull;
|
|
nsTableFrame::GetTableFrame(this, tableFrame);
|
|
for (nsIFrame* childFrame = aFrameList; childFrame; childFrame->GetNextSibling(&childFrame)) {
|
|
const nsStyleDisplay *display;
|
|
childFrame->GetStyleData(eStyleStruct_Display, ((const nsStyleStruct *&)display));
|
|
|
|
if (NS_STYLE_DISPLAY_TABLE_CELL == display->mDisplay) {
|
|
// Add the cell to the cell map
|
|
tableFrame->AppendCell(*aPresContext, (nsTableCellFrame&)*childFrame, GetRowIndex());
|
|
}
|
|
}
|
|
|
|
tableFrame->InvalidateColumnWidths();
|
|
|
|
// Reflow the new frames. They're already marked dirty, so generate a reflow
|
|
// command that tells us to reflow our dirty child frames
|
|
nsIReflowCommand* reflowCmd;
|
|
|
|
if (NS_SUCCEEDED(NS_NewHTMLReflowCommand(&reflowCmd, this,
|
|
nsIReflowCommand::ReflowDirty))) {
|
|
aPresShell.AppendReflowCommand(reflowCmd);
|
|
NS_RELEASE(reflowCmd);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsTableRowFrame::InsertFrames(nsIPresContext* aPresContext,
|
|
nsIPresShell& aPresShell,
|
|
nsIAtom* aListName,
|
|
nsIFrame* aPrevFrame,
|
|
nsIFrame* aFrameList)
|
|
{
|
|
// Get the table frame
|
|
nsTableFrame* tableFrame = nsnull;
|
|
nsTableFrame::GetTableFrame(this, tableFrame);
|
|
|
|
// gather the new frames (only those which are cells) into an array
|
|
nsTableCellFrame* prevCellFrame = (nsTableCellFrame *)nsTableFrame::GetFrameAtOrBefore(aPresContext, this, aPrevFrame, nsLayoutAtoms::tableCellFrame);
|
|
nsVoidArray cellChildren;
|
|
for (nsIFrame* childFrame = aFrameList; childFrame; childFrame->GetNextSibling(&childFrame)) {
|
|
nsIAtom* frameType;
|
|
childFrame->GetFrameType(&frameType);
|
|
if (nsLayoutAtoms::tableCellFrame == frameType) {
|
|
cellChildren.AppendElement(childFrame);
|
|
}
|
|
NS_IF_RELEASE(frameType);
|
|
}
|
|
// insert the cells into the cell map
|
|
PRInt32 colIndex = -1;
|
|
if (prevCellFrame) {
|
|
prevCellFrame->GetColIndex(colIndex);
|
|
}
|
|
tableFrame->InsertCells(*aPresContext, cellChildren, GetRowIndex(), colIndex);
|
|
|
|
// Insert the frames in the frame list
|
|
mFrames.InsertFrames(nsnull, aPrevFrame, aFrameList);
|
|
|
|
// Because the number of columns may have changed invalidate the column widths
|
|
tableFrame->InvalidateColumnWidths();
|
|
|
|
// Reflow the new frames. They're already marked dirty, so generate a reflow
|
|
// command that tells us to reflow our dirty child frames
|
|
nsIReflowCommand* reflowCmd;
|
|
|
|
if (NS_SUCCEEDED(NS_NewHTMLReflowCommand(&reflowCmd, this,
|
|
nsIReflowCommand::ReflowDirty))) {
|
|
aPresShell.AppendReflowCommand(reflowCmd);
|
|
NS_RELEASE(reflowCmd);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsTableRowFrame::RemoveFrame(nsIPresContext* aPresContext,
|
|
nsIPresShell& aPresShell,
|
|
nsIAtom* aListName,
|
|
nsIFrame* aOldFrame)
|
|
{
|
|
// Get the table frame
|
|
nsTableFrame* tableFrame=nsnull;
|
|
nsTableFrame::GetTableFrame(this, tableFrame);
|
|
if (tableFrame) {
|
|
nsIAtom* frameType;
|
|
aOldFrame->GetFrameType(&frameType);
|
|
if (nsLayoutAtoms::tableCellFrame == frameType) {
|
|
nsTableCellFrame* cellFrame = (nsTableCellFrame*)aOldFrame;
|
|
PRInt32 colIndex;
|
|
cellFrame->GetColIndex(colIndex);
|
|
tableFrame->RemoveCell(*aPresContext, cellFrame, GetRowIndex());
|
|
|
|
// Remove the frame and destroy it
|
|
mFrames.DestroyFrame(aPresContext, aOldFrame);
|
|
|
|
// cells have possibly shifted into different columns.
|
|
tableFrame->InvalidateColumnWidths();
|
|
|
|
// Because we haven't added any new frames we don't need to do a pass1
|
|
// reflow. Just generate a reflow command so we reflow the table itself.
|
|
// Target the row so that it gets a dirty reflow before a resize reflow
|
|
// in case another cell gets added to the row during a reflow coallesce.
|
|
nsIReflowCommand* reflowCmd;
|
|
|
|
if (NS_SUCCEEDED(NS_NewHTMLReflowCommand(&reflowCmd, this,
|
|
nsIReflowCommand::ReflowDirty))) {
|
|
aPresShell.AppendReflowCommand(reflowCmd);
|
|
NS_RELEASE(reflowCmd);
|
|
}
|
|
}
|
|
NS_IF_RELEASE(frameType);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nscoord
|
|
GetHeightOfRowsSpannedBelowFirst(nsTableCellFrame& aTableCellFrame,
|
|
nsTableFrame& aTableFrame)
|
|
{
|
|
nscoord height = 0;
|
|
nscoord cellSpacingY = aTableFrame.GetCellSpacingY();
|
|
PRInt32 rowSpan = aTableFrame.GetEffectiveRowSpan(aTableCellFrame);
|
|
// add in height of rows spanned beyond the 1st one
|
|
nsIFrame* nextRow = nsnull;
|
|
aTableCellFrame.GetParent((nsIFrame **)&nextRow);
|
|
nextRow->GetNextSibling(&nextRow);
|
|
for (PRInt32 rowX = 1; ((rowX < rowSpan) && nextRow);) {
|
|
nsCOMPtr<nsIAtom> frameType;
|
|
nextRow->GetFrameType(getter_AddRefs(frameType));
|
|
if (nsLayoutAtoms::tableRowFrame == frameType.get()) {
|
|
nsRect rect;
|
|
nextRow->GetRect(rect);
|
|
height += rect.height;
|
|
rowX++;
|
|
}
|
|
height += cellSpacingY;
|
|
nextRow->GetNextSibling(&nextRow);
|
|
}
|
|
return height;
|
|
}
|
|
|
|
|
|
/**
|
|
* Post-reflow hook. This is where the table row does its post-processing
|
|
*/
|
|
void
|
|
nsTableRowFrame::DidResize(nsIPresContext* aPresContext,
|
|
const nsHTMLReflowState& aReflowState)
|
|
{
|
|
// Resize and re-align the cell frames based on our row height
|
|
nsTableFrame* tableFrame;
|
|
nsTableFrame::GetTableFrame(this, tableFrame);
|
|
if (!tableFrame) return;
|
|
nscoord cellSpacingY = tableFrame->GetCellSpacingY();
|
|
|
|
nsTableIterator iter(aPresContext, *this, eTableDIR);
|
|
nsIFrame* cellFrame = iter.First();
|
|
|
|
while (nsnull != cellFrame) {
|
|
const nsStyleDisplay *kidDisplay;
|
|
cellFrame->GetStyleData(eStyleStruct_Display, ((const nsStyleStruct *&)kidDisplay));
|
|
if (NS_STYLE_DISPLAY_TABLE_CELL == kidDisplay->mDisplay) {
|
|
nscoord cellHeight = mRect.height + GetHeightOfRowsSpannedBelowFirst((nsTableCellFrame&) *cellFrame, *tableFrame);
|
|
|
|
// resize the cell's height
|
|
nsSize cellFrameSize;
|
|
cellFrame->GetSize(cellFrameSize);
|
|
//if (cellFrameSize.height!=cellHeight)
|
|
{
|
|
// XXX If the cell frame has a view, then we need to resize
|
|
// it as well. We would like to only do that if the cell's size
|
|
// is changing. Why is the 'if' stmt above commented out?
|
|
cellFrame->SizeTo(aPresContext, cellFrameSize.width, cellHeight);
|
|
// realign cell content based on the new height
|
|
/*nsHTMLReflowMetrics desiredSize(nsnull);
|
|
nsHTMLReflowState kidReflowState(aPresContext, aReflowState,
|
|
cellFrame,
|
|
nsSize(cellFrameSize.width, cellHeight),
|
|
eReflowReason_Resize);*/
|
|
//XXX: the following reflow is necessary for any content of the cell
|
|
// whose height is a percent of the cell's height (maybe indirectly.)
|
|
// But some content crashes when this reflow is issued, to be investigated
|
|
//XXX nsReflowStatus status;
|
|
//ReflowChild(cellFrame, aPresContext, desiredSize, kidReflowState, status);
|
|
|
|
((nsTableCellFrame *)cellFrame)->VerticallyAlignChild(aPresContext, aReflowState, mMaxCellAscent);
|
|
|
|
/* if we're collapsing borders, notify the cell that the border edge length has changed */
|
|
if (NS_STYLE_BORDER_COLLAPSE == tableFrame->GetBorderCollapseStyle()) {
|
|
((nsTableCellFrame *)(cellFrame))->SetBorderEdgeLength(NS_SIDE_LEFT,
|
|
GetRowIndex(),
|
|
cellHeight);
|
|
((nsTableCellFrame *)(cellFrame))->SetBorderEdgeLength(NS_SIDE_RIGHT,
|
|
GetRowIndex(),
|
|
cellHeight);
|
|
}
|
|
}
|
|
}
|
|
// Get the next cell
|
|
cellFrame = iter.Next();
|
|
}
|
|
|
|
// Let our base class do the usual work
|
|
}
|
|
|
|
// returns max-ascent amongst all cells that have 'vertical-align: baseline'
|
|
// *including* cells with rowspans
|
|
nscoord nsTableRowFrame::GetMaxCellAscent() const
|
|
{
|
|
return mMaxCellAscent;
|
|
}
|
|
|
|
#if 0 // nobody uses this
|
|
// returns max-descent amongst all cells that have 'vertical-align: baseline'
|
|
// does *not* include cells with rowspans
|
|
nscoord nsTableRowFrame::GetMaxCellDescent() const
|
|
{
|
|
return mMaxCellDescent;
|
|
}
|
|
#endif
|
|
|
|
/** returns the height of the tallest child in this row (ignoring any cell with rowspans) */
|
|
nscoord nsTableRowFrame::GetTallestCell() const
|
|
{
|
|
return mTallestCell;
|
|
}
|
|
|
|
void
|
|
nsTableRowFrame::ResetTallestCell(nscoord aRowStyleHeight)
|
|
{
|
|
mTallestCell = (NS_UNCONSTRAINEDSIZE == aRowStyleHeight) ? 0 : aRowStyleHeight;
|
|
mMaxCellAscent = 0;
|
|
mMaxCellDescent = 0;
|
|
}
|
|
|
|
void
|
|
nsTableRowFrame::SetTallestCell(nscoord aHeight,
|
|
nscoord aAscent,
|
|
nscoord aDescent,
|
|
nsTableFrame* aTableFrame,
|
|
nsTableCellFrame* aCellFrame)
|
|
{
|
|
NS_ASSERTION((aTableFrame && aCellFrame) , "invalid call");
|
|
if (aHeight != NS_UNCONSTRAINEDSIZE) {
|
|
if (!(aCellFrame->HasVerticalAlignBaseline())) { // only the cell's height matters
|
|
if (mTallestCell < aHeight) {
|
|
PRInt32 rowSpan = aTableFrame->GetEffectiveRowSpan(*aCellFrame);
|
|
if (rowSpan == 1) {
|
|
mTallestCell = aHeight;
|
|
}
|
|
}
|
|
}
|
|
else { // the alignment on the baseline can change the height
|
|
NS_ASSERTION((aAscent != NS_UNCONSTRAINEDSIZE) && (aDescent != NS_UNCONSTRAINEDSIZE), "invalid call");
|
|
// see if this is a long ascender
|
|
if (mMaxCellAscent < aAscent) {
|
|
mMaxCellAscent = aAscent;
|
|
}
|
|
// see if this is a long descender and without rowspan
|
|
if (mMaxCellDescent < aDescent) {
|
|
PRInt32 rowSpan = aTableFrame->GetEffectiveRowSpan(*aCellFrame);
|
|
if (rowSpan == 1) {
|
|
mMaxCellDescent = aDescent;
|
|
}
|
|
}
|
|
// keep the tallest height in sync
|
|
if (mTallestCell < mMaxCellAscent + mMaxCellDescent) {
|
|
mTallestCell = mMaxCellAscent + mMaxCellDescent;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
nsTableRowFrame::CalcTallestCell()
|
|
{
|
|
nsTableFrame* tableFrame = nsnull;
|
|
nsresult rv = nsTableFrame::GetTableFrame(this, tableFrame);
|
|
if (NS_FAILED(rv)) return;
|
|
|
|
nscoord cellSpacingX = tableFrame->GetCellSpacingX();
|
|
ResetTallestCell(0);
|
|
|
|
for (nsIFrame* kidFrame = mFrames.FirstChild(); kidFrame; kidFrame->GetNextSibling(&kidFrame)) {
|
|
nsCOMPtr<nsIAtom> frameType;
|
|
kidFrame->GetFrameType(getter_AddRefs(frameType));
|
|
if (nsLayoutAtoms::tableCellFrame == frameType.get()) {
|
|
nscoord availWidth = ((nsTableCellFrame *)kidFrame)->GetPriorAvailWidth();
|
|
nsSize desSize = ((nsTableCellFrame *)kidFrame)->GetDesiredSize();
|
|
CalculateCellActualSize(kidFrame, desSize.width, desSize.height, availWidth);
|
|
// height may have changed, adjust descent to absorb any excess difference
|
|
nscoord ascent = ((nsTableCellFrame *)kidFrame)->GetDesiredAscent();
|
|
nscoord descent = desSize.height - ascent;
|
|
SetTallestCell(desSize.height, ascent, descent, tableFrame, (nsTableCellFrame*)kidFrame);
|
|
}
|
|
}
|
|
}
|
|
|
|
static
|
|
PRBool IsFirstRow(nsIPresContext* aPresContext,
|
|
nsTableFrame& aTable,
|
|
nsTableRowFrame& aRow)
|
|
{
|
|
nsIFrame* firstRowGroup = nsnull;
|
|
aTable.FirstChild(aPresContext, nsnull, &firstRowGroup);
|
|
nsIFrame* rowGroupFrame = nsnull;
|
|
nsresult rv = aRow.GetParent(&rowGroupFrame);
|
|
if (NS_SUCCEEDED(rv) && (rowGroupFrame == firstRowGroup)) {
|
|
nsIFrame* firstRow;
|
|
rowGroupFrame->FirstChild(aPresContext, nsnull, &firstRow);
|
|
return (&aRow == firstRow);
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|
|
|
|
NS_METHOD nsTableRowFrame::Paint(nsIPresContext* aPresContext,
|
|
nsIRenderingContext& aRenderingContext,
|
|
const nsRect& aDirtyRect,
|
|
nsFramePaintLayer aWhichLayer)
|
|
{
|
|
PRBool isVisible;
|
|
if (NS_SUCCEEDED(IsVisibleForPainting(aPresContext, aRenderingContext, PR_FALSE, &isVisible)) && !isVisible) {
|
|
return NS_OK;
|
|
}
|
|
nsresult rv;
|
|
if (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer) {
|
|
nsCompatibility mode;
|
|
aPresContext->GetCompatibilityMode(&mode);
|
|
if (eCompatibility_Standard == mode) {
|
|
const nsStyleDisplay* disp =
|
|
(const nsStyleDisplay*)mStyleContext->GetStyleData(eStyleStruct_Display);
|
|
if (disp->IsVisibleOrCollapsed()) {
|
|
const nsStyleSpacing* spacing =
|
|
(const nsStyleSpacing*)mStyleContext->GetStyleData(eStyleStruct_Spacing);
|
|
const nsStyleColor* color =
|
|
(const nsStyleColor*)mStyleContext->GetStyleData(eStyleStruct_Color);
|
|
nsTableFrame* tableFrame = nsnull;
|
|
rv = nsTableFrame::GetTableFrame(this, tableFrame);
|
|
if (NS_FAILED(rv) || (nsnull == tableFrame)) {
|
|
return rv;
|
|
}
|
|
nscoord cellSpacingX = tableFrame->GetCellSpacingX();
|
|
// every row is short by the ending cell spacing X
|
|
nsRect rect(0, 0, mRect.width + cellSpacingX, mRect.height);
|
|
|
|
nsCSSRendering::PaintBackground(aPresContext, aRenderingContext, this,
|
|
aDirtyRect, rect, *color, *spacing, 0, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
// for debug...
|
|
if ((NS_FRAME_PAINT_LAYER_DEBUG == aWhichLayer) && GetShowFrameBorders()) {
|
|
aRenderingContext.SetColor(NS_RGB(0,255,0));
|
|
aRenderingContext.DrawRect(0, 0, mRect.width, mRect.height);
|
|
}
|
|
#endif
|
|
|
|
PaintChildren(aPresContext, aRenderingContext, aDirtyRect, aWhichLayer);
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
PRIntn
|
|
nsTableRowFrame::GetSkipSides() const
|
|
{
|
|
PRIntn skip = 0;
|
|
if (nsnull != mPrevInFlow) {
|
|
skip |= 1 << NS_SIDE_TOP;
|
|
}
|
|
if (nsnull != mNextInFlow) {
|
|
skip |= 1 << NS_SIDE_BOTTOM;
|
|
}
|
|
return skip;
|
|
}
|
|
|
|
/** 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,
|
|
nsFramePaintLayer aWhichLayer)
|
|
{
|
|
nsIFrame* kid = mFrames.FirstChild();
|
|
while (nsnull != kid) {
|
|
nsIView *pView;
|
|
|
|
kid->GetView(aPresContext, &pView);
|
|
if (nsnull == pView) {
|
|
nsRect kidRect;
|
|
kid->GetRect(kidRect);
|
|
nsRect damageArea;
|
|
PRBool overlap = damageArea.IntersectRect(aDirtyRect, kidRect);
|
|
if (overlap) {
|
|
PRBool clipState;
|
|
// 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,
|
|
aWhichLayer);
|
|
#ifdef DEBUG
|
|
if ((NS_FRAME_PAINT_LAYER_DEBUG == aWhichLayer) &&
|
|
GetShowFrameBorders()) {
|
|
aRenderingContext.SetColor(NS_RGB(255,0,0));
|
|
aRenderingContext.DrawRect(0, 0, kidRect.width, kidRect.height);
|
|
}
|
|
#endif
|
|
aRenderingContext.PopState(clipState);
|
|
}
|
|
}
|
|
kid->GetNextSibling(&kid);
|
|
}
|
|
}
|
|
|
|
/* we overload this here because rows have children that can span outside of themselves.
|
|
* so the default "get the child rect, see if it contains the event point" action isn't
|
|
* sufficient. We have to ask the row if it has a child that contains the point.
|
|
*/
|
|
NS_IMETHODIMP
|
|
nsTableRowFrame::GetFrameForPoint(nsIPresContext* aPresContext,
|
|
const nsPoint& aPoint,
|
|
nsFramePaintLayer aWhichLayer,
|
|
nsIFrame** aFrame)
|
|
{
|
|
// XXX This would not need to exist (except as a one-liner, to make this
|
|
// frame work like a block frame) if rows with rowspan cells made the
|
|
// the NS_FRAME_OUTSIDE_CHILDREN bit of mState set correctly (see
|
|
// nsIFrame.h).
|
|
|
|
// I imagine fixing this would help performance of GetFrameForPoint in
|
|
// tables. It may also fix problems with relative positioning.
|
|
|
|
// This is basically copied from nsContainerFrame::GetFrameForPointUsing,
|
|
// except for one bit removed
|
|
|
|
nsIFrame *kid, *hit;
|
|
nsPoint tmp;
|
|
|
|
PRBool inThisFrame = mRect.Contains(aPoint);
|
|
|
|
FirstChild(aPresContext, nsnull, &kid);
|
|
*aFrame = nsnull;
|
|
tmp.MoveTo(aPoint.x - mRect.x, aPoint.y - mRect.y);
|
|
while (nsnull != kid) {
|
|
nsresult rv = kid->GetFrameForPoint(aPresContext, tmp, aWhichLayer, &hit);
|
|
|
|
if (NS_SUCCEEDED(rv) && hit) {
|
|
*aFrame = hit;
|
|
}
|
|
kid->GetNextSibling(&kid);
|
|
}
|
|
|
|
if (*aFrame) {
|
|
return NS_OK;
|
|
}
|
|
|
|
if ( inThisFrame && (aWhichLayer == NS_FRAME_PAINT_LAYER_BACKGROUND)) {
|
|
const nsStyleDisplay* disp = (const nsStyleDisplay*)
|
|
mStyleContext->GetStyleData(eStyleStruct_Display);
|
|
if (disp->IsVisible()) {
|
|
*aFrame = this;
|
|
return NS_OK;
|
|
}
|
|
}
|
|
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
/* 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(nsTableFrame *aTableFrame)
|
|
{
|
|
PRInt32 minRowSpan=-1;
|
|
nsIFrame *frame=mFrames.FirstChild();
|
|
while (nsnull!=frame)
|
|
{
|
|
const nsStyleDisplay *kidDisplay;
|
|
frame->GetStyleData(eStyleStruct_Display, ((const nsStyleStruct *&)kidDisplay));
|
|
if (NS_STYLE_DISPLAY_TABLE_CELL == kidDisplay->mDisplay)
|
|
{
|
|
PRInt32 rowSpan = aTableFrame->GetEffectiveRowSpan((nsTableCellFrame &)*frame);
|
|
if (-1==minRowSpan)
|
|
minRowSpan = rowSpan;
|
|
else if (minRowSpan>rowSpan)
|
|
minRowSpan = rowSpan;
|
|
}
|
|
frame->GetNextSibling(&frame);
|
|
}
|
|
mBits.mMinRowSpan = unsigned(minRowSpan);
|
|
}
|
|
|
|
void nsTableRowFrame::FixMinCellHeight(nsTableFrame *aTableFrame)
|
|
{
|
|
nsIFrame *frame=mFrames.FirstChild();
|
|
while (nsnull!=frame)
|
|
{
|
|
const nsStyleDisplay *kidDisplay;
|
|
frame->GetStyleData(eStyleStruct_Display, ((const nsStyleStruct *&)kidDisplay));
|
|
if (NS_STYLE_DISPLAY_TABLE_CELL == kidDisplay->mDisplay)
|
|
{
|
|
PRInt32 rowSpan = aTableFrame->GetEffectiveRowSpan((nsTableCellFrame &)*frame);
|
|
if (PRInt32(mBits.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& aReflowState,
|
|
nsIFrame* aKidFrame,
|
|
nsHTMLReflowMetrics& aDesiredSize,
|
|
nscoord aX,
|
|
nscoord aY,
|
|
nsSize* aMaxElementSize,
|
|
nsSize* aKidMaxElementSize)
|
|
{
|
|
// Complete the reflow
|
|
FinishReflowChild(aKidFrame, aPresContext, aDesiredSize, aX, aY, 0);
|
|
|
|
// update the running total for the row width
|
|
aReflowState.x += aDesiredSize.width;
|
|
|
|
// Update the maximum element size
|
|
PRInt32 rowSpan = aReflowState.tableFrame->GetEffectiveRowSpan((nsTableCellFrame&)*aKidFrame);
|
|
if (nsnull != aMaxElementSize) {
|
|
aMaxElementSize->width += aKidMaxElementSize->width;
|
|
if (1 == rowSpan) {
|
|
aMaxElementSize->height = PR_MAX(aMaxElementSize->height, aKidMaxElementSize->height);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Calculate the cell's actual size given its pass2 desired width and height.
|
|
// Takes into account the specified height (in the style), and any special logic
|
|
// needed for backwards compatibility.
|
|
// Modifies the desired width and height that are passed in.
|
|
nsresult
|
|
nsTableRowFrame::CalculateCellActualSize(nsIFrame* aCellFrame,
|
|
nscoord& aDesiredWidth,
|
|
nscoord& aDesiredHeight,
|
|
nscoord aAvailWidth)
|
|
{
|
|
nscoord specifiedHeight = 0;
|
|
const nsStylePosition* position;
|
|
|
|
// Get the height specified in the style information
|
|
aCellFrame->GetStyleData(eStyleStruct_Position, (const nsStyleStruct*&)position);
|
|
|
|
switch (position->mHeight.GetUnit()) {
|
|
case eStyleUnit_Coord:
|
|
specifiedHeight = position->mHeight.GetCoordValue();
|
|
break;
|
|
case eStyleUnit_Percent:
|
|
{
|
|
nsTableFrame* table = nsnull;
|
|
nsTableFrame::GetTableFrame(this, table);
|
|
if (table) {
|
|
nscoord basis = table->GetPercentBasisForRows();
|
|
if (basis > 0) {
|
|
float percent = position->mHeight.GetPercentValue();
|
|
specifiedHeight = NSToCoordRound(percent * ((float)basis));
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case eStyleUnit_Inherit:
|
|
// XXX for now, do nothing
|
|
default:
|
|
case eStyleUnit_Auto:
|
|
break;
|
|
}
|
|
|
|
// If the specified height is greater than the desired height, then use the
|
|
// specified height
|
|
if (specifiedHeight > aDesiredHeight)
|
|
aDesiredHeight = specifiedHeight;
|
|
|
|
if (0 == aDesiredWidth) { // special Nav4 compatibility code for the width
|
|
aDesiredWidth = aAvailWidth;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
// Calculates the available width for the table cell based on the known
|
|
// column widths taking into account column spans and column spacing
|
|
nscoord
|
|
nsTableRowFrame::CalculateCellAvailableWidth(nsTableFrame* aTableFrame,
|
|
nsIFrame* aCellFrame,
|
|
PRInt32 aCellColIndex,
|
|
PRInt32 aNumColSpans,
|
|
nscoord aCellSpacingX)
|
|
{
|
|
nscoord availWidth = 0;
|
|
for (PRInt32 index = 0; index < aNumColSpans; index++) {
|
|
// Add in the width of this column
|
|
availWidth += aTableFrame->GetColumnWidth(aCellColIndex + index);
|
|
|
|
// If the cell spans columns, then for all columns except the first column
|
|
// add in the column spacing
|
|
if ((index != 0) && (aTableFrame->GetNumCellsOriginatingInCol(aCellColIndex + index) > 0)) {
|
|
availWidth += aCellSpacingX;
|
|
}
|
|
}
|
|
|
|
return availWidth;
|
|
}
|
|
|
|
/**
|
|
* Called for a resize reflow. Typically because the column widths have
|
|
* changed. Reflows all the existing table cell frames unless aDirtyOnly
|
|
* is PR_TRUE in which case only reflow the dirty frames
|
|
*/
|
|
NS_METHOD nsTableRowFrame::ResizeReflow(nsIPresContext* aPresContext,
|
|
nsHTMLReflowMetrics& aDesiredSize,
|
|
RowReflowState& aReflowState,
|
|
nsReflowStatus& aStatus,
|
|
PRBool aDirtyOnly)
|
|
{
|
|
aStatus = NS_FRAME_COMPLETE;
|
|
if (nsnull == mFrames.FirstChild()) {
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult rv = NS_OK;
|
|
ResetTallestCell(aReflowState.reflowState.mComputedHeight);
|
|
|
|
nsSize localKidMaxElementSize(0,0);
|
|
nsSize* kidMaxElementSize = (aDesiredSize.maxElementSize) ? &localKidMaxElementSize : nsnull;
|
|
|
|
nscoord cellSpacingX = aReflowState.tableFrame->GetCellSpacingX();
|
|
PRInt32 cellColSpan=1; // must be defined here so it's set properly for non-cell kids
|
|
|
|
PRInt32 prevColIndex; // remember the col index of the previous cell to handle rowspans into this row
|
|
|
|
nsTableFrame* tableFrame = nsnull;
|
|
rv = nsTableFrame::GetTableFrame(this, tableFrame);
|
|
nsTableIteration dir = (aReflowState.reflowState.availableWidth == NS_UNCONSTRAINEDSIZE)
|
|
? eTableLTR : eTableDIR;
|
|
nsTableIterator iter(aPresContext, *this, dir);
|
|
if (iter.IsLeftToRight()) {
|
|
prevColIndex = -1;
|
|
}
|
|
else {
|
|
if (NS_FAILED(rv) || (nsnull == tableFrame)) {
|
|
return rv;
|
|
}
|
|
prevColIndex = tableFrame->GetColCount();
|
|
}
|
|
|
|
PRBool isAutoLayout = tableFrame->IsAutoLayout();
|
|
// Reflow each of our existing cell frames
|
|
nsIFrame* kidFrame = iter.First();
|
|
while (nsnull != kidFrame) {
|
|
// Get the frame state bits
|
|
nsFrameState frameState;
|
|
kidFrame->GetFrameState(&frameState);
|
|
|
|
// See if we should only reflow the dirty child frames
|
|
PRBool doReflowChild = PR_TRUE;
|
|
if (aDirtyOnly) {
|
|
if ((frameState & NS_FRAME_IS_DIRTY) == 0) {
|
|
doReflowChild = PR_FALSE;
|
|
}
|
|
}
|
|
|
|
// Reflow the child frame
|
|
const nsStyleDisplay *kidDisplay;
|
|
kidFrame->GetStyleData(eStyleStruct_Display, ((const nsStyleStruct *&)kidDisplay));
|
|
if (doReflowChild) {
|
|
if (NS_STYLE_DISPLAY_TABLE_CELL == kidDisplay->mDisplay) {
|
|
PRInt32 cellColIndex;
|
|
((nsTableCellFrame *)kidFrame)->GetColIndex(cellColIndex);
|
|
cellColSpan = aReflowState.tableFrame->GetEffectiveColSpan((nsTableCellFrame &)*kidFrame);
|
|
|
|
// Compute the x-origin for the child, taking into account straddlers (cells from prior
|
|
// rows with rowspans > 1)
|
|
// if this cell is not immediately adjacent to the previous cell, factor in missing col info
|
|
if (iter.IsLeftToRight()) {
|
|
if (prevColIndex != (cellColIndex - 1)) {
|
|
for (PRInt32 colIndex = prevColIndex + 1; cellColIndex > colIndex; colIndex++) {
|
|
aReflowState.x += aReflowState.tableFrame->GetColumnWidth(colIndex);
|
|
if (aReflowState.tableFrame->GetNumCellsOriginatingInCol(colIndex) > 0) {
|
|
aReflowState.x += cellSpacingX;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (prevColIndex != cellColIndex + cellColSpan) {
|
|
PRInt32 lastCol = cellColIndex + cellColSpan - 1;
|
|
for (PRInt32 colIndex = prevColIndex - 1; colIndex > lastCol; colIndex--) {
|
|
aReflowState.x += aReflowState.tableFrame->GetColumnWidth(colIndex);
|
|
if (aReflowState.tableFrame->GetNumCellsOriginatingInCol(colIndex) > 0) {
|
|
aReflowState.x += cellSpacingX;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
aReflowState.x += cellSpacingX;
|
|
|
|
// Calculate the available width for the table cell using the known
|
|
// column widths
|
|
nscoord availWidth;
|
|
if (!mPrevInFlow && isAutoLayout && (frameState & NS_FRAME_FIRST_REFLOW)) {
|
|
// This is the initial reflow for the cell and so we do an unconstrained
|
|
// reflow.
|
|
// Note: don't assume that we have known column widths. If we don't, then
|
|
// CalculateCellAvailableWidth() may assert if called now...
|
|
availWidth = NS_UNCONSTRAINEDSIZE;
|
|
} else {
|
|
availWidth = CalculateCellAvailableWidth(aReflowState.tableFrame,
|
|
kidFrame, cellColIndex,
|
|
cellColSpan, cellSpacingX);
|
|
}
|
|
|
|
// remember the rightmost (ltr) or leftmost (rtl) column this cell spans into
|
|
prevColIndex = (iter.IsLeftToRight()) ? cellColIndex + (cellColSpan - 1) : cellColIndex;
|
|
nsHTMLReflowMetrics desiredSize(kidMaxElementSize);
|
|
|
|
// If the available width is the same as last time we reflowed the cell,
|
|
// then just use the previous desired size and max element size.
|
|
// if we need the max-element-size we don't need to reflow.
|
|
// we just grab it from the cell frame which remembers it (see the else clause below).
|
|
// Note: we can't do that optimization if our height is constrained or the
|
|
// cell frame has a next-in-flow
|
|
nsIFrame* kidNextInFlow;
|
|
kidFrame->GetNextInFlow(&kidNextInFlow);
|
|
if ((aReflowState.reflowState.availableHeight != NS_UNCONSTRAINEDSIZE) ||
|
|
(availWidth != ((nsTableCellFrame *)kidFrame)->GetPriorAvailWidth()) ||
|
|
(nsnull != kidNextInFlow))
|
|
{
|
|
// Reflow the cell to fit the available height
|
|
nsSize kidAvailSize(availWidth, aReflowState.reflowState.availableHeight);
|
|
|
|
// If it's a dirty frame, then check whether it's the initial reflow
|
|
nsReflowReason reason =
|
|
(frameState & NS_FRAME_FIRST_REFLOW) ? eReflowReason_Initial :eReflowReason_Resize;
|
|
if (!mPrevInFlow && isAutoLayout && (frameState & NS_FRAME_FIRST_REFLOW)) {
|
|
// Use an unconstrained width so we can get the child's maximum width
|
|
// XXX What about fixed layout tables?
|
|
kidAvailSize.SizeTo(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
|
|
// request to get the max element size if not already so
|
|
if (!kidMaxElementSize) {
|
|
kidMaxElementSize = &localKidMaxElementSize;
|
|
desiredSize.maxElementSize = kidMaxElementSize;
|
|
}
|
|
}
|
|
|
|
// Reflow the child
|
|
nsTableCellReflowState kidReflowState(aPresContext, aReflowState.reflowState, kidFrame,
|
|
kidAvailSize, reason);
|
|
|
|
nsReflowStatus status;
|
|
rv = ReflowChild(kidFrame, aPresContext, desiredSize, kidReflowState,
|
|
aReflowState.x, 0, 0, status);
|
|
#ifdef NS_DEBUG_karnaze
|
|
if (desiredSize.width > availWidth)
|
|
{
|
|
printf("WARNING: cell returned desired width %d given avail width %d\n",
|
|
desiredSize.width, availWidth);
|
|
}
|
|
#endif
|
|
|
|
if (eReflowReason_Initial == reason) {
|
|
nscoord oldMaxWidth = ((nsTableCellFrame *)kidFrame)->GetMaximumWidth();
|
|
// Save the pass1 reflow information
|
|
((nsTableCellFrame *)kidFrame)->SetMaximumWidth(desiredSize.width);
|
|
// invalidate the table's max width if the cell's max width changes
|
|
if (oldMaxWidth != desiredSize.mMaximumWidth) {
|
|
aReflowState.tableFrame->InvalidateMaximumWidth();
|
|
}
|
|
if (kidMaxElementSize) {
|
|
((nsTableCellFrame *)kidFrame)->SetPass1MaxElementSize(desiredSize.width, *kidMaxElementSize);
|
|
}
|
|
// XXX if we did an unconstrained reflow, do we need to do another one
|
|
// there needs to be more test cases to show this
|
|
}
|
|
|
|
// If any of the cells are not complete, then we're not complete
|
|
if (NS_FRAME_IS_NOT_COMPLETE(status)) {
|
|
aStatus = NS_FRAME_NOT_COMPLETE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
nsSize priorSize = ((nsTableCellFrame *)kidFrame)->GetDesiredSize();
|
|
desiredSize.width = priorSize.width;
|
|
desiredSize.height = priorSize.height;
|
|
if (kidMaxElementSize)
|
|
*kidMaxElementSize = ((nsTableCellFrame *)kidFrame)->GetPass1MaxElementSize();
|
|
|
|
// Because we may have moved the frame we need to make sure any views are
|
|
// positioned properly. We have to do this, because any one of our parent
|
|
// frames could have moved and we have no way of knowing...
|
|
nsIView* view;
|
|
kidFrame->GetView(aPresContext, &view);
|
|
if (view) {
|
|
nsContainerFrame::PositionFrameView(aPresContext, kidFrame, view);
|
|
} else {
|
|
nsContainerFrame::PositionChildViews(aPresContext, kidFrame);
|
|
}
|
|
}
|
|
|
|
// Calculate the cell's actual size given its pass2 size. This function
|
|
// takes into account the specified height (in the style), and any special
|
|
// logic needed for backwards compatibility
|
|
CalculateCellActualSize(kidFrame, desiredSize.width,
|
|
desiredSize.height, availWidth);
|
|
|
|
// height may have changed, adjust descent to absorb any excess difference
|
|
nscoord ascent = ((nsTableCellFrame *)kidFrame)->GetDesiredAscent();
|
|
nscoord descent = desiredSize.height - ascent;
|
|
SetTallestCell(desiredSize.height, ascent, descent, aReflowState.tableFrame, (nsTableCellFrame*)kidFrame);
|
|
|
|
// Place the child
|
|
PlaceChild(aPresContext, aReflowState, kidFrame, desiredSize,
|
|
aReflowState.x, 0,
|
|
aDesiredSize.maxElementSize, kidMaxElementSize);
|
|
|
|
}
|
|
else
|
|
{// it's an unknown frame type, give it a generic reflow and ignore the results
|
|
nsTableCellReflowState kidReflowState(aPresContext, aReflowState.reflowState,
|
|
kidFrame, nsSize(0,0), eReflowReason_Resize);
|
|
nsHTMLReflowMetrics desiredSize(nsnull);
|
|
nsReflowStatus status;
|
|
ReflowChild(kidFrame, aPresContext, desiredSize, kidReflowState, 0, 0, 0, status);
|
|
kidFrame->DidReflow(aPresContext, NS_FRAME_REFLOW_FINISHED);
|
|
}
|
|
}
|
|
else if (NS_STYLE_DISPLAY_TABLE_CELL == kidDisplay->mDisplay) {
|
|
// we need to account for the cell's width even if it isn't reflowed
|
|
nsRect rect;
|
|
kidFrame->GetRect(rect);
|
|
aReflowState.x += rect.width;
|
|
}
|
|
|
|
kidFrame = iter.Next(); // Get the next child
|
|
// if this was the last child, and it had a colspan>1, add in the cellSpacing for the colspan
|
|
// if the last kid wasn't a colspan, then we still have the colspan of the last real cell
|
|
if ((nsnull==kidFrame) && (cellColSpan>1))
|
|
aReflowState.x += cellSpacingX;
|
|
}
|
|
|
|
// Return our desired size. Note that our desired width is just whatever width
|
|
// we were given by the row group frame
|
|
aDesiredSize.width = aReflowState.x;
|
|
aDesiredSize.height = GetTallestCell();
|
|
#ifdef DEBUG_karnaze
|
|
nscoord overAllocated = aDesiredSize.width - aReflowState.reflowState.availableWidth;
|
|
if (overAllocated > 0) {
|
|
float p2t;
|
|
aPresContext->GetScaledPixelsToTwips(&p2t);
|
|
if (overAllocated > p2t) {
|
|
printf("row over allocated by %d\n twips", overAllocated);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return rv;
|
|
}
|
|
|
|
/**
|
|
* Called for the initial reflow. Creates each table cell frame, and
|
|
* reflows it to gets its minimum and maximum sizes
|
|
*/
|
|
NS_METHOD
|
|
nsTableRowFrame::InitialReflow(nsIPresContext* aPresContext,
|
|
nsHTMLReflowMetrics& aDesiredSize,
|
|
RowReflowState& aReflowState,
|
|
nsReflowStatus& aStatus,
|
|
nsTableCellFrame * aStartFrame,
|
|
PRBool aDoSiblings)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
ResetTallestCell(aReflowState.reflowState.mComputedHeight);
|
|
|
|
// Place our children, one at a time, until we are out of children
|
|
nsSize kidMaxElementSize(0,0);
|
|
nscoord x = 0;
|
|
nsTableFrame* table = aReflowState.tableFrame;
|
|
PRBool isAutoLayout = table->IsAutoLayout();
|
|
nscoord cellSpacingX = table->GetCellSpacingX();
|
|
|
|
nsIFrame* kidFrame;
|
|
if (nsnull==aStartFrame)
|
|
kidFrame = mFrames.FirstChild();
|
|
else
|
|
kidFrame = aStartFrame;
|
|
|
|
for ( ; nsnull != kidFrame; kidFrame->GetNextSibling(&kidFrame)) {
|
|
const nsStyleDisplay *kidDisplay;
|
|
kidFrame->GetStyleData(eStyleStruct_Display, ((const nsStyleStruct *&)kidDisplay));
|
|
if (NS_STYLE_DISPLAY_TABLE_CELL == kidDisplay->mDisplay) {
|
|
// For the initial reflow always allow the child to be as high as it
|
|
// wants. The default available width is also unconstrained so we can
|
|
// get the child's maximum width
|
|
nsSize kidAvailSize;
|
|
nsHTMLReflowMetrics kidSize(nsnull);
|
|
if (isAutoLayout) {
|
|
kidAvailSize.SizeTo(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
|
|
kidSize.maxElementSize=&kidMaxElementSize;
|
|
}
|
|
else {
|
|
PRInt32 colIndex;
|
|
((nsTableCellFrame *)kidFrame)->GetColIndex(colIndex);
|
|
kidAvailSize.SizeTo(table->GetColumnWidth(colIndex), NS_UNCONSTRAINEDSIZE);
|
|
}
|
|
|
|
if (NS_UNCONSTRAINEDSIZE == kidAvailSize.width) {
|
|
((nsTableCellFrame*)kidFrame)->DidSetStyleContext(aPresContext);
|
|
}
|
|
|
|
nsTableCellReflowState kidReflowState(aPresContext, aReflowState.reflowState,
|
|
kidFrame, kidAvailSize, eReflowReason_Initial);
|
|
|
|
rv = ReflowChild(kidFrame, aPresContext, kidSize, kidReflowState,
|
|
x + cellSpacingX, 0, 0, aStatus);
|
|
|
|
// the following signals bugs in the content frames.
|
|
if (kidMaxElementSize.width > kidSize.width) {
|
|
#ifdef DEBUG_karnaze
|
|
printf("WARNING - table cell content max element width %d greater than desired width %d\n",
|
|
kidMaxElementSize.width, kidSize.width);
|
|
#endif
|
|
kidSize.width = kidMaxElementSize.width;
|
|
}
|
|
if (kidMaxElementSize.height > kidSize.height) {
|
|
#ifdef DEBUG_karnaze
|
|
printf("Warning - table cell content max element height %d greater than desired height %d\n",
|
|
kidMaxElementSize.height, kidSize.height);
|
|
#endif
|
|
kidSize.height = kidMaxElementSize.height;
|
|
}
|
|
|
|
((nsTableCellFrame *)kidFrame)->SetMaximumWidth(kidSize.width);
|
|
((nsTableCellFrame *)kidFrame)->SetPass1MaxElementSize(kidSize.width, kidMaxElementSize);
|
|
NS_ASSERTION(NS_FRAME_IS_COMPLETE(aStatus), "unexpected child reflow status");
|
|
|
|
// Place the child
|
|
x += cellSpacingX;
|
|
// XXX do we need to call CalculateCellActualSize?
|
|
PlaceChild(aPresContext, aReflowState, kidFrame, kidSize, x, 0,
|
|
aDesiredSize.maxElementSize, &kidMaxElementSize);
|
|
SetTallestCell(kidSize.height, kidSize.ascent, kidSize.descent, aReflowState.tableFrame, (nsTableCellFrame*)kidFrame);
|
|
x += kidSize.width + cellSpacingX;
|
|
}
|
|
else
|
|
{// it's an unknown frame type, give it a generic reflow and ignore the results
|
|
nsTableCellReflowState kidReflowState(aPresContext, aReflowState.reflowState,
|
|
kidFrame, nsSize(0,0), eReflowReason_Initial);
|
|
nsHTMLReflowMetrics desiredSize(nsnull);
|
|
ReflowChild(kidFrame, aPresContext, desiredSize, kidReflowState, 0, 0, 0, aStatus);
|
|
kidFrame->DidReflow(aPresContext, NS_FRAME_REFLOW_FINISHED);
|
|
}
|
|
if (PR_FALSE==aDoSiblings)
|
|
break;
|
|
}
|
|
|
|
// Return our desired size
|
|
aDesiredSize.width = x;
|
|
aDesiredSize.height = GetTallestCell();
|
|
|
|
return rv;
|
|
}
|
|
|
|
// Recover the reflow state to what it should be if aKidFrame is about
|
|
// to be reflowed
|
|
//
|
|
// The things in the RowReflowState object we need to restore are:
|
|
// - max-element size
|
|
// - x
|
|
NS_METHOD nsTableRowFrame::RecoverState(nsIPresContext* aPresContext,
|
|
RowReflowState& aReflowState,
|
|
nsIFrame* aKidFrame,
|
|
nsSize* aMaxElementSize)
|
|
{
|
|
// Initialize OUT parameters
|
|
if (aMaxElementSize) {
|
|
aMaxElementSize->width = 0;
|
|
aMaxElementSize->height = 0;
|
|
}
|
|
|
|
// Walk the child frames (except aKidFrame) and find the table cell frames
|
|
for (nsIFrame* frame = mFrames.FirstChild(); frame; frame->GetNextSibling(&frame)) {
|
|
if (frame != aKidFrame) {
|
|
// See if the frame is a table cell frame
|
|
const nsStyleDisplay *kidDisplay;
|
|
frame->GetStyleData(eStyleStruct_Display, ((const nsStyleStruct *&)kidDisplay));
|
|
|
|
if (NS_STYLE_DISPLAY_TABLE_CELL == kidDisplay->mDisplay) {
|
|
// Recover the max element size if requested,
|
|
// ignoring the height of cells that span rows
|
|
if (aMaxElementSize) {
|
|
nsSize kidMaxElementSize = ((nsTableCellFrame *)frame)->GetPass1MaxElementSize();
|
|
aMaxElementSize->width += kidMaxElementSize.width;
|
|
|
|
PRInt32 rowSpan = aReflowState.tableFrame->GetEffectiveRowSpan((nsTableCellFrame &)*frame);
|
|
if (1 == rowSpan) {
|
|
aMaxElementSize->height = PR_MAX(aMaxElementSize->height, kidMaxElementSize.height);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update the running x-offset based on the frame's current x-origin
|
|
nsPoint origin;
|
|
aKidFrame->GetOrigin(origin);
|
|
aReflowState.x = origin.x;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_METHOD nsTableRowFrame::IncrementalReflow(nsIPresContext* aPresContext,
|
|
nsHTMLReflowMetrics& aDesiredSize,
|
|
RowReflowState& aReflowState,
|
|
nsReflowStatus& aStatus)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
CalcTallestCell(); // need to recalculate it based on last reflow sizes
|
|
|
|
// determine if this frame is the target or not
|
|
nsIFrame *target=nsnull;
|
|
rv = aReflowState.reflowState.reflowCommand->GetTarget(target);
|
|
if ((PR_TRUE==NS_SUCCEEDED(rv)) && (nsnull!=target))
|
|
{
|
|
if (this==target)
|
|
rv = IR_TargetIsMe(aPresContext, aDesiredSize, aReflowState, aStatus);
|
|
else
|
|
{
|
|
// Get the next frame in the reflow chain
|
|
nsIFrame* nextFrame;
|
|
aReflowState.reflowState.reflowCommand->GetNext(nextFrame);
|
|
rv = IR_TargetIsChild(aPresContext, aDesiredSize, aReflowState, aStatus, nextFrame);
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
NS_METHOD nsTableRowFrame::IR_TargetIsMe(nsIPresContext* aPresContext,
|
|
nsHTMLReflowMetrics& aDesiredSize,
|
|
RowReflowState& aReflowState,
|
|
nsReflowStatus& aStatus)
|
|
{
|
|
nsresult rv = NS_FRAME_COMPLETE;
|
|
nsIReflowCommand::ReflowType type;
|
|
aReflowState.reflowState.reflowCommand->GetType(type);
|
|
nsIFrame *objectFrame;
|
|
aReflowState.reflowState.reflowCommand->GetChildFrame(objectFrame);
|
|
const nsStyleDisplay *childDisplay=nsnull;
|
|
if (nsnull!=objectFrame)
|
|
objectFrame->GetStyleData(eStyleStruct_Display, ((const nsStyleStruct *&)childDisplay));
|
|
switch (type)
|
|
{
|
|
case nsIReflowCommand::ReflowDirty:
|
|
{
|
|
// Reflow the dirty child frames. Typically this is newly added frames.
|
|
// XXX What we really should do is do a pass-1 reflow of newly added
|
|
// frames (only if necessary, i.e., the table isn't fixed layout), then
|
|
// see if column widtsh changed and decide whether to do the pass-2 reflow
|
|
// of just the dirty rows or have the table rebalance column widths and
|
|
// do a pass-2 reflow of all rows
|
|
rv = ResizeReflow(aPresContext, aDesiredSize, aReflowState, aStatus, PR_TRUE);
|
|
|
|
// If any column widths have to change due to this, rebalance column widths.
|
|
// XXX need to calculate this, but for now just do it
|
|
aReflowState.tableFrame->InvalidateColumnWidths();
|
|
break;
|
|
}
|
|
|
|
case nsIReflowCommand::StyleChanged :
|
|
rv = IR_StyleChanged(aPresContext, aDesiredSize, aReflowState, aStatus);
|
|
break;
|
|
|
|
case nsIReflowCommand::ContentChanged :
|
|
NS_ASSERTION(PR_FALSE, "illegal reflow type: ContentChanged");
|
|
rv = NS_ERROR_ILLEGAL_VALUE;
|
|
break;
|
|
|
|
default:
|
|
NS_NOTYETIMPLEMENTED("unexpected reflow command type");
|
|
rv = NS_ERROR_NOT_IMPLEMENTED;
|
|
break;
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
NS_METHOD nsTableRowFrame::IR_StyleChanged(nsIPresContext* aPresContext,
|
|
nsHTMLReflowMetrics& aDesiredSize,
|
|
RowReflowState& aReflowState,
|
|
nsReflowStatus& aStatus)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
// we presume that all the easy optimizations were done in the nsHTMLStyleSheet before we were called here
|
|
// XXX: we can optimize this when we know which style attribute changed
|
|
aReflowState.tableFrame->InvalidateFirstPassCache();
|
|
return rv;
|
|
}
|
|
|
|
NS_METHOD nsTableRowFrame::IR_TargetIsChild(nsIPresContext* aPresContext,
|
|
nsHTMLReflowMetrics& aDesiredSize,
|
|
RowReflowState& aReflowState,
|
|
nsReflowStatus& aStatus,
|
|
nsIFrame * aNextFrame)
|
|
|
|
{
|
|
nsresult rv;
|
|
|
|
const nsStyleDisplay *childDisplay;
|
|
aNextFrame->GetStyleData(eStyleStruct_Display, ((const nsStyleStruct *&)childDisplay));
|
|
if (NS_STYLE_DISPLAY_TABLE_CELL == childDisplay->mDisplay)
|
|
{
|
|
// Recover our reflow state
|
|
RecoverState(aPresContext, aReflowState, aNextFrame, aDesiredSize.maxElementSize);
|
|
|
|
// At this point, we know the column widths. Compute the cell available width
|
|
PRInt32 cellColIndex;
|
|
((nsTableCellFrame *)aNextFrame)->GetColIndex(cellColIndex);
|
|
PRInt32 cellColSpan = aReflowState.tableFrame->GetEffectiveColSpan((nsTableCellFrame &)*aNextFrame);
|
|
nscoord cellSpacingX = aReflowState.tableFrame->GetCellSpacingX();
|
|
|
|
nscoord cellAvailWidth = CalculateCellAvailableWidth(aReflowState.tableFrame, aNextFrame,
|
|
cellColIndex, cellColSpan, cellSpacingX);
|
|
|
|
// Always let the cell be as high as it wants. We ignore the height that's
|
|
// passed in and always place the entire row. Let the row group decide
|
|
// whether we fit or wehther the entire row is pushed
|
|
nsSize kidAvailSize(cellAvailWidth, NS_UNCONSTRAINEDSIZE);
|
|
|
|
// Pass along the reflow command
|
|
nsSize kidMaxElementSize;
|
|
// Unless this is a fixed-layout table, then have the cell incrementally
|
|
// update its maximum width. XXX should not skip if the cell is in the 1st row
|
|
nsHTMLReflowMetrics cellMet(&kidMaxElementSize, aReflowState.tableFrame->IsAutoLayout() ?
|
|
NS_REFLOW_CALC_MAX_WIDTH : 0);
|
|
nsTableCellReflowState kidRS(aPresContext, aReflowState.reflowState,
|
|
aNextFrame, kidAvailSize);
|
|
|
|
// Remember the current desired size, we'll need it later
|
|
nsSize oldCellMinSize = ((nsTableCellFrame*)aNextFrame)->GetPass1MaxElementSize();
|
|
nscoord oldCellMaximumWidth = ((nsTableCellFrame*)aNextFrame)->GetMaximumWidth();
|
|
nsSize oldCellDesSize = ((nsTableCellFrame*)aNextFrame)->GetDesiredSize();
|
|
nscoord oldCellDesAscent = ((nsTableCellFrame*)aNextFrame)->GetDesiredAscent();
|
|
nscoord oldCellDesDescent = oldCellDesSize.height - oldCellDesAscent;
|
|
|
|
// Reflow the cell passing it 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...
|
|
rv = ReflowChild(aNextFrame, aPresContext, cellMet, kidRS,
|
|
aReflowState.x, 0, 0, aStatus);
|
|
nsSize initCellDesSize(cellMet.width, cellMet.height);
|
|
nscoord initCellDesAscent = cellMet.ascent;
|
|
nscoord initCellDesDescent = cellMet.descent;
|
|
|
|
// Update the cell layout data.. If the cell's maximum width changed,
|
|
// then inform the table that its maximum width needs to be recomputed
|
|
((nsTableCellFrame *)aNextFrame)->SetPass1MaxElementSize(cellMet.width, kidMaxElementSize);
|
|
if (cellMet.mFlags & NS_REFLOW_CALC_MAX_WIDTH) {
|
|
((nsTableCellFrame *)aNextFrame)->SetMaximumWidth(cellMet.mMaximumWidth);
|
|
if (oldCellMaximumWidth != cellMet.mMaximumWidth) {
|
|
aReflowState.tableFrame->InvalidateMaximumWidth();
|
|
}
|
|
}
|
|
|
|
// Calculate the cell's actual size given its pass2 size. This function
|
|
// takes into account the specified height (in the style), and any special
|
|
// logic needed for backwards compatibility
|
|
CalculateCellActualSize(aNextFrame, cellMet.width, cellMet.height, cellAvailWidth);
|
|
|
|
// height may have changed, adjust descent to absorb any excess difference
|
|
cellMet.descent = cellMet.height - cellMet.ascent;
|
|
|
|
// if the cell got shorter and it may have been the tallest, recalc the tallest cell
|
|
PRBool tallestCellGotShorter = PR_FALSE;
|
|
PRBool hasVerticalAlignBaseline = ((nsTableCellFrame*)aNextFrame)->HasVerticalAlignBaseline();
|
|
if (!hasVerticalAlignBaseline) {
|
|
// only the height matters
|
|
tallestCellGotShorter =
|
|
TallestCellGotShorter(oldCellDesSize.height, cellMet.height, mTallestCell);
|
|
}
|
|
else {
|
|
// the ascent matters
|
|
tallestCellGotShorter =
|
|
TallestCellGotShorter(oldCellDesAscent, cellMet.ascent, mMaxCellAscent);
|
|
// the descent of cells without rowspan also matters
|
|
if (!tallestCellGotShorter) {
|
|
PRInt32 rowSpan = aReflowState.tableFrame->GetEffectiveRowSpan((nsTableCellFrame&)*aNextFrame);
|
|
if (rowSpan == 1) {
|
|
tallestCellGotShorter =
|
|
TallestCellGotShorter(oldCellDesAscent, cellMet.descent, mMaxCellDescent);
|
|
}
|
|
}
|
|
}
|
|
if (tallestCellGotShorter) {
|
|
CalcTallestCell();
|
|
}
|
|
else {
|
|
SetTallestCell(cellMet.height, cellMet.ascent, cellMet.descent, aReflowState.tableFrame, (nsTableCellFrame*)aNextFrame);
|
|
}
|
|
|
|
// if the cell's desired size didn't changed, our height is unchanged
|
|
aDesiredSize.mNothingChanged = PR_FALSE;
|
|
PRInt32 rowSpan = aReflowState.tableFrame->GetEffectiveRowSpan((nsTableCellFrame&)*aNextFrame);
|
|
if ((initCellDesSize.width == oldCellDesSize.width) &&
|
|
(initCellDesSize.height == oldCellDesSize.height)) {
|
|
if (!hasVerticalAlignBaseline) { // only the cell's height matters
|
|
aDesiredSize.mNothingChanged = PR_TRUE;
|
|
}
|
|
else { // cell's ascent and cell's descent matter
|
|
if (initCellDesAscent == oldCellDesAscent) {
|
|
if ((rowSpan == 1) && (initCellDesDescent == oldCellDesDescent)) {
|
|
aDesiredSize.mNothingChanged = PR_TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
aDesiredSize.height = (aDesiredSize.mNothingChanged) ? mRect.height : GetTallestCell();
|
|
if (1 == rowSpan) {
|
|
cellMet.height = aDesiredSize.height;
|
|
}
|
|
else {
|
|
nscoord heightOfRows = aDesiredSize.height + GetHeightOfRowsSpannedBelowFirst((nsTableCellFrame&)*aNextFrame, *aReflowState.tableFrame);
|
|
cellMet.height = PR_MAX(cellMet.height, heightOfRows);
|
|
// XXX need to check what happens when this height differs from height of the cell's previous mRect.height
|
|
}
|
|
|
|
// Now place the child
|
|
PlaceChild(aPresContext, aReflowState, aNextFrame, cellMet, aReflowState.x,
|
|
0, aDesiredSize.maxElementSize, &kidMaxElementSize);
|
|
|
|
|
|
// Now that we know the minimum and preferred widths see if the column
|
|
// widths need to be rebalanced
|
|
if (!aDesiredSize.mNothingChanged &&
|
|
!aReflowState.tableFrame->ColumnsAreValidFor(*(nsTableCellFrame*)aNextFrame,
|
|
oldCellMinSize.width,
|
|
oldCellMaximumWidth)) {
|
|
// The column widths need to be rebalanced. Tell the table to rebalance
|
|
// the column widths
|
|
aReflowState.tableFrame->InvalidateColumnWidths();
|
|
}
|
|
|
|
// Return our desired size. Note that our desired width is just whatever width
|
|
// we were given by the row group frame
|
|
aDesiredSize.width = aReflowState.availSize.width;
|
|
if (!aDesiredSize.mNothingChanged) {
|
|
if (aDesiredSize.height == mRect.height) { // our height didn't change
|
|
((nsTableCellFrame *)aNextFrame)->VerticallyAlignChild(aPresContext, aReflowState.reflowState, mMaxCellAscent);
|
|
nsRect dirtyRect;
|
|
aNextFrame->GetRect(dirtyRect);
|
|
dirtyRect.height = mRect.height;
|
|
Invalidate(aPresContext, dirtyRect);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{ // pass reflow to unknown frame child
|
|
// aDesiredSize does not change
|
|
}
|
|
|
|
// When returning whether we're complete we need to look at each of our cell
|
|
// frames. If any of them has a continuing frame, then we're not complete. We
|
|
// can't just return the status of the cell frame we just reflowed...
|
|
aStatus = NS_FRAME_COMPLETE;
|
|
if (mNextInFlow) {
|
|
for (nsIFrame* cell = mFrames.FirstChild(); cell; cell->GetNextSibling(&cell)) {
|
|
nsIFrame* contFrame;
|
|
|
|
cell->GetNextInFlow(&contFrame);
|
|
if (contFrame) {
|
|
aStatus = NS_FRAME_NOT_COMPLETE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
|
|
/** Layout the entire row.
|
|
* This method stacks cells horizontally according to HTML 4.0 rules.
|
|
*/
|
|
NS_METHOD
|
|
nsTableRowFrame::Reflow(nsIPresContext* aPresContext,
|
|
nsHTMLReflowMetrics& aDesiredSize,
|
|
const nsHTMLReflowState& aReflowState,
|
|
nsReflowStatus& aStatus)
|
|
{
|
|
DO_GLOBAL_REFLOW_COUNT("nsTableRowFrame", aReflowState.reason);
|
|
#if defined DEBUG_TABLE_REFLOW | DEBUG_TABLE_REFLOW_TIMING
|
|
nsTableFrame::DebugReflow(this, (nsHTMLReflowState&)aReflowState);
|
|
#endif
|
|
nsresult rv = NS_OK;
|
|
|
|
// Initialize 'out' parameters (aStatus set below, undefined if rv returns an error)
|
|
if (nsnull != aDesiredSize.maxElementSize) {
|
|
aDesiredSize.maxElementSize->width = 0;
|
|
aDesiredSize.maxElementSize->height = 0;
|
|
}
|
|
|
|
// Create a reflow state
|
|
nsTableFrame *tableFrame=nsnull;
|
|
rv = nsTableFrame::GetTableFrame(this, tableFrame);
|
|
if (NS_FAILED(rv) || nsnull==tableFrame)
|
|
return rv;
|
|
RowReflowState state(aReflowState, tableFrame);
|
|
|
|
// Do the reflow
|
|
// XXX If the width is unconstrained, then treat it like we would treat the
|
|
// initial reflow instead. This needs to be cleaned up
|
|
nsReflowReason reason = aReflowState.reason;
|
|
if (NS_UNCONSTRAINEDSIZE == aReflowState.availableWidth) {
|
|
reason = eReflowReason_Initial;
|
|
}
|
|
|
|
switch (reason) {
|
|
case eReflowReason_Initial:
|
|
rv = InitialReflow(aPresContext, aDesiredSize, state, aStatus, nsnull, PR_TRUE);
|
|
if (!tableFrame->IsAutoLayout())
|
|
{ // this resize reflow is necessary to place the cells correctly in the case of rowspans and colspans.
|
|
// It is very efficient. It does not actually need to pass a reflow down to the cells.
|
|
nsSize availSpace(aReflowState.availableWidth, aReflowState.availableHeight);
|
|
nsHTMLReflowState resizeReflowState(aPresContext,
|
|
(const nsHTMLReflowState&)(*(aReflowState.parentReflowState)),
|
|
(nsIFrame *)this,
|
|
availSpace,
|
|
eReflowReason_Resize);
|
|
RowReflowState rowResizeReflowState(resizeReflowState, tableFrame);
|
|
rv = ResizeReflow(aPresContext, aDesiredSize, rowResizeReflowState, aStatus);
|
|
}
|
|
//GetMinRowSpan(tableFrame);
|
|
//FixMinCellHeight(tableFrame);
|
|
aStatus = NS_FRAME_COMPLETE;
|
|
break;
|
|
|
|
case eReflowReason_Resize:
|
|
case eReflowReason_StyleChange:
|
|
rv = ResizeReflow(aPresContext, aDesiredSize, state, aStatus);
|
|
break;
|
|
|
|
case eReflowReason_Incremental:
|
|
rv = IncrementalReflow(aPresContext, aDesiredSize, state, aStatus);
|
|
break;
|
|
}
|
|
|
|
// If we computed our max element element size, then cache it so we can return
|
|
// it later when asked
|
|
if (aDesiredSize.maxElementSize) {
|
|
mMaxElementSize = *aDesiredSize.maxElementSize;
|
|
}
|
|
|
|
#if defined DEBUG_TABLE_REFLOW | DEBUG_TABLE_REFLOW_TIMING
|
|
nsTableFrame::DebugReflow(this, (nsHTMLReflowState&)aReflowState, &aDesiredSize, aStatus);
|
|
#endif
|
|
return rv;
|
|
}
|
|
|
|
/* we overload this here because rows have children that can span outside of themselves.
|
|
* so the default "get the child rect, see if it contains the event point" action isn't
|
|
* sufficient. We have to ask the row if it has a child that contains the point.
|
|
*/
|
|
PRBool nsTableRowFrame::Contains(nsIPresContext* aPresContext, const nsPoint& aPoint)
|
|
{
|
|
PRBool result = PR_FALSE;
|
|
// first, check the row rect and see if the point is in their
|
|
if (mRect.Contains(aPoint)) {
|
|
result = PR_TRUE;
|
|
}
|
|
// if that fails, check the cells, they might span outside the row rect
|
|
else {
|
|
nsIFrame* kid;
|
|
FirstChild(aPresContext, nsnull, &kid);
|
|
while (nsnull != kid) {
|
|
nsRect kidRect;
|
|
kid->GetRect(kidRect);
|
|
nsPoint point(aPoint);
|
|
point.MoveBy(-mRect.x, -mRect.y); // offset the point to check by the row container
|
|
if (kidRect.Contains(point)) {
|
|
result = PR_TRUE;
|
|
break;
|
|
}
|
|
kid->GetNextSibling(&kid);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* This function is called by the row group frame's SplitRowGroup() code when
|
|
* pushing a row frame that has cell frames that span into it. The cell frame
|
|
* should be reflowed with the specified height
|
|
*/
|
|
void nsTableRowFrame::ReflowCellFrame(nsIPresContext* aPresContext,
|
|
const nsHTMLReflowState& aReflowState,
|
|
nsTableCellFrame* aCellFrame,
|
|
nscoord aAvailableHeight,
|
|
nsReflowStatus& aStatus)
|
|
{
|
|
// Reflow the cell frame with the specified height. Use the existing width
|
|
nsSize cellSize;
|
|
aCellFrame->GetSize(cellSize);
|
|
|
|
nsSize availSize(cellSize.width, aAvailableHeight);
|
|
nsTableCellReflowState cellReflowState(aPresContext, aReflowState, aCellFrame, availSize,
|
|
eReflowReason_Resize);
|
|
nsHTMLReflowMetrics desiredSize(nsnull);
|
|
|
|
ReflowChild(aCellFrame, aPresContext, desiredSize, cellReflowState,
|
|
0, 0, NS_FRAME_NO_MOVE_FRAME, aStatus);
|
|
aCellFrame->SizeTo(aPresContext, cellSize.width, aAvailableHeight);
|
|
// XXX What happens if this cell has 'vertical-align: baseline' ?
|
|
// XXX Why is it assumed that the cell's ascent hasn't changed ?
|
|
aCellFrame->VerticallyAlignChild(aPresContext, aReflowState, mMaxCellAscent);
|
|
aCellFrame->DidReflow(aPresContext, NS_FRAME_REFLOW_FINISHED);
|
|
}
|
|
|
|
/**
|
|
* This function is called by the row group frame's SplitRowGroup() code when
|
|
* it creates a continuing cell frame and wants to insert it into the row's
|
|
* child list
|
|
*/
|
|
void nsTableRowFrame::InsertCellFrame(nsTableCellFrame* aFrame,
|
|
nsTableCellFrame* aPrevSibling)
|
|
{
|
|
mFrames.InsertFrame(nsnull, aPrevSibling, aFrame);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsTableRowFrame::GetFrameType(nsIAtom** aType) const
|
|
{
|
|
NS_PRECONDITION(nsnull != aType, "null OUT parameter pointer");
|
|
*aType = nsLayoutAtoms::tableRowFrame;
|
|
NS_ADDREF(*aType);
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
/* ----- global methods ----- */
|
|
|
|
nsresult
|
|
NS_NewTableRowFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame)
|
|
{
|
|
NS_PRECONDITION(aNewFrame, "null OUT ptr");
|
|
if (nsnull == aNewFrame) {
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
nsTableRowFrame* it = new (aPresShell) nsTableRowFrame;
|
|
if (nsnull == it) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
*aNewFrame = it;
|
|
return NS_OK;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
NS_IMETHODIMP
|
|
nsTableRowFrame::GetFrameName(nsString& aResult) const
|
|
{
|
|
return MakeFrameName("TableRow", aResult);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsTableRowFrame::SizeOf(nsISizeOfHandler* aHandler, PRUint32* aResult) const
|
|
{
|
|
if (!aResult) {
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
PRUint32 sum = sizeof(*this);
|
|
*aResult = sum;
|
|
return NS_OK;
|
|
}
|
|
#endif
|