Bug 63895 (Part 2) - Support table parts as absolute containing blocks. r=dbaron

This commit is contained in:
Seth Fowler 2014-02-19 16:03:30 -08:00
parent 44c398a3ec
commit 1ae544f009
10 changed files with 281 additions and 36 deletions

View File

@ -1982,27 +1982,64 @@ nsCSSFrameConstructor::ConstructTable(nsFrameConstructorState& aState,
return newFrame;
}
nsIFrame*
nsCSSFrameConstructor::ConstructTableRow(nsFrameConstructorState& aState,
FrameConstructionItem& aItem,
nsIFrame* aParentFrame,
const nsStyleDisplay* aDisplay,
nsFrameItems& aFrameItems)
static void
MakeTablePartAbsoluteContainingBlockIfNeeded(nsFrameConstructorState& aState,
const nsStyleDisplay* aDisplay,
nsFrameConstructorSaveState& aAbsSaveState,
nsIFrame* aFrame)
{
NS_PRECONDITION(aDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_ROW,
// If we're positioned, then we need to become an absolute containing block
// for any absolutely positioned children and register for post-reflow fixup.
//
// Note that usually if a frame type can be an absolute containing block, we
// always set NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN, whether it actually is or not.
// However, in this case flag serves the additional purpose of indicating that
// the frame was registered with its table frame. This allows us to avoid the
// overhead of unregistering the frame in most cases.
if (aDisplay->IsRelativelyPositionedStyle() ||
aDisplay->IsAbsolutelyPositionedStyle() ||
aDisplay->HasTransform(aFrame)) {
aFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
aState.PushAbsoluteContainingBlock(aFrame, aFrame, aAbsSaveState);
nsTableFrame::RegisterPositionedTablePart(aFrame);
}
}
nsIFrame*
nsCSSFrameConstructor::ConstructTableRowOrRowGroup(nsFrameConstructorState& aState,
FrameConstructionItem& aItem,
nsIFrame* aParentFrame,
const nsStyleDisplay* aDisplay,
nsFrameItems& aFrameItems)
{
NS_PRECONDITION(aDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_ROW ||
aDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_ROW_GROUP ||
aDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP ||
aDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_HEADER_GROUP,
"Unexpected call");
MOZ_ASSERT(aItem.mStyleContext->StyleDisplay() == aDisplay,
"Display style doesn't match style context");
nsIContent* const content = aItem.mContent;
nsStyleContext* const styleContext = aItem.mStyleContext;
const uint32_t nameSpaceID = aItem.mNameSpaceID;
nsIFrame* newFrame;
if (kNameSpaceID_MathML == nameSpaceID)
newFrame = NS_NewMathMLmtrFrame(mPresShell, styleContext);
else
newFrame = NS_NewTableRowFrame(mPresShell, styleContext);
if (aDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_ROW) {
if (kNameSpaceID_MathML == nameSpaceID)
newFrame = NS_NewMathMLmtrFrame(mPresShell, styleContext);
else
newFrame = NS_NewTableRowFrame(mPresShell, styleContext);
} else {
newFrame = NS_NewTableRowGroupFrame(mPresShell, styleContext);
}
InitAndRestoreFrame(aState, content, aParentFrame, newFrame);
nsFrameConstructorSaveState absoluteSaveState;
MakeTablePartAbsoluteContainingBlockIfNeeded(aState, aDisplay,
absoluteSaveState,
newFrame);
nsFrameItems childItems;
NS_ASSERTION(aItem.mAnonChildren.IsEmpty(),
"nsIAnonymousContentCreator::CreateAnonymousContent "
@ -2105,6 +2142,11 @@ nsCSSFrameConstructor::ConstructTableCell(nsFrameConstructorState& aState,
InitAndRestoreFrame(aState, content, newFrame, cellInnerFrame);
nsFrameConstructorSaveState absoluteSaveState;
MakeTablePartAbsoluteContainingBlockIfNeeded(aState, aDisplay,
absoluteSaveState,
newFrame);
nsFrameItems childItems;
NS_ASSERTION(aItem.mAnonChildren.IsEmpty(),
"nsIAnonymousContentCreator::CreateAnonymousContent "
@ -4375,20 +4417,17 @@ nsCSSFrameConstructor::FindDisplayData(const nsStyleDisplay* aDisplay,
FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
NS_NewTableCaptionFrame) },
{ NS_STYLE_DISPLAY_TABLE_ROW_GROUP,
FCDATA_DECL(FCDATA_IS_TABLE_PART | FCDATA_DISALLOW_OUT_OF_FLOW |
FCDATA_SKIP_ABSPOS_PUSH |
FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
NS_NewTableRowGroupFrame) },
FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART |
FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
&nsCSSFrameConstructor::ConstructTableRowOrRowGroup) },
{ NS_STYLE_DISPLAY_TABLE_HEADER_GROUP,
FCDATA_DECL(FCDATA_IS_TABLE_PART | FCDATA_DISALLOW_OUT_OF_FLOW |
FCDATA_SKIP_ABSPOS_PUSH |
FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
NS_NewTableRowGroupFrame) },
FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART |
FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
&nsCSSFrameConstructor::ConstructTableRowOrRowGroup) },
{ NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP,
FCDATA_DECL(FCDATA_IS_TABLE_PART | FCDATA_DISALLOW_OUT_OF_FLOW |
FCDATA_SKIP_ABSPOS_PUSH |
FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
NS_NewTableRowGroupFrame) },
FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART |
FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
&nsCSSFrameConstructor::ConstructTableRowOrRowGroup) },
{ NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP,
FCDATA_DECL(FCDATA_IS_TABLE_PART | FCDATA_DISALLOW_OUT_OF_FLOW |
FCDATA_SKIP_ABSPOS_PUSH |
@ -4401,7 +4440,7 @@ nsCSSFrameConstructor::FindDisplayData(const nsStyleDisplay* aDisplay,
{ NS_STYLE_DISPLAY_TABLE_ROW,
FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART |
FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRowGroup),
&nsCSSFrameConstructor::ConstructTableRow) },
&nsCSSFrameConstructor::ConstructTableRowOrRowGroup) },
{ NS_STYLE_DISPLAY_TABLE_CELL,
FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART |
FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRow),
@ -8818,15 +8857,14 @@ nsCSSFrameConstructor::sPseudoParentData[eParentTypeCount] = {
FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET |
FCDATA_USE_CHILD_ITEMS |
FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRowGroup),
&nsCSSFrameConstructor::ConstructTableRow),
&nsCSSFrameConstructor::ConstructTableRowOrRowGroup),
&nsCSSAnonBoxes::tableRow
},
{ // Row group
FCDATA_DECL(FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET |
FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_USE_CHILD_ITEMS |
FCDATA_SKIP_ABSPOS_PUSH |
FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
NS_NewTableRowGroupFrame),
FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET |
FCDATA_USE_CHILD_ITEMS |
FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
&nsCSSFrameConstructor::ConstructTableRowOrRowGroup),
&nsCSSAnonBoxes::tableRowGroup
},
{ // Column group

View File

@ -391,13 +391,13 @@ private:
nsFrameItems& aFrameItems);
/**
* FrameConstructionData callback used for constructing table rows.
* FrameConstructionData callback for constructing table rows and row groups.
*/
nsIFrame* ConstructTableRow(nsFrameConstructorState& aState,
FrameConstructionItem& aItem,
nsIFrame* aParentFrame,
const nsStyleDisplay* aStyleDisplay,
nsFrameItems& aFrameItems);
nsIFrame* ConstructTableRowOrRowGroup(nsFrameConstructorState& aState,
FrameConstructionItem& aItem,
nsIFrame* aParentFrame,
const nsStyleDisplay* aStyleDisplay,
nsFrameItems& aFrameItems);
/**
* FrameConstructionData callback used for constructing table columns.

View File

@ -87,6 +87,16 @@ nsTableCellFrame::Init(nsIContent* aContent,
}
}
void
nsTableCellFrame::DestroyFrom(nsIFrame* aDestructRoot)
{
if (GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN) {
nsTableFrame::UnregisterPositionedTablePart(this, aDestructRoot);
}
nsContainerFrame::DestroyFrom(aDestructRoot);
}
// nsIPercentHeightObserver methods
void

View File

@ -47,6 +47,8 @@ public:
nsIFrame* aParent,
nsIFrame* aPrevInFlow) MOZ_OVERRIDE;
virtual void DestroyFrom(nsIFrame* aDestructRoot) MOZ_OVERRIDE;
#ifdef ACCESSIBILITY
virtual mozilla::a11y::AccType AccessibleType() MOZ_OVERRIDE;
#endif

View File

@ -41,6 +41,7 @@
#include "nsDisplayList.h"
#include "nsIScrollableFrame.h"
#include "nsCSSProps.h"
#include "RestyleTracker.h"
#include <algorithm>
using namespace mozilla;
@ -252,6 +253,64 @@ nsTableFrame::PageBreakAfter(nsIFrame* aSourceFrame,
return false;
}
typedef nsTArray<nsIFrame*> FrameTArray;
/* static */ void
nsTableFrame::DestroyPositionedTablePartArray(void* aPropertyValue)
{
auto positionedObjs = static_cast<FrameTArray*>(aPropertyValue);
delete positionedObjs;
}
/* static */ void
nsTableFrame::RegisterPositionedTablePart(nsIFrame* aFrame)
{
nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(aFrame);
MOZ_ASSERT(tableFrame, "Should have a table frame here");
tableFrame = static_cast<nsTableFrame*>(tableFrame->FirstContinuation());
// Retrieve the positioned parts array for this table.
FrameProperties props = tableFrame->Properties();
auto positionedParts =
static_cast<FrameTArray*>(props.Get(PositionedTablePartArray()));
// Lazily create the array if it doesn't exist yet.
if (!positionedParts) {
positionedParts = new FrameTArray;
props.Set(PositionedTablePartArray(), positionedParts);
}
// Add this frame to the list.
positionedParts->AppendElement(aFrame);
}
/* static */ void
nsTableFrame::UnregisterPositionedTablePart(nsIFrame* aFrame,
nsIFrame* aDestructRoot)
{
// Retrieve the table frame, and ensure that we hit aDestructRoot on the way.
// If we don't, that means that the table frame will be destroyed, so we don't
// need to bother with unregistering this frame.
nsTableFrame* tableFrame = GetTableFramePassingThrough(aDestructRoot, aFrame);
if (!tableFrame) {
return;
}
tableFrame = static_cast<nsTableFrame*>(tableFrame->FirstContinuation());
// Retrieve the positioned parts array for this table.
FrameProperties props = tableFrame->Properties();
auto positionedParts =
static_cast<FrameTArray*>(props.Get(PositionedTablePartArray()));
// Remove the frame.
MOZ_ASSERT(positionedParts &&
positionedParts->IndexOf(aFrame) != FrameTArray::NoIndex,
"Asked to unregister a positioned table part that wasn't registered");
if (positionedParts) {
positionedParts->RemoveElement(aFrame);
}
}
// XXX this needs to be cleaned up so that the frame constructor breaks out col group
// frames into a separate child list, bug 343048.
nsresult
@ -1814,6 +1873,10 @@ nsresult nsTableFrame::Reflow(nsPresContext* aPresContext,
AdjustForCollapsingRowsCols(aDesiredSize, borderPadding);
}
// If there are any relatively-positioned table parts, we need to reflow their
// absolutely-positioned descendants now that their dimensions are final.
FixupPositionedTableParts(aPresContext, aReflowState);
// make sure the table overflow area does include the table rect.
nsRect tableRect(0, 0, aDesiredSize.Width(), aDesiredSize.Height()) ;
@ -1834,6 +1897,57 @@ nsresult nsTableFrame::Reflow(nsPresContext* aPresContext,
return rv;
}
void
nsTableFrame::FixupPositionedTableParts(nsPresContext* aPresContext,
const nsHTMLReflowState& aReflowState)
{
auto positionedParts =
static_cast<FrameTArray*>(Properties().Get(PositionedTablePartArray()));
if (!positionedParts) {
return;
}
OverflowChangedTracker overflowTracker;
overflowTracker.SetSubtreeRoot(this);
for (size_t i = 0; i < positionedParts->Length(); ++i) {
nsIFrame* positionedPart = positionedParts->ElementAt(i);
// As we've already finished reflow, positionedParts's size and overflow
// areas have already been assigned, so we just pull them back out.
nsSize size(positionedPart->GetSize());
nsHTMLReflowMetrics desiredSize(aReflowState.GetWritingMode());
desiredSize.Width() = size.width;
desiredSize.Height() = size.height;
desiredSize.mOverflowAreas = positionedPart->GetOverflowAreasRelativeToSelf();
// Construct a dummy reflow state and reflow status.
// XXX(seth): Note that the dummy reflow state doesn't have a correct
// chain of parent reflow states. It also doesn't necessarily have a
// correct containing block.
nsHTMLReflowState reflowState(aPresContext, positionedPart,
aReflowState.rendContext,
nsSize(size.width, NS_UNCONSTRAINEDSIZE),
nsHTMLReflowState::DUMMY_PARENT_REFLOW_STATE);
nsReflowStatus reflowStatus = NS_FRAME_COMPLETE;
// Reflow absolutely-positioned descendants of the positioned part.
// FIXME: Unconditionally using NS_UNCONSTRAINEDSIZE for the height and
// ignoring any change to the reflow status aren't correct. We'll never
// paginate absolutely positioned frames.
overflowTracker.AddFrame(positionedPart);
nsFrame* positionedFrame = static_cast<nsFrame*>(positionedPart);
positionedFrame->FinishReflowWithAbsoluteFrames(PresContext(),
desiredSize,
reflowState,
reflowStatus,
true);
}
// Propagate updated overflow areas up the tree.
overflowTracker.Flush();
}
bool
nsTableFrame::UpdateOverflow()
{
@ -3444,6 +3558,31 @@ nsTableFrame::GetTableFrame(nsIFrame* aFrame)
return nullptr;
}
nsTableFrame*
nsTableFrame::GetTableFramePassingThrough(nsIFrame* aMustPassThrough,
nsIFrame* aFrame)
{
MOZ_ASSERT(aMustPassThrough == aFrame ||
nsLayoutUtils::IsProperAncestorFrame(aMustPassThrough, aFrame),
"aMustPassThrough should be an ancestor");
// Retrieve the table frame, and ensure that we hit aMustPassThrough on the way.
// If we don't, just return null.
nsTableFrame* tableFrame = nullptr;
for (nsIFrame* ancestor = aFrame; ancestor; ancestor = ancestor->GetParent()) {
if (nsGkAtoms::tableFrame == ancestor->GetType()) {
tableFrame = static_cast<nsTableFrame*>(ancestor);
break;
}
if (ancestor == aMustPassThrough) {
return nullptr;
}
}
MOZ_ASSERT(tableFrame, "Should have a table frame here");
return tableFrame;
}
bool
nsTableFrame::IsAutoHeight()
{

View File

@ -111,6 +111,9 @@ class nsTableFrame : public nsContainerFrame
public:
NS_DECL_FRAMEARENA_HELPERS
static void DestroyPositionedTablePartArray(void* aPropertyValue);
NS_DECLARE_FRAME_PROPERTY(PositionedTablePartArray, DestroyPositionedTablePartArray)
/** nsTableOuterFrame has intimate knowledge of the inner table frame */
friend class nsTableOuterFrame;
@ -146,6 +149,15 @@ public:
static bool PageBreakAfter(nsIFrame* aSourceFrame,
nsIFrame* aNextFrame);
// Register a positioned table part with its nsTableFrame. These objects will
// be visited by FixupPositionedTableParts after reflow is complete. (See that
// function for more explanation.) Should be called during frame construction.
static void RegisterPositionedTablePart(nsIFrame* aFrame);
// Unregister a positioned table part with its nsTableFrame.
static void UnregisterPositionedTablePart(nsIFrame* aFrame,
nsIFrame* aDestructRoot);
nsPoint GetFirstSectionOrigin(const nsHTMLReflowState& aReflowState) const;
/*
@ -179,6 +191,12 @@ public:
/** helper method to find the table parent of any table frame object */
static nsTableFrame* GetTableFrame(nsIFrame* aSourceFrame);
/* Like GetTableFrame, but will return nullptr if we don't pass through
* aMustPassThrough on the way to the table.
*/
static nsTableFrame* GetTableFramePassingThrough(nsIFrame* aMustPassThrough,
nsIFrame* aSourceFrame);
typedef void (* DisplayGenericTablePartTraversal)
(nsDisplayListBuilder* aBuilder, nsFrame* aFrame,
const nsRect& aDirtyRect, const nsDisplayListSet& aLists);
@ -549,6 +567,18 @@ protected:
void AdjustForCollapsingRowsCols(nsHTMLReflowMetrics& aDesiredSize,
nsMargin aBorderPadding);
/** FixupPositionedTableParts is called at the end of table reflow to reflow
* the absolutely positioned descendants of positioned table parts. This is
* necessary because the dimensions of table parts may change after they've
* been reflowed (e.g. in AdjustForCollapsingRowsCols).
*/
void FixupPositionedTableParts(nsPresContext* aPresContext,
const nsHTMLReflowState& aReflowState);
// Clears the list of positioned table parts.
void ClearAllPositionedTableParts();
nsITableLayoutStrategy* LayoutStrategy() const {
return static_cast<nsTableFrame*>(FirstInFlow())->
mTableLayoutStrategy;

View File

@ -150,6 +150,16 @@ nsTableRowFrame::Init(nsIContent* aContent,
}
}
void
nsTableRowFrame::DestroyFrom(nsIFrame* aDestructRoot)
{
if (GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN) {
nsTableFrame::UnregisterPositionedTablePart(this, aDestructRoot);
}
nsContainerFrame::DestroyFrom(aDestructRoot);
}
/* virtual */ void
nsTableRowFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
{

View File

@ -36,6 +36,9 @@ public:
virtual void Init(nsIContent* aContent,
nsIFrame* aParent,
nsIFrame* aPrevInFlow) MOZ_OVERRIDE;
virtual void DestroyFrom(nsIFrame* aDestructRoot) MOZ_OVERRIDE;
/** @see nsIFrame::DidSetStyleContext */
virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext) MOZ_OVERRIDE;

View File

@ -34,6 +34,16 @@ nsTableRowGroupFrame::~nsTableRowGroupFrame()
{
}
void
nsTableRowGroupFrame::DestroyFrom(nsIFrame* aDestructRoot)
{
if (GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN) {
nsTableFrame::UnregisterPositionedTablePart(this, aDestructRoot);
}
nsContainerFrame::DestroyFrom(aDestructRoot);
}
NS_QUERYFRAME_HEAD(nsTableRowGroupFrame)
NS_QUERYFRAME_ENTRY(nsTableRowGroupFrame)
NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)

View File

@ -67,6 +67,9 @@ public:
*/
friend nsIFrame* NS_NewTableRowGroupFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
virtual ~nsTableRowGroupFrame();
virtual void DestroyFrom(nsIFrame* aDestructRoot) MOZ_OVERRIDE;
/** @see nsIFrame::DidSetStyleContext */
virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext) MOZ_OVERRIDE;