Bug 1903141 Part 3 - Store floats list in a frame property rather than in nsBlockFrame::mFloats. r=dholbert

On optimized builds for all desktop platforms, `nsBlockFrame` is 192 bytes, and
`mFloats` is 16 bytes. With this patch, we can reduce the size of `nsBlockFrame`
to 176 bytes, saving 8.33% memory on every `nsBlockFrame` and its derived
subclasses allocated.

On a real webpage like https://en.wikipedia.org/wiki/Firefox, `about:memory`
shows that we allocate 0.36 MB of `nsBlockFrame` consistently. After this patch,
we allocate 0.33 MB, saving approximately 30 KB (0.36 MB * 8.33%) of the memory.

Differential Revision: https://phabricator.services.mozilla.com/D214046
This commit is contained in:
Ting-Yu Lin 2024-06-18 20:13:55 +00:00
Родитель 11ae833dc9
Коммит cd80eb1aef
4 изменённых файлов: 195 добавлений и 92 удалений

Просмотреть файл

@ -536,7 +536,7 @@ bool BlockReflowState::AddFloat(nsLineLayout* aLineLayout, nsIFrame* aFloat,
// Appending is fine, since if a float was pushed to the next // Appending is fine, since if a float was pushed to the next
// page/column, all later floats were also pushed. // page/column, all later floats were also pushed.
mBlock->mFloats.AppendFrame(mBlock, aFloat); mBlock->EnsureFloats()->AppendFrame(mBlock, aFloat);
} }
// Because we are in the middle of reflowing a placeholder frame // Because we are in the middle of reflowing a placeholder frame

Просмотреть файл

