mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-30 08:12:05 +00:00
Bug 1052924 - Rewrite reflow code. r=dbaron
Important changes: * Avoid using GetPrefISize on the ruby texts in nsRubyBaseContainerFrame::Reflow, since the size it produces might not match the size produced by Reflow. The old code calls that on all the ruby texts to determine how big they are, then reflows all the ruby bases, and then reflows all the ruby texts. The new code instead processes one pair at a time, and for each pair reflows the ruby texts and then the ruby base. * Change the base class of nsRubyTextContainerFrame from nsBlockFrame to nsContainerFrame, and stop constructing an nsBlockReflowState for its reflow. * Move the code for reflowing ruby texts from nsRubyTextContainerFrame and to nsRubyBaseContainerFrame. * Fix the regression that ruby text containers contain span are not reflowed properly. It is the regression introduced in patch 0. Known regression: * This patch drops centering ruby base and annotation in pairs. This should be fixed in bug 1055676 (ruby-align).
This commit is contained in:
parent
e254b3d884
commit
c25136319a
@ -220,96 +220,138 @@ nsRubyBaseContainerFrame::Reflow(nsPresContext* aPresContext,
|
||||
}
|
||||
|
||||
aStatus = NS_FRAME_COMPLETE;
|
||||
nscoord isize = 0;
|
||||
nscoord leftoverSpace = 0;
|
||||
nscoord spaceApart = 0;
|
||||
WritingMode lineWM = aReflowState.mLineLayout->GetWritingMode();
|
||||
WritingMode frameWM = aReflowState.GetWritingMode();
|
||||
LogicalMargin borderPadding =
|
||||
aReflowState.ComputedLogicalBorderPadding();
|
||||
nscoord baseStart = 0;
|
||||
|
||||
LogicalSize availSize(lineWM, aReflowState.AvailableWidth(),
|
||||
aReflowState.AvailableHeight());
|
||||
|
||||
const uint32_t rtcCount = mTextContainers.Length();
|
||||
const uint32_t spanCount = mSpanContainers.Length();
|
||||
const uint32_t totalCount = rtcCount + spanCount;
|
||||
// We have a reflow state and a line layout for each RTC.
|
||||
// They are conceptually the state of the RTCs, but we don't actually
|
||||
// reflow those RTCs in this code. These two arrays are holders of
|
||||
// the reflow states and line layouts.
|
||||
nsAutoTArray<UniquePtr<nsHTMLReflowState>, RTC_ARRAY_SIZE> reflowStates;
|
||||
nsAutoTArray<UniquePtr<nsLineLayout>, RTC_ARRAY_SIZE> lineLayouts;
|
||||
reflowStates.SetCapacity(totalCount);
|
||||
lineLayouts.SetCapacity(totalCount);
|
||||
|
||||
nsAutoTArray<nsHTMLReflowState*, RTC_ARRAY_SIZE> rtcReflowStates;
|
||||
nsAutoTArray<nsHTMLReflowState*, RTC_ARRAY_SIZE> spanReflowStates;
|
||||
rtcReflowStates.SetCapacity(rtcCount);
|
||||
spanReflowStates.SetCapacity(spanCount);
|
||||
|
||||
// Begin the line layout for each ruby text container in advance.
|
||||
for (uint32_t i = 0; i < rtcCount; i++) {
|
||||
nsRubyTextContainerFrame* rtcFrame = mTextContainers.ElementAt(i);
|
||||
nsHTMLReflowState rtcReflowState(aPresContext,
|
||||
*aReflowState.parentReflowState,
|
||||
rtcFrame, availSize);
|
||||
rtcReflowState.mLineLayout = aReflowState.mLineLayout;
|
||||
// FIXME: Avoid using/needing the rtcReflowState argument
|
||||
rtcFrame->BeginRTCLineLayout(aPresContext, rtcReflowState);
|
||||
for (uint32_t i = 0; i < totalCount; i++) {
|
||||
nsIFrame* textContainer;
|
||||
nsTArray<nsHTMLReflowState*>* reflowStateArray;
|
||||
if (i < rtcCount) {
|
||||
textContainer = mTextContainers[i];
|
||||
reflowStateArray = &rtcReflowStates;
|
||||
} else {
|
||||
textContainer = mSpanContainers[i - rtcCount];
|
||||
reflowStateArray = &spanReflowStates;
|
||||
}
|
||||
nsHTMLReflowState* reflowState = new nsHTMLReflowState(
|
||||
aPresContext, *aReflowState.parentReflowState, textContainer, availSize);
|
||||
reflowStates.AppendElement(reflowState);
|
||||
reflowStateArray->AppendElement(reflowState);
|
||||
nsLineLayout* lineLayout = new nsLineLayout(
|
||||
aPresContext, reflowState->mFloatManager, reflowState, nullptr);
|
||||
lineLayouts.AppendElement(lineLayout);
|
||||
|
||||
// Line number is useless for ruby text
|
||||
// XXX nullptr here may cause problem, see comments for
|
||||
// nsLineLayout::mBlockRS and nsLineLayout::AddFloat
|
||||
lineLayout->Init(nullptr, reflowState->CalcLineHeight(), -1);
|
||||
reflowState->mLineLayout = lineLayout;
|
||||
|
||||
LogicalMargin borderPadding = reflowState->ComputedLogicalBorderPadding();
|
||||
nscoord containerWidth =
|
||||
reflowState->ComputedWidth() + borderPadding.LeftRight(lineWM);
|
||||
|
||||
lineLayout->BeginLineReflow(borderPadding.IStart(lineWM),
|
||||
borderPadding.BStart(lineWM),
|
||||
reflowState->ComputedISize(),
|
||||
NS_UNCONSTRAINEDSIZE,
|
||||
false, false, lineWM, containerWidth);
|
||||
}
|
||||
|
||||
nscoord istart = aReflowState.mLineLayout->GetCurrentICoord();
|
||||
nscoord icoord = istart;
|
||||
|
||||
// Reflow non-span annotations and bases
|
||||
for (PairEnumerator e(this, mTextContainers); !e.AtEnd(); e.Next()) {
|
||||
// Determine if we need more spacing between bases in the inline direction
|
||||
// depending on the inline size of the corresponding annotations
|
||||
// FIXME: The use of GetPrefISize here and below is easier but not ideal. It
|
||||
// would be better to use metrics from reflow.
|
||||
nscoord textWidth = 0;
|
||||
nscoord pairISize = 0;
|
||||
|
||||
for (uint32_t i = 0; i < rtcCount; i++) {
|
||||
nsRubyTextFrame* rtFrame = do_QueryFrame(e.GetTextFrame(i));
|
||||
if (rtFrame) {
|
||||
int newWidth = rtFrame->GetPrefISize(aReflowState.rendContext);
|
||||
if (newWidth > textWidth) {
|
||||
textWidth = newWidth;
|
||||
}
|
||||
nsReflowStatus reflowStatus;
|
||||
nsHTMLReflowMetrics metrics(*rtcReflowStates[i]);
|
||||
|
||||
bool pushedFrame;
|
||||
rtcReflowStates[i]->mLineLayout->ReflowFrame(rtFrame, reflowStatus,
|
||||
&metrics, pushedFrame);
|
||||
NS_ASSERTION(!pushedFrame, "Ruby line breaking is not yet implemented");
|
||||
pairISize = std::max(pairISize, metrics.ISize(lineWM));
|
||||
}
|
||||
}
|
||||
|
||||
nsIFrame* rbFrame = e.GetBaseFrame();
|
||||
NS_ASSERTION(!rbFrame || rbFrame->GetType() == nsGkAtoms::rubyBaseFrame,
|
||||
"Unrecognized child type for ruby base container");
|
||||
if (rbFrame) {
|
||||
nsReflowStatus frameReflowStatus;
|
||||
nsHTMLReflowMetrics metrics(aReflowState, aDesiredSize.mFlags);
|
||||
nscoord prefWidth = rbFrame->GetPrefISize(aReflowState.rendContext);
|
||||
|
||||
if (textWidth > prefWidth) {
|
||||
spaceApart = std::max((textWidth - prefWidth) / 2, spaceApart);
|
||||
leftoverSpace = spaceApart;
|
||||
} else {
|
||||
spaceApart = leftoverSpace;
|
||||
leftoverSpace = 0;
|
||||
}
|
||||
if (spaceApart > 0) {
|
||||
aReflowState.mLineLayout->AdvanceICoord(spaceApart);
|
||||
}
|
||||
baseStart = aReflowState.mLineLayout->GetCurrentICoord();
|
||||
MOZ_ASSERT(rbFrame->GetType() == nsGkAtoms::rubyBaseFrame);
|
||||
nsReflowStatus reflowStatus;
|
||||
nsHTMLReflowMetrics metrics(aReflowState);
|
||||
|
||||
bool pushedFrame;
|
||||
aReflowState.mLineLayout->ReflowFrame(rbFrame, frameReflowStatus,
|
||||
aReflowState.mLineLayout->ReflowFrame(rbFrame, reflowStatus,
|
||||
&metrics, pushedFrame);
|
||||
NS_ASSERTION(!pushedFrame, "Ruby line breaking is not yet implemented");
|
||||
|
||||
isize += metrics.ISize(lineWM);
|
||||
rbFrame->SetSize(LogicalSize(lineWM, metrics.ISize(lineWM),
|
||||
metrics.BSize(lineWM)));
|
||||
FinishReflowChild(rbFrame, aPresContext, metrics, &aReflowState, 0, 0,
|
||||
NS_FRAME_NO_MOVE_FRAME | NS_FRAME_NO_MOVE_VIEW);
|
||||
pairISize = std::max(pairISize, metrics.ISize(lineWM));
|
||||
}
|
||||
|
||||
// Now reflow the ruby text boxes that correspond to this ruby base box.
|
||||
// Align all the line layout to the new coordinate.
|
||||
icoord += pairISize;
|
||||
aReflowState.mLineLayout->AdvanceICoord(
|
||||
icoord - aReflowState.mLineLayout->GetCurrentICoord());
|
||||
for (uint32_t i = 0; i < rtcCount; i++) {
|
||||
nsRubyTextFrame* rtFrame = do_QueryFrame(e.GetTextFrame(i));
|
||||
nsRubyTextContainerFrame* rtcFrame = mTextContainers[i];
|
||||
if (rtFrame) {
|
||||
nsHTMLReflowMetrics rtcMetrics(*aReflowState.parentReflowState,
|
||||
aDesiredSize.mFlags);
|
||||
nsHTMLReflowState rtcReflowState(aPresContext,
|
||||
*aReflowState.parentReflowState,
|
||||
rtcFrame, availSize);
|
||||
rtcReflowState.mLineLayout = rtcFrame->GetLineLayout();
|
||||
rtcFrame->ReflowRubyTextFrame(rtFrame, rbFrame, baseStart,
|
||||
aPresContext, rtcMetrics,
|
||||
rtcReflowState);
|
||||
}
|
||||
nsLineLayout* lineLayout = rtcReflowStates[i]->mLineLayout;
|
||||
lineLayout->AdvanceICoord(icoord - lineLayout->GetCurrentICoord());
|
||||
}
|
||||
}
|
||||
|
||||
// Reflow spans
|
||||
nscoord spanISize = 0;
|
||||
for (uint32_t i = 0; i < spanCount; i++) {
|
||||
nsRubyTextContainerFrame* container = mSpanContainers[i];
|
||||
nsIFrame* rtFrame = container->GetFirstPrincipalChild();
|
||||
nsReflowStatus reflowStatus;
|
||||
nsHTMLReflowMetrics metrics(*spanReflowStates[i]);
|
||||
bool pushedFrame;
|
||||
spanReflowStates[i]->mLineLayout->ReflowFrame(rtFrame, reflowStatus,
|
||||
&metrics, pushedFrame);
|
||||
NS_ASSERTION(!pushedFrame, "Ruby line breaking is not yet implemented");
|
||||
spanISize = std::max(spanISize, metrics.ISize(lineWM));
|
||||
}
|
||||
|
||||
nscoord isize = icoord - istart;
|
||||
if (isize < spanISize) {
|
||||
aReflowState.mLineLayout->AdvanceICoord(spanISize - isize);
|
||||
isize = spanISize;
|
||||
}
|
||||
for (uint32_t i = 0; i < totalCount; i++) {
|
||||
// It happens before the ruby text container is reflowed, and that
|
||||
// when it is reflowed, it will just use this size.
|
||||
nsRubyTextContainerFrame* textContainer = i < rtcCount ?
|
||||
mTextContainers[i] : mSpanContainers[i - rtcCount];
|
||||
textContainer->SetISize(isize);
|
||||
lineLayouts[i]->EndLineReflow();
|
||||
}
|
||||
|
||||
LogicalMargin borderPadding = aReflowState.ComputedLogicalBorderPadding();
|
||||
aDesiredSize.ISize(lineWM) = isize;
|
||||
nsLayoutUtils::SetBSizeFromFontMetrics(this, aDesiredSize, aReflowState,
|
||||
borderPadding, lineWM, frameWM);
|
||||
|
@ -9,6 +9,7 @@
|
||||
#ifndef nsRubyBaseContainerFrame_h___
|
||||
#define nsRubyBaseContainerFrame_h___
|
||||
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "nsContainerFrame.h"
|
||||
#include "nsRubyTextContainerFrame.h"
|
||||
#include "nsRubyBaseFrame.h"
|
||||
|
@ -12,6 +12,8 @@
|
||||
#include "WritingModes.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
// Frame class boilerplate
|
||||
@ -19,7 +21,7 @@
|
||||
|
||||
NS_QUERYFRAME_HEAD(nsRubyTextContainerFrame)
|
||||
NS_QUERYFRAME_ENTRY(nsRubyTextContainerFrame)
|
||||
NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame)
|
||||
NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
|
||||
|
||||
NS_IMPL_FRAMEARENA_HELPERS(nsRubyTextContainerFrame)
|
||||
|
||||
@ -50,97 +52,6 @@ nsRubyTextContainerFrame::GetFrameName(nsAString& aResult) const
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
nsRubyTextContainerFrame::BeginRTCLineLayout(nsPresContext* aPresContext,
|
||||
const nsHTMLReflowState& aReflowState)
|
||||
{
|
||||
// Construct block reflow state and line layout
|
||||
nscoord consumedBSize = GetConsumedBSize();
|
||||
|
||||
ClearLineCursor();
|
||||
|
||||
mISize = 0;
|
||||
|
||||
nsBlockReflowState state(aReflowState, aPresContext, this, true, true,
|
||||
false, consumedBSize);
|
||||
|
||||
NS_ASSERTION(!mLines.empty(),
|
||||
"There should be at least one line in the ruby text container");
|
||||
line_iterator firstLine = begin_lines();
|
||||
mLineLayout = mozilla::MakeUnique<nsLineLayout>(
|
||||
state.mPresContext,
|
||||
state.mReflowState.mFloatManager,
|
||||
&state.mReflowState, &firstLine);
|
||||
mLineLayout->Init(&state, state.mMinLineHeight, state.mLineNumber);
|
||||
|
||||
mozilla::WritingMode lineWM = aReflowState.mLineLayout->GetWritingMode();
|
||||
mozilla::LogicalRect lineRect(state.mContentArea);
|
||||
nscoord iStart = lineRect.IStart(lineWM);
|
||||
nscoord availISize = lineRect.ISize(lineWM);
|
||||
nscoord availBSize = NS_UNCONSTRAINEDSIZE;
|
||||
|
||||
mLineLayout->BeginLineReflow(iStart, state.mBCoord,
|
||||
availISize, availBSize,
|
||||
false,
|
||||
false,
|
||||
lineWM, state.mContainerWidth);
|
||||
}
|
||||
|
||||
void
|
||||
nsRubyTextContainerFrame::ReflowRubyTextFrame(
|
||||
nsRubyTextFrame* rtFrame,
|
||||
nsIFrame* rbFrame,
|
||||
nscoord baseStart,
|
||||
nsPresContext* aPresContext,
|
||||
nsHTMLReflowMetrics& aDesiredSize,
|
||||
const nsHTMLReflowState& aReflowState)
|
||||
{
|
||||
nsReflowStatus frameReflowStatus;
|
||||
nsHTMLReflowMetrics metrics(aReflowState, aDesiredSize.mFlags);
|
||||
mozilla::WritingMode lineWM = mLineLayout->GetWritingMode();
|
||||
mozilla::LogicalSize availSize(lineWM, aReflowState.AvailableWidth(),
|
||||
aReflowState.AvailableHeight());
|
||||
nsHTMLReflowState childReflowState(aPresContext, aReflowState, rtFrame, availSize);
|
||||
|
||||
// Determine the inline coordinate for the text frame by centering over
|
||||
// the corresponding base frame
|
||||
int baseWidth;
|
||||
if (rbFrame) {
|
||||
baseWidth = rbFrame->ISize();
|
||||
|
||||
// If this is the last ruby annotation, it gets paired with ALL remaining
|
||||
// ruby bases
|
||||
if (!rtFrame->GetNextSibling()) {
|
||||
rbFrame = rbFrame->GetNextSibling();
|
||||
while (rbFrame) {
|
||||
baseWidth += rbFrame->ISize();
|
||||
rbFrame = rbFrame->GetNextSibling();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
baseWidth = 0;
|
||||
}
|
||||
|
||||
int baseCenter = baseStart + baseWidth / 2;
|
||||
// FIXME: Find a way to avoid using GetPrefISize here, potentially by moving
|
||||
// the frame after it has reflowed.
|
||||
nscoord ICoord = baseCenter - rtFrame->GetPrefISize(aReflowState.rendContext) / 2;
|
||||
if (ICoord > mLineLayout->GetCurrentICoord()) {
|
||||
mLineLayout->AdvanceICoord(ICoord - mLineLayout->GetCurrentICoord());
|
||||
}
|
||||
|
||||
bool pushedFrame;
|
||||
mLineLayout->ReflowFrame(rtFrame, frameReflowStatus,
|
||||
&metrics, pushedFrame);
|
||||
|
||||
NS_ASSERTION(!pushedFrame, "Ruby line breaking is not yet implemented");
|
||||
|
||||
mISize += metrics.ISize(lineWM);
|
||||
rtFrame->SetSize(nsSize(metrics.ISize(lineWM), metrics.BSize(lineWM)));
|
||||
FinishReflowChild(rtFrame, aPresContext, metrics, &childReflowState, 0, 0,
|
||||
NS_FRAME_NO_MOVE_FRAME | NS_FRAME_NO_MOVE_VIEW);
|
||||
}
|
||||
|
||||
/* virtual */ void
|
||||
nsRubyTextContainerFrame::Reflow(nsPresContext* aPresContext,
|
||||
nsHTMLReflowMetrics& aDesiredSize,
|
||||
@ -154,26 +65,11 @@ nsRubyTextContainerFrame::Reflow(nsPresContext* aPresContext,
|
||||
// the line layout.
|
||||
|
||||
aStatus = NS_FRAME_COMPLETE;
|
||||
mozilla::WritingMode lineWM = aReflowState.mLineLayout->GetWritingMode();
|
||||
mozilla::WritingMode frameWM = aReflowState.GetWritingMode();
|
||||
mozilla::LogicalMargin borderPadding =
|
||||
aReflowState.ComputedLogicalBorderPadding();
|
||||
WritingMode lineWM = aReflowState.mLineLayout->GetWritingMode();
|
||||
WritingMode frameWM = aReflowState.GetWritingMode();
|
||||
LogicalMargin borderPadding = aReflowState.ComputedLogicalBorderPadding();
|
||||
|
||||
aDesiredSize.ISize(lineWM) = mISize;
|
||||
nsLayoutUtils::SetBSizeFromFontMetrics(this, aDesiredSize, aReflowState,
|
||||
borderPadding, lineWM, frameWM);
|
||||
|
||||
nscoord bsize = aDesiredSize.BSize(lineWM);
|
||||
if (!mLines.empty()) {
|
||||
// Okay to use BlockStartAscent because it has just been correctly set by
|
||||
// nsLayoutUtils::SetBSizeFromFontMetrics.
|
||||
mLines.begin()->SetLogicalAscent(aDesiredSize.BlockStartAscent());
|
||||
mLines.begin()->SetBounds(aReflowState.GetWritingMode(), 0, 0, mISize,
|
||||
bsize, mISize);
|
||||
}
|
||||
|
||||
if (mLineLayout) {
|
||||
mLineLayout->EndLineReflow();
|
||||
mLineLayout = nullptr;
|
||||
}
|
||||
}
|
||||
|
@ -21,10 +21,7 @@
|
||||
nsContainerFrame* NS_NewRubyTextContainerFrame(nsIPresShell* aPresShell,
|
||||
nsStyleContext* aContext);
|
||||
|
||||
// If this is ever changed to be inline again, the code in
|
||||
// nsFrame::IsFontSizeInflationContainer should be updated to stop excluding
|
||||
// this from being considered inline.
|
||||
class nsRubyTextContainerFrame MOZ_FINAL : public nsBlockFrame
|
||||
class nsRubyTextContainerFrame MOZ_FINAL : public nsContainerFrame
|
||||
{
|
||||
public:
|
||||
NS_DECL_FRAMEARENA_HELPERS
|
||||
@ -41,24 +38,15 @@ public:
|
||||
#ifdef DEBUG_FRAME_DUMP
|
||||
virtual nsresult GetFrameName(nsAString& aResult) const MOZ_OVERRIDE;
|
||||
#endif
|
||||
|
||||
void ReflowRubyTextFrame(nsRubyTextFrame* rtFrame, nsIFrame* rbFrame,
|
||||
nscoord baseStart, nsPresContext* aPresContext,
|
||||
nsHTMLReflowMetrics& aDesiredSize,
|
||||
const nsHTMLReflowState& aReflowState);
|
||||
void BeginRTCLineLayout(nsPresContext* aPresContext,
|
||||
const nsHTMLReflowState& aReflowState);
|
||||
nsLineLayout* GetLineLayout() { return mLineLayout.get(); };
|
||||
void SetISize(nscoord aISize) { mISize = aISize; }
|
||||
|
||||
protected:
|
||||
friend nsContainerFrame*
|
||||
NS_NewRubyTextContainerFrame(nsIPresShell* aPresShell,
|
||||
nsStyleContext* aContext);
|
||||
explicit nsRubyTextContainerFrame(nsStyleContext* aContext) : nsBlockFrame(aContext) {}
|
||||
// This pointer is active only during reflow of the ruby structure. It gets
|
||||
// created when the corresponding ruby base container is reflowed, and it is
|
||||
// destroyed when the ruby text container itself is reflowed.
|
||||
mozilla::UniquePtr<nsLineLayout> mLineLayout;
|
||||
explicit nsRubyTextContainerFrame(nsStyleContext* aContext)
|
||||
: nsContainerFrame(aContext) {}
|
||||
|
||||
// The intended dimensions of the ruby text container. These are modified
|
||||
// whenever a ruby text box is reflowed and used when the ruby text container
|
||||
// is reflowed.
|
||||
|
Loading…
Reference in New Issue
Block a user