Bug 1052924 - Rewrite pref isize and min isize computation. r=dbaron

Known regression:
This patch changes AppendTextContainer, and put aside text containers
which contain spanning annotations. This changes makes those text
containers not be reflowed by the current code. It will be fixed in
some later patch.
This commit is contained in:
Xidorn Quan 2014-11-26 15:52:48 +11:00
Родитель 0574ed0fc4
Коммит 9b0f833e9c
4 изменённых файлов: 255 добавлений и 83 удалений

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

@ -14,6 +14,8 @@
using namespace mozilla;
#define RTC_ARRAY_SIZE 1
//----------------------------------------------------------------------
// Frame class boilerplate
@ -52,6 +54,121 @@ nsRubyBaseContainerFrame::GetFrameName(nsAString& aResult) const
}
#endif
class MOZ_STACK_CLASS PairEnumerator
{
public:
PairEnumerator(nsRubyBaseContainerFrame* aRBCFrame,
const nsTArray<nsRubyTextContainerFrame*>& aRTCFrames);
void Next();
bool AtEnd() const;
uint32_t GetLevelCount() const { return mFrames.Length(); }
nsIFrame* GetFrame(uint32_t aIndex) const { return mFrames[aIndex]; }
nsIFrame* GetBaseFrame() const { return GetFrame(0); }
nsIFrame* GetTextFrame(uint32_t aIndex) const { return GetFrame(aIndex + 1); }
private:
nsAutoTArray<nsIFrame*, RTC_ARRAY_SIZE + 1> mFrames;
};
PairEnumerator::PairEnumerator(
nsRubyBaseContainerFrame* aBaseContainer,
const nsTArray<nsRubyTextContainerFrame*>& aTextContainers)
{
const uint32_t rtcCount = aTextContainers.Length();
mFrames.SetCapacity(rtcCount + 1);
mFrames.AppendElement(aBaseContainer->GetFirstPrincipalChild());
for (uint32_t i = 0; i < rtcCount; i++) {
nsIFrame* rtFrame = aTextContainers[i]->GetFirstPrincipalChild();
mFrames.AppendElement(rtFrame);
}
}
void
PairEnumerator::Next()
{
for (uint32_t i = 0, iend = mFrames.Length(); i < iend; i++) {
if (mFrames[i]) {
mFrames[i] = mFrames[i]->GetNextSibling();
}
}
}
bool
PairEnumerator::AtEnd() const
{
for (uint32_t i = 0, iend = mFrames.Length(); i < iend; i++) {
if (mFrames[i]) {
return false;
}
}
return true;
}
nscoord
nsRubyBaseContainerFrame::CalculateMaxSpanISize(
nsRenderingContext* aRenderingContext)
{
nscoord max = 0;
uint32_t spanCount = mSpanContainers.Length();
for (uint32_t i = 0; i < spanCount; i++) {
nsIFrame* frame = mSpanContainers[i]->GetFirstPrincipalChild();
nscoord isize = frame->GetPrefISize(aRenderingContext);
max = std::max(max, isize);
}
return max;
}
static nscoord
CalculatePairPrefISize(nsRenderingContext* aRenderingContext,
const PairEnumerator& aEnumerator)
{
nscoord max = 0;
uint32_t levelCount = aEnumerator.GetLevelCount();
for (uint32_t i = 0; i < levelCount; i++) {
nsIFrame* frame = aEnumerator.GetFrame(i);
if (frame) {
max = std::max(max, frame->GetPrefISize(aRenderingContext));
}
}
return max;
}
/* virtual */ void
nsRubyBaseContainerFrame::AddInlineMinISize(
nsRenderingContext *aRenderingContext, nsIFrame::InlineMinISizeData *aData)
{
if (!mSpanContainers.IsEmpty()) {
// Since spans are not breakable internally, use our pref isize
// directly if there is any span.
aData->currentLine += GetPrefISize(aRenderingContext);
return;
}
nscoord max = 0;
PairEnumerator enumerator(this, mTextContainers);
for (; !enumerator.AtEnd(); enumerator.Next()) {
// We use *pref* isize for computing the min isize of pairs
// because ruby bases and texts are unbreakable internally.
max = std::max(max, CalculatePairPrefISize(aRenderingContext, enumerator));
}
aData->currentLine += max;
}
/* virtual */ void
nsRubyBaseContainerFrame::AddInlinePrefISize(
nsRenderingContext *aRenderingContext, nsIFrame::InlinePrefISizeData *aData)
{
nscoord sum = 0;
PairEnumerator enumerator(this, mTextContainers);
for (; !enumerator.AtEnd(); enumerator.Next()) {
sum += CalculatePairPrefISize(aRenderingContext, enumerator);
}
sum = std::max(sum, CalculateMaxSpanISize(aRenderingContext));
aData->currentLine += sum;
}
/* virtual */ bool
nsRubyBaseContainerFrame::IsFrameOfType(uint32_t aFlags) const
{
@ -62,12 +179,20 @@ nsRubyBaseContainerFrame::IsFrameOfType(uint32_t aFlags) const
void nsRubyBaseContainerFrame::AppendTextContainer(nsIFrame* aFrame)
{
nsRubyTextContainerFrame* rtcFrame = do_QueryFrame(aFrame);
if (rtcFrame) {
MOZ_ASSERT(rtcFrame, "Must provide a ruby text container.");
nsIFrame* onlyChild = rtcFrame->PrincipalChildList().OnlyChild();
if (onlyChild && onlyChild->IsPseudoFrame(rtcFrame->GetContent())) {
// Per CSS Ruby spec, if the only child of an rtc frame is
// a pseudo rt frame, it spans all bases in the segment.
mSpanContainers.AppendElement(rtcFrame);
} else {
mTextContainers.AppendElement(rtcFrame);
}
}
void nsRubyBaseContainerFrame::ClearTextContainers() {
mSpanContainers.Clear();
mTextContainers.Clear();
}

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

@ -32,6 +32,10 @@ public:
virtual nsIAtom* GetType() const MOZ_OVERRIDE;
virtual bool IsFrameOfType(uint32_t aFlags) const MOZ_OVERRIDE;
virtual bool CanContinueTextRun() const MOZ_OVERRIDE;
virtual void AddInlineMinISize(nsRenderingContext *aRenderingContext,
InlineMinISizeData *aData) MOZ_OVERRIDE;
virtual void AddInlinePrefISize(nsRenderingContext *aRenderingContext,
InlinePrefISizeData *aData) MOZ_OVERRIDE;
virtual void Reflow(nsPresContext* aPresContext,
nsHTMLReflowMetrics& aDesiredSize,
const nsHTMLReflowState& aReflowState,
@ -41,6 +45,14 @@ public:
virtual nsresult GetFrameName(nsAString& aResult) const MOZ_OVERRIDE;
#endif
#ifdef DEBUG
void AssertTextContainersEmpty()
{
MOZ_ASSERT(mSpanContainers.IsEmpty());
MOZ_ASSERT(mTextContainers.IsEmpty());
}
#endif
void AppendTextContainer(nsIFrame* aFrame);
void ClearTextContainers();
@ -49,13 +61,21 @@ protected:
NS_NewRubyBaseContainerFrame(nsIPresShell* aPresShell,
nsStyleContext* aContext);
explicit nsRubyBaseContainerFrame(nsStyleContext* aContext) : nsContainerFrame(aContext) {}
/*
* The ruby text containers that belong to the ruby segment defined by
* this ruby base container. These text containers are located at the start
* of reflow for the ruby frame (parent) and cleared at the end of that
* reflow.
nscoord CalculateMaxSpanISize(nsRenderingContext* aRenderingContext);
/**
* The arrays of ruby text containers below are filled before the ruby
* frame (parent) starts reflowing this ruby segment, and cleared when
* the reflow finishes.
*/
// The text containers that contain a span, which spans all ruby
// pairs in the ruby segment.
nsTArray<nsRubyTextContainerFrame*> mSpanContainers;
// Normal text containers that do not contain spans.
nsTArray<nsRubyTextContainerFrame*> mTextContainers;
};
#endif /* nsRubyBaseContainerFrame_h___ */

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

@ -59,87 +59,119 @@ nsRubyFrame::GetFrameName(nsAString& aResult) const
}
#endif
void
nsRubyFrame::CalculateColSizes(nsRenderingContext* aRenderingContext,
nsTArray<nscoord>& aColSizes)
class MOZ_STACK_CLASS TextContainerIterator
{
nsFrameList::Enumerator e(this->PrincipalChildList());
uint32_t annotationNum = 0;
int segmentNum = -1;
public:
TextContainerIterator(nsRubyBaseContainerFrame* aBaseContainer);
void Next();
bool AtEnd() const { return !mFrame; }
nsRubyTextContainerFrame* GetTextContainer() const
{
return static_cast<nsRubyTextContainerFrame*>(mFrame);
}
nsTArray<int> segmentBaseCounts;
private:
nsIFrame* mFrame;
};
for(; !e.AtEnd(); e.Next()) {
nsIFrame* childFrame = e.get();
if (childFrame->GetType() == nsGkAtoms::rubyBaseContainerFrame) {
segmentNum++;
segmentBaseCounts.AppendElement(0);
nsFrameList::Enumerator bases(childFrame->PrincipalChildList());
for(; !bases.AtEnd(); bases.Next()) {
aColSizes.AppendElement(bases.get()->GetPrefISize(aRenderingContext));
segmentBaseCounts.ElementAt(segmentNum)++;
}
} else if (childFrame->GetType() == nsGkAtoms::rubyTextContainerFrame) {
if (segmentNum == -1) {
// No rbc exists for first segment, so act as if there is one
segmentNum++;
segmentBaseCounts.AppendElement(1);
aColSizes.AppendElement(0);
}
nsFrameList::Enumerator annotations(childFrame->PrincipalChildList());
uint32_t baseCount = segmentBaseCounts.ElementAt(segmentNum);
for(; !annotations.AtEnd(); annotations.Next()) {
nsIFrame* annotationFrame = annotations.get();
if (annotationNum > baseCount) {
aColSizes.AppendElement(annotationFrame->
GetPrefISize(aRenderingContext));
baseCount++;
segmentBaseCounts.ElementAt(segmentNum) = baseCount;
annotationNum++;
} else if (annotationNum < baseCount - 1) {
//there are fewer annotations than bases, so the last annotation is
//associated with (spans) the remaining bases. This means these
//columns can't be broken up, so gather their entire ISize in one
//entry of aColSizes and clear the other entries.
int baseSum = 0;
for (uint32_t i = annotationNum; i < annotationNum + baseCount; i++) {
baseSum += aColSizes.ElementAt(i);
if (i > annotationNum) {
aColSizes.ElementAt(i) = 0;
}
}
aColSizes.ElementAt(annotationNum) =
std::max(baseSum, annotationFrame->GetPrefISize(aRenderingContext));
annotationNum = baseCount;
} else {
aColSizes.ElementAt(annotationNum) =
std::max(aColSizes.ElementAt(annotationNum),
annotationFrame->GetPrefISize(aRenderingContext));
annotationNum++;
}
}
} else {
NS_ASSERTION(false, "Unrecognized child type for ruby frame.");
TextContainerIterator::TextContainerIterator(
nsRubyBaseContainerFrame* aBaseContainer)
{
mFrame = aBaseContainer;
Next();
}
void
TextContainerIterator::Next()
{
if (mFrame) {
mFrame = mFrame->GetNextSibling();
if (mFrame && mFrame->GetType() != nsGkAtoms::rubyTextContainerFrame) {
mFrame = nullptr;
}
}
}
/**
* This class is responsible for appending and clearing
* text container list of the base container.
*/
class MOZ_STACK_CLASS AutoSetTextContainers
{
public:
AutoSetTextContainers(nsRubyBaseContainerFrame* aBaseContainer);
~AutoSetTextContainers();
private:
nsRubyBaseContainerFrame* mBaseContainer;
};
AutoSetTextContainers::AutoSetTextContainers(
nsRubyBaseContainerFrame* aBaseContainer)
: mBaseContainer(aBaseContainer)
{
#ifdef DEBUG
aBaseContainer->AssertTextContainersEmpty();
#endif
for (TextContainerIterator iter(aBaseContainer);
!iter.AtEnd(); iter.Next()) {
aBaseContainer->AppendTextContainer(iter.GetTextContainer());
}
}
AutoSetTextContainers::~AutoSetTextContainers()
{
mBaseContainer->ClearTextContainers();
}
/**
* This enumerator enumerates each segment.
*/
class MOZ_STACK_CLASS SegmentEnumerator
{
public:
SegmentEnumerator(nsRubyFrame* aRubyFrame);
void Next();
bool AtEnd() const { return !mBaseContainer; }
nsRubyBaseContainerFrame* GetBaseContainer() const
{
return mBaseContainer;
}
private:
nsRubyBaseContainerFrame* mBaseContainer;
};
SegmentEnumerator::SegmentEnumerator(nsRubyFrame* aRubyFrame)
{
nsIFrame* frame = aRubyFrame->GetFirstPrincipalChild();
MOZ_ASSERT(!frame ||
frame->GetType() == nsGkAtoms::rubyBaseContainerFrame);
mBaseContainer = static_cast<nsRubyBaseContainerFrame*>(frame);
}
void
SegmentEnumerator::Next()
{
MOZ_ASSERT(mBaseContainer);
nsIFrame* frame = mBaseContainer->GetNextSibling();
while (frame && frame->GetType() != nsGkAtoms::rubyBaseContainerFrame) {
frame = frame->GetNextSibling();
}
mBaseContainer = static_cast<nsRubyBaseContainerFrame*>(frame);
}
/* virtual */ void
nsRubyFrame::AddInlineMinISize(nsRenderingContext *aRenderingContext,
nsIFrame::InlineMinISizeData *aData)
{
//FIXME: This needs to handle the cases where it's possible for a ruby base to
//break, as well as forced breaks.
nsTArray<int> colSizes;
CalculateColSizes(aRenderingContext, colSizes);
nscoord max = 0;
for (uint32_t i = 0; i < colSizes.Length(); i++) {
if (colSizes.ElementAt(i) > max) {
max = colSizes.ElementAt(i);
}
for (SegmentEnumerator e(this); !e.AtEnd(); e.Next()) {
AutoSetTextContainers holder(e.GetBaseContainer());
max = std::max(max, e.GetBaseContainer()->GetMinISize(aRenderingContext));
}
aData->currentLine += max;
}
@ -147,14 +179,11 @@ nsRubyFrame::AddInlineMinISize(nsRenderingContext *aRenderingContext,
nsRubyFrame::AddInlinePrefISize(nsRenderingContext *aRenderingContext,
nsIFrame::InlinePrefISizeData *aData)
{
nsTArray<int> colSizes;
CalculateColSizes(aRenderingContext, colSizes);
nscoord sum = 0;
for (uint32_t i = 0; i < colSizes.Length(); i++) {
sum += colSizes.ElementAt(i);
for (SegmentEnumerator e(this); !e.AtEnd(); e.Next()) {
AutoSetTextContainers holder(e.GetBaseContainer());
sum += e.GetBaseContainer()->GetPrefISize(aRenderingContext);
}
aData->currentLine += sum;
}
@ -208,7 +237,7 @@ nsRubyFrame::Reflow(nsPresContext* aPresContext,
nscoord annotationBSize = 0;
for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) {
nsIFrame* childFrame = e.get();
if (e.get()->GetType() == nsGkAtoms::rubyBaseContainerFrame) {
if (childFrame->GetType() == nsGkAtoms::rubyBaseContainerFrame) {
if (segmentRBC) {
annotationBSize = 0;
}

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

@ -48,8 +48,6 @@ protected:
friend nsContainerFrame* NS_NewRubyFrame(nsIPresShell* aPresShell,
nsStyleContext* aContext);
explicit nsRubyFrame(nsStyleContext* aContext) : nsContainerFrame(aContext) {}
void CalculateColSizes(nsRenderingContext* aRenderingContext,
nsTArray<nscoord>& aColSizes);
nscoord mBaseline;
};