@ -417,6 +417,7 @@ static void RecordReflowStatus(bool aChildIsBlock,
NS_DECLARE_FRAME_PROPERTY_WITH_DTOR_NEVER_CALLED(OverflowLinesProperty, NS_DECLARE_FRAME_PROPERTY_WITH_DTOR_NEVER_CALLED(OverflowLinesProperty,
nsBlockFrame::FrameLines) nsBlockFrame::FrameLines)
NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OverflowOutOfFlowsProperty) NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OverflowOutOfFlowsProperty)
NS_DECLARE_FRAME_PROPERTY_FRAMELIST(FloatsProperty)
NS_DECLARE_FRAME_PROPERTY_FRAMELIST(PushedFloatsProperty) NS_DECLARE_FRAME_PROPERTY_FRAMELIST(PushedFloatsProperty)
NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OutsideMarkerProperty) NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OutsideMarkerProperty)
NS_DECLARE_FRAME_PROPERTY_WITHOUT_DTOR(InsideMarkerProperty, nsIFrame) NS_DECLARE_FRAME_PROPERTY_WITHOUT_DTOR(InsideMarkerProperty, nsIFrame)
@ -453,11 +454,14 @@ void nsBlockFrame::AddSizeOfExcludingThisForTree(
void nsBlockFrame::Destroy(DestroyContext& aContext) { void nsBlockFrame::Destroy(DestroyContext& aContext) {
ClearLineCursors(); ClearLineCursors();
DestroyAbsoluteFrames(aContext); DestroyAbsoluteFrames(aContext);
mFloats.DestroyFrames(aContext);
nsPresContext* presContext = PresContext(); nsPresContext* presContext = PresContext();
mozilla::PresShell* presShell = presContext->PresShell(); mozilla::PresShell* presShell = presContext->PresShell();
nsLineBox::DeleteLineList(presContext, mLines, &mFrames, aContext); nsLineBox::DeleteLineList(presContext, mLines, &mFrames, aContext);
if (HasFloats()) {
SafelyDestroyFrameListProp(aContext, presShell, FloatsProperty());
RemoveStateBits(NS_BLOCK_HAS_FLOATS);
}
if (HasPushedFloats()) { if (HasPushedFloats()) {
SafelyDestroyFrameListProp(aContext, presShell, PushedFloatsProperty()); SafelyDestroyFrameListProp(aContext, presShell, PushedFloatsProperty());
RemoveStateBits(NS_BLOCK_HAS_PUSHED_FLOATS); RemoveStateBits(NS_BLOCK_HAS_PUSHED_FLOATS);
@ -685,12 +689,14 @@ const nsFrameList& nsBlockFrame::GetChildList(ChildListID aListID) const {
FrameLines* overflowLines = GetOverflowLines(); FrameLines* overflowLines = GetOverflowLines();
return overflowLines ? overflowLines->mFrames : nsFrameList::EmptyList(); return overflowLines ? overflowLines->mFrames : nsFrameList::EmptyList();
} }
case FrameChildListID::Float:
return mFloats;
case FrameChildListID::OverflowOutOfFlow: { case FrameChildListID::OverflowOutOfFlow: {
const nsFrameList* list = GetOverflowOutOfFlows(); const nsFrameList* list = GetOverflowOutOfFlows();
return list ? *list : nsFrameList::EmptyList(); return list ? *list : nsFrameList::EmptyList();
} }
case FrameChildListID::Float: {
const nsFrameList* list = GetFloats();
return list ? *list : nsFrameList::EmptyList();
}
case FrameChildListID::PushedFloats: { case FrameChildListID::PushedFloats: {
const nsFrameList* list = GetPushedFloats(); const nsFrameList* list = GetPushedFloats();
return list ? *list : nsFrameList::EmptyList(); return list ? *list : nsFrameList::EmptyList();
@ -710,17 +716,16 @@ void nsBlockFrame::GetChildLists(nsTArray<ChildList>* aLists) const {
if (overflowLines) { if (overflowLines) {
overflowLines->mFrames.AppendIfNonempty(aLists, FrameChildListID::Overflow); overflowLines->mFrames.AppendIfNonempty(aLists, FrameChildListID::Overflow);
} }
const nsFrameList* list = GetOverflowOutOfFlows(); if (const nsFrameList* list = GetOverflowOutOfFlows()) {
if (list) {
list->AppendIfNonempty(aLists, FrameChildListID::OverflowOutOfFlow); list->AppendIfNonempty(aLists, FrameChildListID::OverflowOutOfFlow);
} }
mFloats.AppendIfNonempty(aLists, FrameChildListID::Float); if (const nsFrameList* list = GetOutsideMarkerList()) {
list = GetOutsideMarkerList();
if (list) {
list->AppendIfNonempty(aLists, FrameChildListID::Bullet); list->AppendIfNonempty(aLists, FrameChildListID::Bullet);
} }
list = GetPushedFloats(); if (const nsFrameList* list = GetFloats()) {
if (list) { list->AppendIfNonempty(aLists, FrameChildListID::Float);
}
if (const nsFrameList* list = GetPushedFloats()) {
list->AppendIfNonempty(aLists, FrameChildListID::PushedFloats); list->AppendIfNonempty(aLists, FrameChildListID::PushedFloats);
} }
} }
@ -1225,7 +1230,7 @@ bool nsBlockFrame::IsInLineClampContext() const {
} }
bool nsBlockFrame::MaybeHasFloats() const { bool nsBlockFrame::MaybeHasFloats() const {
if (!mFloats.IsEmpty()) { if (HasFloats()) {
return true; return true;
} }
if (HasPushedFloats()) { if (HasPushedFloats()) {
@ -1648,10 +1653,12 @@ void nsBlockFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aMetrics,
UpdateLineContainerSize(&line, containerSize); UpdateLineContainerSize(&line, containerSize);
} }
trialState.mFcBounds.Clear(); trialState.mFcBounds.Clear();
for (nsIFrame* f : mFloats) { if (nsFrameList* floats = GetFloats()) {
for (nsIFrame* f : *floats) {
f->MovePositionBy(physicalDelta); f->MovePositionBy(physicalDelta);
ConsiderChildOverflow(trialState.mFcBounds, f); ConsiderChildOverflow(trialState.mFcBounds, f);
} }
}
nsFrameList* markerList = GetOutsideMarkerList(); nsFrameList* markerList = GetOutsideMarkerList();
if (markerList) { if (markerList) {
for (nsIFrame* f : *markerList) { for (nsIFrame* f : *markerList) {
@ -1777,7 +1784,8 @@ void nsBlockFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aMetrics,
// our floats list, since a first-in-flow might get pushed to a later // our floats list, since a first-in-flow might get pushed to a later
// continuation of its containing block. But it's not permitted // continuation of its containing block. But it's not permitted
// outside that time. // outside that time.
nsLayoutUtils::AssertNoDuplicateContinuations(this, mFloats); nsLayoutUtils::AssertNoDuplicateContinuations(
this, GetChildList(FrameChildListID::Float));
if (gNoisyReflow) { if (gNoisyReflow) {
IndentBy(stdout, gNoiseIndent); IndentBy(stdout, gNoiseIndent);
@ -1831,7 +1839,8 @@ nsReflowStatus nsBlockFrame::TrialReflow(nsPresContext* aPresContext,
// our floats list, since a first-in-flow might get pushed to a later // our floats list, since a first-in-flow might get pushed to a later
// continuation of its containing block. But it's not permitted // continuation of its containing block. But it's not permitted
// outside that time. // outside that time.
nsLayoutUtils::AssertNoDuplicateContinuations(this, mFloats); nsLayoutUtils::AssertNoDuplicateContinuations(
this, GetChildList(FrameChildListID::Float));
#endif #endif
// ALWAYS drain overflow. We never want to leave the previnflow's // ALWAYS drain overflow. We never want to leave the previnflow's
@ -2820,7 +2829,7 @@ static bool LineHasClear(nsLineBox* aLine) {
/** /**
* Reparent a whole list of floats from aOldParent to this block. The * Reparent a whole list of floats from aOldParent to this block. The
* floats might be taken from aOldParent's overflow list. They will be * floats might be taken from aOldParent's overflow list. They will be
* removed from the list. They end up appended to our mFloats list. * removed from the list. They end up appended to our floats list.
*/ */
void nsBlockFrame::ReparentFloats(nsIFrame* aFirstFrame, void nsBlockFrame::ReparentFloats(nsIFrame* aFirstFrame,
nsBlockFrame* aOldParent, nsBlockFrame* aOldParent,
@ -2833,7 +2842,7 @@ void nsBlockFrame::ReparentFloats(nsIFrame* aFirstFrame,
"CollectFloats should've removed that bit"); "CollectFloats should've removed that bit");
ReparentFrame(f, aOldParent, this); ReparentFrame(f, aOldParent, this);
} }
mFloats.AppendFrames(nullptr, std::move(list)); EnsureFloats()->AppendFrames(nullptr, std::move(list));
} }
} }
@ -2909,8 +2918,8 @@ bool nsBlockFrame::ReflowDirtyLines(BlockReflowState& aState) {
bool needToRecoverState = false; bool needToRecoverState = false;
// Float continuations were reflowed in ReflowPushedFloats // Float continuations were reflowed in ReflowPushedFloats
bool reflowedFloat = bool reflowedFloat =
mFloats.NotEmpty() && HasFloats() &&
mFloats.FirstChild()->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT); GetFloats()->FirstChild()->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT);
bool lastLineMovedUp = false; bool lastLineMovedUp = false;
// We save up information about BR-clearance here // We save up information about BR-clearance here
StyleClear inlineFloatClearType = aState.mTrailingClearFromPIF; StyleClear inlineFloatClearType = aState.mTrailingClearFromPIF;
@ -5604,7 +5613,7 @@ void nsBlockFrame::PushLines(BlockReflowState& aState,
bool firstLine = overBegin == LinesBegin(); bool firstLine = overBegin == LinesBegin();
if (overBegin != LinesEnd()) { if (overBegin != LinesEnd()) {
// Remove floats in the lines from mFloats // Remove floats in the lines from floats list.
nsFrameList floats; nsFrameList floats;
CollectFloats(overBegin->mFirstChild, floats, true); CollectFloats(overBegin->mFirstChild, floats, true);
@ -5741,7 +5750,7 @@ bool nsBlockFrame::DrainOverflowLines() {
} }
} }
ReparentFrames(oofs.mList, prevBlock, this); ReparentFrames(oofs.mList, prevBlock, this);
mFloats.InsertFrames(nullptr, nullptr, std::move(oofs.mList)); EnsureFloats()->InsertFrames(nullptr, nullptr, std::move(oofs.mList));
if (!pushedFloats.IsEmpty()) { if (!pushedFloats.IsEmpty()) {
nsFrameList* pf = EnsurePushedFloats(); nsFrameList* pf = EnsurePushedFloats();
pf->InsertFrames(nullptr, nullptr, std::move(pushedFloats)); pf->InsertFrames(nullptr, nullptr, std::move(pushedFloats));
@ -5776,7 +5785,7 @@ bool nsBlockFrame::DrainSelfOverflowList() {
} }
// No need to reparent frames in our own overflow lines/oofs, because they're // No need to reparent frames in our own overflow lines/oofs, because they're
// already ours. But we should put overflow floats back in mFloats. // already ours. But we should put overflow floats back in our floats list.
// (explicit scope to remove the OOF list before VerifyOverflowSituation) // (explicit scope to remove the OOF list before VerifyOverflowSituation)
{ {
nsAutoOOFFrameList oofs(this); nsAutoOOFFrameList oofs(this);
@ -5788,7 +5797,7 @@ bool nsBlockFrame::DrainSelfOverflowList() {
} }
#endif #endif
// The overflow floats go after our regular floats. // The overflow floats go after our regular floats.
mFloats.AppendFrames(nullptr, std::move(oofs).mList); EnsureFloats()->AppendFrames(nullptr, std::move(oofs).mList);
} }
} }
if (!ourOverflowLines->mLines.empty()) { if (!ourOverflowLines->mLines.empty()) {
@ -5824,8 +5833,8 @@ bool nsBlockFrame::DrainSelfOverflowList() {
* also maintains these invariants. * also maintains these invariants.
* *
* DrainSelfPushedFloats moves any pushed floats from this block's own * DrainSelfPushedFloats moves any pushed floats from this block's own
* PushedFloats list back into mFloats. DrainPushedFloats additionally * pushed floats list back into floats list. DrainPushedFloats additionally
* moves frames from its prev-in-flow's PushedFloats list into mFloats. * moves frames from its prev-in-flow's pushed floats list into floats list.
*/ */
void nsBlockFrame::DrainSelfPushedFloats() { void nsBlockFrame::DrainSelfPushedFloats() {
// If we're getting reflowed multiple times without our // If we're getting reflowed multiple times without our
@ -5839,12 +5848,14 @@ void nsBlockFrame::DrainSelfPushedFloats() {
mozilla::PresShell* presShell = PresShell(); mozilla::PresShell* presShell = PresShell();
nsFrameList* ourPushedFloats = GetPushedFloats(); nsFrameList* ourPushedFloats = GetPushedFloats();
if (ourPushedFloats) { if (ourPushedFloats) {
nsFrameList* floats = GetFloats();
// When we pull back floats, we want to put them with the pushed // When we pull back floats, we want to put them with the pushed
// floats, which must live at the start of our float list, but we // floats, which must live at the start of our float list, but we
// want them at the end of those pushed floats. // want them at the end of those pushed floats.
// FIXME: This isn't quite right! What if they're all pushed floats? // FIXME: This isn't quite right! What if they're all pushed floats?
nsIFrame* insertionPrevSibling = nullptr; /* beginning of list */ nsIFrame* insertionPrevSibling = nullptr; /* beginning of list */
for (nsIFrame* f = mFloats.FirstChild(); for (nsIFrame* f = floats ? floats->FirstChild() : nullptr;
f && f->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT); f && f->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT);
f = f->GetNextSibling()) { f = f->GetNextSibling()) {
insertionPrevSibling = f; insertionPrevSibling = f;
@ -5863,7 +5874,10 @@ void nsBlockFrame::DrainSelfPushedFloats() {
// list and put it in our floats list, before any of our // list and put it in our floats list, before any of our
// floats, but after other pushed floats. // floats, but after other pushed floats.
ourPushedFloats->RemoveFrame(f); ourPushedFloats->RemoveFrame(f);
mFloats.InsertFrame(nullptr, insertionPrevSibling, f); if (!floats) {
floats = EnsureFloats();
}
floats->InsertFrame(nullptr, insertionPrevSibling, f);
} }
f = prevSibling; f = prevSibling;
@ -5884,7 +5898,7 @@ void nsBlockFrame::DrainPushedFloats() {
if (prevBlock) { if (prevBlock) {
AutoFrameListPtr list(PresContext(), prevBlock->StealPushedFloats()); AutoFrameListPtr list(PresContext(), prevBlock->StealPushedFloats());
if (list && list->NotEmpty()) { if (list && list->NotEmpty()) {
mFloats.InsertFrames(this, nullptr, std::move(*list)); EnsureFloats()->InsertFrames(this, nullptr, std::move(*list));
} }
} }
} }
@ -6004,13 +6018,63 @@ nsFrameList* nsBlockFrame::GetOutsideMarkerList() const {
return list; return list;
} }
bool nsBlockFrame::HasFloats() const {
const bool isStateBitSet = HasAnyStateBits(NS_BLOCK_HAS_FLOATS);
MOZ_ASSERT(
isStateBitSet == HasProperty(FloatsProperty()),
"State bit should accurately reflect presence/absence of the property!");
return isStateBitSet;
}
nsFrameList* nsBlockFrame::GetFloats() const {
if (!HasFloats()) {
return nullptr;
}
nsFrameList* list = GetProperty(FloatsProperty());
MOZ_ASSERT(list, "List should always be valid when the property is set!");
MOZ_ASSERT(list->NotEmpty(),
"Someone forgot to delete the list when it is empty!");
return list;
}
nsFrameList* nsBlockFrame::EnsureFloats() {
nsFrameList* list = GetFloats();
if (list) {
return list;
}
list = new (PresShell()) nsFrameList;
SetProperty(FloatsProperty(), list);
AddStateBits(NS_BLOCK_HAS_FLOATS);
return list;
}
nsFrameList* nsBlockFrame::StealFloats() {
if (!HasFloats()) {
return nullptr;
}
nsFrameList* list = TakeProperty(FloatsProperty());
RemoveStateBits(NS_BLOCK_HAS_FLOATS);
MOZ_ASSERT(list, "List should always be valid when the property is set!");
return list;
}
bool nsBlockFrame::HasPushedFloats() const {
const bool isStateBitSet = HasAnyStateBits(NS_BLOCK_HAS_PUSHED_FLOATS);
MOZ_ASSERT(
isStateBitSet == HasProperty(PushedFloatsProperty()),
"State bit should accurately reflect presence/absence of the property!");
return isStateBitSet;
}
nsFrameList* nsBlockFrame::GetPushedFloats() const { nsFrameList* nsBlockFrame::GetPushedFloats() const {
if (!HasPushedFloats()) { if (!HasPushedFloats()) {
return nullptr; return nullptr;
} }
nsFrameList* result = GetProperty(PushedFloatsProperty()); nsFrameList* list = GetProperty(PushedFloatsProperty());
NS_ASSERTION(result, "value should always be non-empty when state set"); MOZ_ASSERT(list, "List should always be valid when the property is set!");
return result; MOZ_ASSERT(list->NotEmpty(),
"Someone forgot to delete the list when it is empty!");
return list;
} }
nsFrameList* nsBlockFrame::EnsurePushedFloats() { nsFrameList* nsBlockFrame::EnsurePushedFloats() {
@ -6030,10 +6094,10 @@ nsFrameList* nsBlockFrame::StealPushedFloats() {
if (!HasPushedFloats()) { if (!HasPushedFloats()) {
return nullptr; return nullptr;
} }
nsFrameList* result = TakeProperty(PushedFloatsProperty()); nsFrameList* list = TakeProperty(PushedFloatsProperty());
RemoveStateBits(NS_BLOCK_HAS_PUSHED_FLOATS); RemoveStateBits(NS_BLOCK_HAS_PUSHED_FLOATS);
NS_ASSERTION(result, "value should always be non-empty when state set"); MOZ_ASSERT(list, "List should always be valid when the property is set!");
return result; return list;
} }
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
@ -6045,8 +6109,8 @@ void nsBlockFrame::AppendFrames(ChildListID aListID, nsFrameList&& aFrameList) {
} }
if (aListID != FrameChildListID::Principal) { if (aListID != FrameChildListID::Principal) {
if (FrameChildListID::Float == aListID) { if (FrameChildListID::Float == aListID) {
DrainSelfPushedFloats(); // ensure the last frame is in mFloats DrainSelfPushedFloats(); // ensure the last frame is in floats list.
mFloats.AppendFrames(nullptr, std::move(aFrameList)); EnsureFloats()->AppendFrames(nullptr, std::move(aFrameList));
return; return;
} }
MOZ_ASSERT(FrameChildListID::NoReflowPrincipal == aListID, MOZ_ASSERT(FrameChildListID::NoReflowPrincipal == aListID,
@ -6096,8 +6160,8 @@ void nsBlockFrame::InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame,
if (aListID != FrameChildListID::Principal) { if (aListID != FrameChildListID::Principal) {
if (FrameChildListID::Float == aListID) { if (FrameChildListID::Float == aListID) {
DrainSelfPushedFloats(); // ensure aPrevFrame is in mFloats DrainSelfPushedFloats(); // ensure aPrevFrame is in floats list.
mFloats.InsertFrames(this, aPrevFrame, std::move(aFrameList)); EnsureFloats()->InsertFrames(this, aPrevFrame, std::move(aFrameList));
return; return;
} }
MOZ_ASSERT(FrameChildListID::NoReflowPrincipal == aListID, MOZ_ASSERT(FrameChildListID::NoReflowPrincipal == aListID,
@ -6373,19 +6437,22 @@ void nsBlockFrame::RemoveFloatFromFloatCache(nsIFrame* aFloat) {
} }
void nsBlockFrame::RemoveFloat(nsIFrame* aFloat) { void nsBlockFrame::RemoveFloat(nsIFrame* aFloat) {
#ifdef DEBUG MOZ_ASSERT(aFloat);
// Floats live in mFloats, or in the PushedFloat or OverflowOutOfFlows
// frame list properties.
if (!mFloats.ContainsFrame(aFloat)) {
MOZ_ASSERT(
(GetOverflowOutOfFlows() &&
GetOverflowOutOfFlows()->ContainsFrame(aFloat)) ||
(HasPushedFloats() && GetPushedFloats()->ContainsFrame(aFloat)),
"aFloat is not our child or on an unexpected frame list");
}
#endif
if (mFloats.StartRemoveFrame(aFloat)) { // Floats live in floats list, pushed floats list, or overflow out-of-flow
// list.
MOZ_ASSERT(
GetChildList(FrameChildListID::Float).ContainsFrame(aFloat) ||
GetChildList(FrameChildListID::PushedFloats).ContainsFrame(aFloat) ||
GetChildList(FrameChildListID::OverflowOutOfFlow)
.ContainsFrame(aFloat),
"aFloat is not our child or on an unexpected frame list");
nsFrameList* floats = GetFloats();
if (floats && floats->StartRemoveFrame(aFloat)) {
if (floats->IsEmpty()) {
StealFloats()->Delete(PresShell());
}
return; return;
} }
@ -7250,7 +7317,8 @@ void nsBlockFrame::ReflowPushedFloats(BlockReflowState& aState,
OverflowAreas& aOverflowAreas) { OverflowAreas& aOverflowAreas) {
// Pushed floats live at the start of our float list; see comment // Pushed floats live at the start of our float list; see comment
// above nsBlockFrame::DrainPushedFloats. // above nsBlockFrame::DrainPushedFloats.
nsIFrame* f = mFloats.FirstChild(); nsFrameList* floats = GetFloats();
nsIFrame* f = floats ? floats->FirstChild() : nullptr;
nsIFrame* prev = nullptr; nsIFrame* prev = nullptr;
while (f && f->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT)) { while (f && f->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT)) {
MOZ_ASSERT(prev == f->GetPrevSibling()); MOZ_ASSERT(prev == f->GetPrevSibling());
@ -7288,9 +7356,15 @@ void nsBlockFrame::ReflowPushedFloats(BlockReflowState& aState,
// third and later. // third and later.
nsIFrame* prevContinuation = f->GetPrevContinuation(); nsIFrame* prevContinuation = f->GetPrevContinuation();
if (prevContinuation && prevContinuation->GetParent() == f->GetParent()) { if (prevContinuation && prevContinuation->GetParent() == f->GetParent()) {
mFloats.RemoveFrame(f); floats->RemoveFrame(f);
aState.AppendPushedFloatChain(f); aState.AppendPushedFloatChain(f);
f = !prev ? mFloats.FirstChild() : prev->GetNextSibling(); if (floats->IsEmpty()) {
StealFloats()->Delete(PresShell());
floats = nullptr;
f = prev = nullptr;
break;
}
f = !prev ? floats->FirstChild() : prev->GetNextSibling();
continue; continue;
} }
@ -7304,7 +7378,16 @@ void nsBlockFrame::ReflowPushedFloats(BlockReflowState& aState,
ConsiderChildOverflow(aOverflowAreas, f); ConsiderChildOverflow(aOverflowAreas, f);
} }
nsIFrame* next = !prev ? mFloats.FirstChild() : prev->GetNextSibling(); // If f is the only child in the floats list, pushing it to the pushed
// floats list in FlowAndPlaceFloat() can result in the floats list being
// deleted. Get the floats list again.
floats = GetFloats();
if (!floats) {
f = prev = nullptr;
break;
}
nsIFrame* next = !prev ? floats->FirstChild() : prev->GetNextSibling();
if (next == f) { if (next == f) {
// We didn't push |f| so its next-sibling is next. // We didn't push |f| so its next-sibling is next.
next = f->GetNextSibling(); next = f->GetNextSibling();
@ -7329,7 +7412,8 @@ void nsBlockFrame::RecoverFloats(nsFloatManager& aFloatManager, WritingMode aWM,
// Recover our own floats // Recover our own floats
nsIFrame* stop = nullptr; // Stop before we reach pushed floats that nsIFrame* stop = nullptr; // Stop before we reach pushed floats that
// belong to our next-in-flow // belong to our next-in-flow
for (nsIFrame* f = mFloats.FirstChild(); f && f != stop; const nsFrameList* floats = GetFloats();
for (nsIFrame* f = floats ? floats->FirstChild() : nullptr; f && f != stop;
f = f->GetNextSibling()) { f = f->GetNextSibling()) {
LogicalRect region = nsFloatManager::GetRegionFor(aWM, f, aContainerSize); LogicalRect region = nsFloatManager::GetRegionFor(aWM, f, aContainerSize);
aFloatManager.AddFloat(f, region, aWM, aContainerSize); aFloatManager.AddFloat(f, region, aWM, aContainerSize);
@ -7379,22 +7463,21 @@ void nsBlockFrame::RecoverFloatsFor(nsIFrame* aFrame,
} }
bool nsBlockFrame::HasPushedFloatsFromPrevContinuation() const { bool nsBlockFrame::HasPushedFloatsFromPrevContinuation() const {
if (!mFloats.IsEmpty()) { if (const nsFrameList* floats = GetFloats()) {
// If we have pushed floats, then they should be at the beginning of our // If we have pushed floats, then they should be at the beginning of our
// float list. // float list.
if (mFloats.FirstChild()->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT)) { if (floats->FirstChild()->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT)) {
return true; return true;
} }
}
#ifdef DEBUG #ifdef DEBUG
// Double-check the above assertion that pushed floats should be at the // Double-check the above assertion that pushed floats should be at the
// beginning of our floats list. // beginning of our floats list.
for (nsIFrame* f : mFloats) { for (nsIFrame* f : *floats) {
NS_ASSERTION(!f->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT), NS_ASSERTION(!f->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT),
"pushed floats must be at the beginning of the float list"); "pushed floats must be at the beginning of the float list");
} }
#endif #endif
}
// We may have a pending push of pushed floats, too. // We may have a pending push of pushed floats, too.
return HasPushedFloats(); return HasPushedFloats();
@ -7527,14 +7610,15 @@ void nsBlockFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
if (GetPrevInFlow()) { if (GetPrevInFlow()) {
DisplayOverflowContainers(aBuilder, aLists); DisplayOverflowContainers(aBuilder, aLists);
for (nsIFrame* f : mFloats) { for (nsIFrame* f : GetChildList(FrameChildListID::Float)) {
if (f->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT)) { if (f->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT)) {
BuildDisplayListForChild(aBuilder, f, aLists); BuildDisplayListForChild(aBuilder, f, aLists);
} }
} }
} }
aBuilder->MarkFramesForDisplayList(this, mFloats); aBuilder->MarkFramesForDisplayList(this,
GetChildList(FrameChildListID::Float));
if (HasOutsideMarker()) { if (HasOutsideMarker()) {
// Display outside ::marker manually. // Display outside ::marker manually.
@ -7903,7 +7987,8 @@ void nsBlockFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
void nsBlockFrame::SetInitialChildList(ChildListID aListID, void nsBlockFrame::SetInitialChildList(ChildListID aListID,
nsFrameList&& aChildList) { nsFrameList&& aChildList) {
if (FrameChildListID::Float == aListID) { if (FrameChildListID::Float == aListID) {
mFloats = std::move(aChildList); nsFrameList* floats = EnsureFloats();
*floats = std::move(aChildList);
} else if (FrameChildListID::Principal == aListID) { } else if (FrameChildListID::Principal == aListID) {
#ifdef DEBUG #ifdef DEBUG
// The only times a block that is an anonymous box is allowed to have a // The only times a block that is an anonymous box is allowed to have a
@ -8085,7 +8170,7 @@ void nsBlockFrame::CheckFloats(BlockReflowState& aState) {
bool equal = true; bool equal = true;
bool hasHiddenFloats = false; bool hasHiddenFloats = false;
uint32_t i = 0; uint32_t i = 0;
for (nsIFrame* f : mFloats) { for (nsIFrame* f : GetChildList(FrameChildListID::Float)) {
if (f->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT)) { if (f->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT)) {
continue; continue;
} }
@ -8564,23 +8649,26 @@ void nsBlockFrame::VerifyLines(bool aFinalCheckOK) {
} }
void nsBlockFrame::VerifyOverflowSituation() { void nsBlockFrame::VerifyOverflowSituation() {
// Overflow out-of-flows must not have a next-in-flow in mFloats or mFrames. // Overflow out-of-flows must not have a next-in-flow in floats list or
// mFrames.
nsFrameList* oofs = GetOverflowOutOfFlows(); nsFrameList* oofs = GetOverflowOutOfFlows();
if (oofs) { if (oofs) {
for (nsIFrame* f : *oofs) { for (nsIFrame* f : *oofs) {
nsIFrame* nif = f->GetNextInFlow(); nsIFrame* nif = f->GetNextInFlow();
MOZ_ASSERT(!nif || MOZ_ASSERT(!nif ||
(!mFloats.ContainsFrame(nif) && !mFrames.ContainsFrame(nif))); (!GetChildList(FrameChildListID::Float).ContainsFrame(nif) &&
!mFrames.ContainsFrame(nif)));
} }
} }
// Pushed floats must not have a next-in-flow in mFloats or mFrames. // Pushed floats must not have a next-in-flow in floats list or mFrames.
oofs = GetPushedFloats(); oofs = GetPushedFloats();
if (oofs) { if (oofs) {
for (nsIFrame* f : *oofs) { for (nsIFrame* f : *oofs) {
nsIFrame* nif = f->GetNextInFlow(); nsIFrame* nif = f->GetNextInFlow();
MOZ_ASSERT(!nif || MOZ_ASSERT(!nif ||
(!mFloats.ContainsFrame(nif) && !mFrames.ContainsFrame(nif))); (!GetChildList(FrameChildListID::Float).ContainsFrame(nif) &&
!mFrames.ContainsFrame(nif)));
} }
} }

Просмотреть файл

@ -642,7 +642,7 @@ class nsBlockFrame : public nsContainerFrame {
/** /**
* Determine if we have any pushed floats from a previous continuation. * Determine if we have any pushed floats from a previous continuation.
* *
* @returns true, if any of the floats at the beginning of our mFloats list * @returns true, if any of the floats at the beginning of our floats list
* have the NS_FRAME_IS_PUSHED_FLOAT bit set; false otherwise. * have the NS_FRAME_IS_PUSHED_FLOAT bit set; false otherwise.
*/ */
bool HasPushedFloatsFromPrevContinuation() const; bool HasPushedFloatsFromPrevContinuation() const;
@ -965,14 +965,30 @@ class nsBlockFrame : public nsContainerFrame {
// Return the outside ::marker frame list frame property. // Return the outside ::marker frame list frame property.
nsFrameList* GetOutsideMarkerList() const; nsFrameList* GetOutsideMarkerList() const;
// Return true if this frame has pushed floats. // Return true if this frame has floats.
bool HasPushedFloats() const { bool HasFloats() const;
return HasAnyStateBits(NS_BLOCK_HAS_PUSHED_FLOATS);
}
// Get the pushed floats list, which is used for *temporary* storage // Get the floats list, or nullptr if there isn't one.
// of floats during reflow, between when we decide they don't fit in nsFrameList* GetFloats() const;
// this block until our next continuation takes them.
// Get the floats list, or if there is not currently one, make a new empty
// one.
nsFrameList* EnsureFloats() MOZ_NONNULL_RETURN;
// Get the float list and remove the property from this frame.
//
// The caller is responsible for deleting the returned list and managing the
// ownership of all frames in the list.
[[nodiscard]] nsFrameList* StealFloats();
// Return true if this frame has pushed floats.
bool HasPushedFloats() const;
// Get the pushed floats list, or nullptr if there isn't one.
//
// The pushed floats list is used for *temporary* storage of floats during
// reflow, between when we decide they don't fit in this block until our next
// continuation takes them.
nsFrameList* GetPushedFloats() const; nsFrameList* GetPushedFloats() const;
// Get the pushed floats list, or if there is not currently one, // Get the pushed floats list, or if there is not currently one,
@ -996,10 +1012,6 @@ class nsBlockFrame : public nsContainerFrame {
nsLineList mLines; nsLineList mLines;
// List of all floats in this block
// XXXmats blocks rarely have floats, make it a frame property
nsFrameList mFloats;
friend class mozilla::BlockReflowState; friend class mozilla::BlockReflowState;
friend class nsBlockInFlowLineIterator; friend class nsBlockInFlowLineIterator;

Просмотреть файл

@ -521,10 +521,8 @@ FRAME_STATE_BIT(Text, 63, TEXT_IN_OFFSET_CACHE)
FRAME_STATE_GROUP(Block, nsBlockFrame) FRAME_STATE_GROUP(Block, nsBlockFrame)
// Something in the block has changed that requires Bidi resolution to be // NS_BLOCK_HAS_FLOATS indicates that the block has a float list.
// performed on the block. This flag must be either set on all blocks in a FRAME_STATE_BIT(Block, 20, NS_BLOCK_HAS_FLOATS)
// continuation chain or none of them.
FRAME_STATE_BIT(Block, 20, NS_BLOCK_NEEDS_BIDI_RESOLUTION)
// NS_BLOCK_HAS_PUSHED_FLOATS indicates that the block has a pushed float list. // NS_BLOCK_HAS_PUSHED_FLOATS indicates that the block has a pushed float list.
FRAME_STATE_BIT(Block, 21, NS_BLOCK_HAS_PUSHED_FLOATS) FRAME_STATE_BIT(Block, 21, NS_BLOCK_HAS_PUSHED_FLOATS)
@ -565,7 +563,12 @@ FRAME_STATE_BIT(Block, 27, NS_BLOCK_HAS_FIRST_LETTER_STYLE)
FRAME_STATE_BIT(Block, 28, NS_BLOCK_HAS_OUTSIDE_MARKER) FRAME_STATE_BIT(Block, 28, NS_BLOCK_HAS_OUTSIDE_MARKER)
FRAME_STATE_BIT(Block, 29, NS_BLOCK_HAS_INSIDE_MARKER) FRAME_STATE_BIT(Block, 29, NS_BLOCK_HAS_INSIDE_MARKER)
// bits 30 and 31 free. // Something in the block has changed that requires Bidi resolution to be
// performed on the block. This flag must be either set on all blocks in a
// continuation chain or none of them.
FRAME_STATE_BIT(Block, 30, NS_BLOCK_NEEDS_BIDI_RESOLUTION)
// bits 31 free.
// NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS indicates that exactly one line in this // NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS indicates that exactly one line in this
// block has the LineClampEllipsis flag set, and that such a line must be found // block has the LineClampEllipsis flag set, and that such a line must be found