forked from mirrors/gecko-dev
nsReflowStatus::IsEmpty() assertions are added after DISPLAY_REFLOW in the beginning of the Reflow(). A few Reflow() implementations have Reset() calls at the end which are left in place by this patch (with an explanatory comment added to each). These ending Reset()s are only needed for cases where a non-splittable frame passes its own nsReflowStatus to a child's reflow method. Just in case the child leaves a "not fully complete" value in the nsReflowStatus, the non-splittable parent frame must clear out the nsReflowStatus before returning, so that its own parent doesn't then try to split it. MozReview-Commit-ID: 6Jj3jfMAqj4 --HG-- extra : rebase_source : e1fe6a775ad97e16a6d7cc224634ff962ccb0fbf
519 lines
18 KiB
C++
519 lines
18 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
#include "nsTableColGroupFrame.h"
|
|
#include "nsTableColFrame.h"
|
|
#include "nsTableFrame.h"
|
|
#include "nsStyleContext.h"
|
|
#include "nsStyleConsts.h"
|
|
#include "nsPresContext.h"
|
|
#include "nsHTMLParts.h"
|
|
#include "nsGkAtoms.h"
|
|
#include "nsCOMPtr.h"
|
|
#include "nsCSSRendering.h"
|
|
#include "nsIPresShell.h"
|
|
#include "mozilla/GeckoStyleContext.h"
|
|
|
|
using namespace mozilla;
|
|
|
|
#define COLGROUP_SYNTHETIC_BIT NS_FRAME_STATE_BIT(30)
|
|
|
|
bool
|
|
nsTableColGroupFrame::IsSynthetic() const
|
|
{
|
|
return HasAnyStateBits(COLGROUP_SYNTHETIC_BIT);
|
|
}
|
|
|
|
void nsTableColGroupFrame::SetIsSynthetic()
|
|
{
|
|
AddStateBits(COLGROUP_SYNTHETIC_BIT);
|
|
}
|
|
|
|
void nsTableColGroupFrame::ResetColIndices(nsIFrame* aFirstColGroup,
|
|
int32_t aFirstColIndex,
|
|
nsIFrame* aStartColFrame)
|
|
{
|
|
nsTableColGroupFrame* colGroupFrame = (nsTableColGroupFrame*)aFirstColGroup;
|
|
int32_t colIndex = aFirstColIndex;
|
|
while (colGroupFrame) {
|
|
if (colGroupFrame->IsTableColGroupFrame()) {
|
|
// reset the starting col index for the first cg only if we should reset
|
|
// the whole colgroup (aStartColFrame defaults to nullptr) or if
|
|
// aFirstColIndex is smaller than the existing starting col index
|
|
if ((colIndex != aFirstColIndex) ||
|
|
(colIndex < colGroupFrame->GetStartColumnIndex()) ||
|
|
!aStartColFrame) {
|
|
colGroupFrame->SetStartColumnIndex(colIndex);
|
|
}
|
|
nsIFrame* colFrame = aStartColFrame;
|
|
if (!colFrame || (colIndex != aFirstColIndex)) {
|
|
colFrame = colGroupFrame->PrincipalChildList().FirstChild();
|
|
}
|
|
while (colFrame) {
|
|
if (colFrame->IsTableColFrame()) {
|
|
((nsTableColFrame*)colFrame)->SetColIndex(colIndex);
|
|
colIndex++;
|
|
}
|
|
colFrame = colFrame->GetNextSibling();
|
|
}
|
|
}
|
|
colGroupFrame = static_cast<nsTableColGroupFrame*>
|
|
(colGroupFrame->GetNextSibling());
|
|
}
|
|
}
|
|
|
|
|
|
nsresult
|
|
nsTableColGroupFrame::AddColsToTable(int32_t aFirstColIndex,
|
|
bool aResetSubsequentColIndices,
|
|
const nsFrameList::Slice& aCols)
|
|
{
|
|
nsTableFrame* tableFrame = GetTableFrame();
|
|
|
|
tableFrame->InvalidateFrameSubtree();
|
|
|
|
// set the col indices of the col frames and and add col info to the table
|
|
int32_t colIndex = aFirstColIndex;
|
|
nsFrameList::Enumerator e(aCols);
|
|
for (; !e.AtEnd(); e.Next()) {
|
|
((nsTableColFrame*)e.get())->SetColIndex(colIndex);
|
|
mColCount++;
|
|
tableFrame->InsertCol((nsTableColFrame &)*e.get(), colIndex);
|
|
colIndex++;
|
|
}
|
|
|
|
for (nsFrameList::Enumerator eTail = e.GetUnlimitedEnumerator();
|
|
!eTail.AtEnd();
|
|
eTail.Next()) {
|
|
((nsTableColFrame*)eTail.get())->SetColIndex(colIndex);
|
|
colIndex++;
|
|
}
|
|
|
|
// We have already set the colindex for all the colframes in this
|
|
// colgroup that come after the first inserted colframe, but there could
|
|
// be other colgroups following this one and their colframes need
|
|
// correct colindices too.
|
|
if (aResetSubsequentColIndices && GetNextSibling()) {
|
|
ResetColIndices(GetNextSibling(), colIndex);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
nsTableColGroupFrame*
|
|
nsTableColGroupFrame::GetLastRealColGroup(nsTableFrame* aTableFrame)
|
|
{
|
|
nsFrameList colGroups = aTableFrame->GetColGroups();
|
|
|
|
auto lastColGroup = static_cast<nsTableColGroupFrame*>(colGroups.LastChild());
|
|
if (!lastColGroup) {
|
|
return nullptr;
|
|
}
|
|
|
|
if (!lastColGroup->IsSynthetic()) {
|
|
return lastColGroup;
|
|
}
|
|
|
|
return static_cast<nsTableColGroupFrame*>(lastColGroup->GetPrevSibling());
|
|
}
|
|
|
|
// don't set mColCount here, it is done in AddColsToTable
|
|
void
|
|
nsTableColGroupFrame::SetInitialChildList(ChildListID aListID,
|
|
nsFrameList& aChildList)
|
|
{
|
|
MOZ_ASSERT(mFrames.IsEmpty(),
|
|
"unexpected second call to SetInitialChildList");
|
|
MOZ_ASSERT(aListID == kPrincipalList, "unexpected child list");
|
|
if (aChildList.IsEmpty()) {
|
|
GetTableFrame()->AppendAnonymousColFrames(this, GetSpan(),
|
|
eColAnonymousColGroup, false);
|
|
return;
|
|
}
|
|
|
|
mFrames.AppendFrames(this, aChildList);
|
|
}
|
|
|
|
/* virtual */ void
|
|
nsTableColGroupFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
|
|
{
|
|
nsContainerFrame::DidSetStyleContext(aOldStyleContext);
|
|
|
|
if (!aOldStyleContext) //avoid this on init
|
|
return;
|
|
|
|
nsTableFrame* tableFrame = GetTableFrame();
|
|
if (tableFrame->IsBorderCollapse() &&
|
|
tableFrame->BCRecalcNeeded(aOldStyleContext, StyleContext())) {
|
|
int32_t colCount = GetColCount();
|
|
if (!colCount)
|
|
return; // this is a degenerated colgroup
|
|
TableArea damageArea(GetFirstColumn()->GetColIndex(), 0, colCount,
|
|
tableFrame->GetRowCount());
|
|
tableFrame->AddBCDamageArea(damageArea);
|
|
}
|
|
}
|
|
|
|
void
|
|
nsTableColGroupFrame::AppendFrames(ChildListID aListID,
|
|
nsFrameList& aFrameList)
|
|
{
|
|
NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
|
|
|
|
nsTableColFrame* col = GetFirstColumn();
|
|
nsTableColFrame* nextCol;
|
|
while (col && col->GetColType() == eColAnonymousColGroup) {
|
|
// this colgroup spans one or more columns but now that there is a
|
|
// real column below, spanned anonymous columns should be removed,
|
|
// since the HTML spec says to ignore the span of a colgroup if it
|
|
// has content columns in it.
|
|
nextCol = col->GetNextCol();
|
|
RemoveFrame(kPrincipalList, col);
|
|
col = nextCol;
|
|
}
|
|
|
|
// Our next colframe should be an eColContent. We've removed all the
|
|
// eColAnonymousColGroup colframes, eColAnonymousCol colframes always follow
|
|
// eColContent ones, and eColAnonymousCell colframes only appear in a
|
|
// synthetic colgroup, which never gets AppendFrames() called on it.
|
|
MOZ_ASSERT(!col || col->GetColType() == eColContent,
|
|
"What's going on with our columns?");
|
|
|
|
const nsFrameList::Slice& newFrames =
|
|
mFrames.AppendFrames(this, aFrameList);
|
|
InsertColsReflow(GetStartColumnIndex() + mColCount, newFrames);
|
|
}
|
|
|
|
void
|
|
nsTableColGroupFrame::InsertFrames(ChildListID aListID,
|
|
nsIFrame* aPrevFrame,
|
|
nsFrameList& aFrameList)
|
|
{
|
|
NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
|
|
NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
|
|
"inserting after sibling frame with different parent");
|
|
|
|
nsTableColFrame* col = GetFirstColumn();
|
|
nsTableColFrame* nextCol;
|
|
while (col && col->GetColType() == eColAnonymousColGroup) {
|
|
// this colgroup spans one or more columns but now that there is a
|
|
// real column below, spanned anonymous columns should be removed,
|
|
// since the HTML spec says to ignore the span of a colgroup if it
|
|
// has content columns in it.
|
|
nextCol = col->GetNextCol();
|
|
if (col == aPrevFrame) {
|
|
// This can happen when we're being appended to
|
|
NS_ASSERTION(!nextCol || nextCol->GetColType() != eColAnonymousColGroup,
|
|
"Inserting in the middle of our anonymous cols?");
|
|
// We'll want to insert at the beginning
|
|
aPrevFrame = nullptr;
|
|
}
|
|
RemoveFrame(kPrincipalList, col);
|
|
col = nextCol;
|
|
}
|
|
|
|
// Our next colframe should be an eColContent. We've removed all the
|
|
// eColAnonymousColGroup colframes, eColAnonymousCol colframes always follow
|
|
// eColContent ones, and eColAnonymousCell colframes only appear in a
|
|
// synthetic colgroup, which never gets InsertFrames() called on it.
|
|
MOZ_ASSERT(!col || col->GetColType() == eColContent,
|
|
"What's going on with our columns?");
|
|
|
|
NS_ASSERTION(!aPrevFrame || aPrevFrame == aPrevFrame->LastContinuation(),
|
|
"Prev frame should be last in continuation chain");
|
|
NS_ASSERTION(!aPrevFrame || !GetNextColumn(aPrevFrame) ||
|
|
GetNextColumn(aPrevFrame)->GetColType() != eColAnonymousCol,
|
|
"Shouldn't be inserting before a spanned colframe");
|
|
|
|
const nsFrameList::Slice& newFrames =
|
|
mFrames.InsertFrames(this, aPrevFrame, aFrameList);
|
|
nsIFrame* prevFrame = nsTableFrame::GetFrameAtOrBefore(
|
|
this, aPrevFrame, LayoutFrameType::TableCol);
|
|
|
|
int32_t colIndex = (prevFrame) ? ((nsTableColFrame*)prevFrame)->GetColIndex() + 1 : GetStartColumnIndex();
|
|
InsertColsReflow(colIndex, newFrames);
|
|
}
|
|
|
|
void
|
|
nsTableColGroupFrame::InsertColsReflow(int32_t aColIndex,
|
|
const nsFrameList::Slice& aCols)
|
|
{
|
|
AddColsToTable(aColIndex, true, aCols);
|
|
|
|
PresContext()->PresShell()->FrameNeedsReflow(this,
|
|
nsIPresShell::eTreeChange,
|
|
NS_FRAME_HAS_DIRTY_CHILDREN);
|
|
}
|
|
|
|
void
|
|
nsTableColGroupFrame::RemoveChild(nsTableColFrame& aChild,
|
|
bool aResetSubsequentColIndices)
|
|
{
|
|
int32_t colIndex = 0;
|
|
nsIFrame* nextChild = nullptr;
|
|
if (aResetSubsequentColIndices) {
|
|
colIndex = aChild.GetColIndex();
|
|
nextChild = aChild.GetNextSibling();
|
|
}
|
|
mFrames.DestroyFrame(&aChild);
|
|
mColCount--;
|
|
if (aResetSubsequentColIndices) {
|
|
if (nextChild) { // reset inside this and all following colgroups
|
|
ResetColIndices(this, colIndex, nextChild);
|
|
}
|
|
else {
|
|
nsIFrame* nextGroup = GetNextSibling();
|
|
if (nextGroup) // reset next and all following colgroups
|
|
ResetColIndices(nextGroup, colIndex);
|
|
}
|
|
}
|
|
|
|
PresContext()->PresShell()->FrameNeedsReflow(this,
|
|
nsIPresShell::eTreeChange,
|
|
NS_FRAME_HAS_DIRTY_CHILDREN);
|
|
}
|
|
|
|
void
|
|
nsTableColGroupFrame::RemoveFrame(ChildListID aListID,
|
|
nsIFrame* aOldFrame)
|
|
{
|
|
NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
|
|
|
|
if (!aOldFrame) {
|
|
return;
|
|
}
|
|
bool contentRemoval = false;
|
|
|
|
if (aOldFrame->IsTableColFrame()) {
|
|
nsTableColFrame* colFrame = (nsTableColFrame*)aOldFrame;
|
|
if (colFrame->GetColType() == eColContent) {
|
|
contentRemoval = true;
|
|
// Remove any anonymous column frames this <col> produced via a colspan
|
|
nsTableColFrame* col = colFrame->GetNextCol();
|
|
nsTableColFrame* nextCol;
|
|
while (col && col->GetColType() == eColAnonymousCol) {
|
|
#ifdef DEBUG
|
|
nsIFrame* providerFrame;
|
|
nsStyleContext* psc = colFrame->GetParentStyleContext(&providerFrame);
|
|
if (psc->IsGecko()) {
|
|
// This check code is useful only in Gecko-backed style system.
|
|
if (colFrame->StyleContext()->AsGecko()->GetParent() == psc->AsGecko()) {
|
|
NS_ASSERTION(col->StyleContext() == colFrame->StyleContext() &&
|
|
col->GetContent() == colFrame->GetContent(),
|
|
"How did that happen??");
|
|
}
|
|
// else colFrame is being removed because of a frame
|
|
// reconstruct on it, and its style context is still the old
|
|
// one, so we can't assert anything about how it compares to
|
|
// col's style context.
|
|
}
|
|
#endif
|
|
nextCol = col->GetNextCol();
|
|
RemoveFrame(kPrincipalList, col);
|
|
col = nextCol;
|
|
}
|
|
}
|
|
|
|
int32_t colIndex = colFrame->GetColIndex();
|
|
// The RemoveChild call handles calling FrameNeedsReflow on us.
|
|
RemoveChild(*colFrame, true);
|
|
|
|
nsTableFrame* tableFrame = GetTableFrame();
|
|
tableFrame->RemoveCol(this, colIndex, true, true);
|
|
if (mFrames.IsEmpty() && contentRemoval && !IsSynthetic()) {
|
|
tableFrame->AppendAnonymousColFrames(this, GetSpan(),
|
|
eColAnonymousColGroup, true);
|
|
}
|
|
}
|
|
else {
|
|
mFrames.DestroyFrame(aOldFrame);
|
|
}
|
|
}
|
|
|
|
nsIFrame::LogicalSides
|
|
nsTableColGroupFrame::GetLogicalSkipSides(const ReflowInput* aReflowInput) const
|
|
{
|
|
if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==
|
|
StyleBoxDecorationBreak::Clone)) {
|
|
return LogicalSides();
|
|
}
|
|
|
|
LogicalSides skip;
|
|
if (nullptr != GetPrevInFlow()) {
|
|
skip |= eLogicalSideBitsBStart;
|
|
}
|
|
if (nullptr != GetNextInFlow()) {
|
|
skip |= eLogicalSideBitsBEnd;
|
|
}
|
|
return skip;
|
|
}
|
|
|
|
void
|
|
nsTableColGroupFrame::Reflow(nsPresContext* aPresContext,
|
|
ReflowOutput& aDesiredSize,
|
|
const ReflowInput& aReflowInput,
|
|
nsReflowStatus& aStatus)
|
|
{
|
|
MarkInReflow();
|
|
DO_GLOBAL_REFLOW_COUNT("nsTableColGroupFrame");
|
|
DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
|
|
MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
|
|
NS_ASSERTION(nullptr!=mContent, "bad state -- null content for frame");
|
|
|
|
const nsStyleVisibility* groupVis = StyleVisibility();
|
|
bool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE == groupVis->mVisible);
|
|
if (collapseGroup) {
|
|
GetTableFrame()->SetNeedToCollapse(true);
|
|
}
|
|
// for every content child that (is a column thingy and does not already have a frame)
|
|
// create a frame and adjust it's style
|
|
|
|
for (nsIFrame *kidFrame = mFrames.FirstChild(); kidFrame;
|
|
kidFrame = kidFrame->GetNextSibling()) {
|
|
// Give the child frame a chance to reflow, even though we know it'll have 0 size
|
|
ReflowOutput kidSize(aReflowInput);
|
|
ReflowInput kidReflowInput(aPresContext, aReflowInput, kidFrame,
|
|
LogicalSize(kidFrame->GetWritingMode()));
|
|
|
|
nsReflowStatus status;
|
|
ReflowChild(kidFrame, aPresContext, kidSize, kidReflowInput, 0, 0, 0, status);
|
|
FinishReflowChild(kidFrame, aPresContext, kidSize, nullptr, 0, 0, 0);
|
|
}
|
|
|
|
aDesiredSize.ClearSize();
|
|
NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
|
|
}
|
|
|
|
void
|
|
nsTableColGroupFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
|
const nsDisplayListSet& aLists)
|
|
{
|
|
nsTableFrame::DisplayGenericTablePart(aBuilder, this, aLists);
|
|
}
|
|
|
|
nsTableColFrame * nsTableColGroupFrame::GetFirstColumn()
|
|
{
|
|
return GetNextColumn(nullptr);
|
|
}
|
|
|
|
nsTableColFrame * nsTableColGroupFrame::GetNextColumn(nsIFrame *aChildFrame)
|
|
{
|
|
nsTableColFrame *result = nullptr;
|
|
nsIFrame *childFrame = aChildFrame;
|
|
if (!childFrame) {
|
|
childFrame = mFrames.FirstChild();
|
|
}
|
|
else {
|
|
childFrame = childFrame->GetNextSibling();
|
|
}
|
|
while (childFrame)
|
|
{
|
|
if (mozilla::StyleDisplay::TableColumn ==
|
|
childFrame->StyleDisplay()->mDisplay)
|
|
{
|
|
result = (nsTableColFrame *)childFrame;
|
|
break;
|
|
}
|
|
childFrame = childFrame->GetNextSibling();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
int32_t nsTableColGroupFrame::GetSpan()
|
|
{
|
|
return StyleTable()->mSpan;
|
|
}
|
|
|
|
void nsTableColGroupFrame::SetContinuousBCBorderWidth(LogicalSide aForSide,
|
|
BCPixelSize aPixelValue)
|
|
{
|
|
switch (aForSide) {
|
|
case eLogicalSideBStart:
|
|
mBStartContBorderWidth = aPixelValue;
|
|
return;
|
|
case eLogicalSideBEnd:
|
|
mBEndContBorderWidth = aPixelValue;
|
|
return;
|
|
default:
|
|
NS_ERROR("invalid side arg");
|
|
}
|
|
}
|
|
|
|
void nsTableColGroupFrame::GetContinuousBCBorderWidth(WritingMode aWM,
|
|
LogicalMargin& aBorder)
|
|
{
|
|
int32_t d2a = PresContext()->AppUnitsPerDevPixel();
|
|
nsTableColFrame* col = GetTableFrame()->
|
|
GetColFrame(mStartColIndex + mColCount - 1);
|
|
col->GetContinuousBCBorderWidth(aWM, aBorder);
|
|
aBorder.BStart(aWM) = BC_BORDER_END_HALF_COORD(d2a,
|
|
mBStartContBorderWidth);
|
|
aBorder.BEnd(aWM) = BC_BORDER_START_HALF_COORD(d2a,
|
|
mBEndContBorderWidth);
|
|
}
|
|
|
|
/* ----- global methods ----- */
|
|
|
|
nsTableColGroupFrame*
|
|
NS_NewTableColGroupFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
|
|
{
|
|
return new (aPresShell) nsTableColGroupFrame(aContext);
|
|
}
|
|
|
|
NS_IMPL_FRAMEARENA_HELPERS(nsTableColGroupFrame)
|
|
|
|
void
|
|
nsTableColGroupFrame::InvalidateFrame(uint32_t aDisplayItemKey)
|
|
{
|
|
nsIFrame::InvalidateFrame(aDisplayItemKey);
|
|
GetParent()->InvalidateFrameWithRect(GetVisualOverflowRect() + GetPosition(), aDisplayItemKey);
|
|
}
|
|
|
|
void
|
|
nsTableColGroupFrame::InvalidateFrameWithRect(const nsRect& aRect,
|
|
uint32_t aDisplayItemKey)
|
|
{
|
|
nsIFrame::InvalidateFrameWithRect(aRect, aDisplayItemKey);
|
|
// If we have filters applied that would affects our bounds, then
|
|
// we get an inactive layer created and this is computed
|
|
// within FrameLayerBuilder
|
|
GetParent()->InvalidateFrameWithRect(aRect + GetPosition(), aDisplayItemKey);
|
|
}
|
|
|
|
#ifdef DEBUG_FRAME_DUMP
|
|
nsresult
|
|
nsTableColGroupFrame::GetFrameName(nsAString& aResult) const
|
|
{
|
|
return MakeFrameName(NS_LITERAL_STRING("TableColGroup"), aResult);
|
|
}
|
|
|
|
void nsTableColGroupFrame::Dump(int32_t aIndent)
|
|
{
|
|
char* indent = new char[aIndent + 1];
|
|
if (!indent) return;
|
|
for (int32_t i = 0; i < aIndent + 1; i++) {
|
|
indent[i] = ' ';
|
|
}
|
|
indent[aIndent] = 0;
|
|
|
|
printf("%s**START COLGROUP DUMP**\n%s startcolIndex=%d colcount=%d span=%d isSynthetic=%s",
|
|
indent, indent, GetStartColumnIndex(), GetColCount(), GetSpan(),
|
|
IsSynthetic() ? "true" : "false");
|
|
|
|
// verify the colindices
|
|
int32_t j = GetStartColumnIndex();
|
|
nsTableColFrame* col = GetFirstColumn();
|
|
while (col) {
|
|
NS_ASSERTION(j == col->GetColIndex(), "wrong colindex on col frame");
|
|
col = col->GetNextCol();
|
|
j++;
|
|
}
|
|
NS_ASSERTION((j - GetStartColumnIndex()) == GetColCount(),
|
|
"number of cols out of sync");
|
|
printf("\n%s**END COLGROUP DUMP** ", indent);
|
|
delete [] indent;
|
|
}
|
|
#endif
|
|
|