зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
0574ed0fc4
Коммит
9b0f833e9c
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче