2014-07-23 06:08:01 +04:00
|
|
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* vim: set ts=2 et sw=2 tw=80: */
|
|
|
|
/* This Source Code is subject to the terms of the Mozilla Public License
|
|
|
|
* version 2.0 (the "License"). You can obtain a copy of the License at
|
|
|
|
* http://mozilla.org/MPL/2.0/. */
|
|
|
|
|
|
|
|
/* rendering object for CSS "display: ruby" */
|
|
|
|
#include "nsRubyFrame.h"
|
2014-08-15 21:34:20 +04:00
|
|
|
#include "nsLineLayout.h"
|
2014-07-23 06:08:01 +04:00
|
|
|
#include "nsPresContext.h"
|
|
|
|
#include "nsStyleContext.h"
|
2014-08-15 21:34:20 +04:00
|
|
|
#include "WritingModes.h"
|
|
|
|
#include "nsRubyBaseContainerFrame.h"
|
|
|
|
#include "nsRubyTextContainerFrame.h"
|
|
|
|
|
|
|
|
using namespace mozilla;
|
2014-07-23 06:08:01 +04:00
|
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
|
|
|
|
// Frame class boilerplate
|
|
|
|
// =======================
|
|
|
|
|
|
|
|
NS_QUERYFRAME_HEAD(nsRubyFrame)
|
|
|
|
NS_QUERYFRAME_ENTRY(nsRubyFrame)
|
|
|
|
NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
|
|
|
|
|
|
|
|
NS_IMPL_FRAMEARENA_HELPERS(nsRubyFrame)
|
|
|
|
|
|
|
|
nsContainerFrame*
|
|
|
|
NS_NewRubyFrame(nsIPresShell* aPresShell,
|
|
|
|
nsStyleContext* aContext)
|
|
|
|
{
|
|
|
|
return new (aPresShell) nsRubyFrame(aContext);
|
|
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
|
|
|
|
// nsRubyFrame Method Implementations
|
|
|
|
// ==================================
|
|
|
|
|
|
|
|
nsIAtom*
|
|
|
|
nsRubyFrame::GetType() const
|
|
|
|
{
|
|
|
|
return nsGkAtoms::rubyFrame;
|
|
|
|
}
|
|
|
|
|
2014-08-15 21:34:20 +04:00
|
|
|
/* virtual */ bool
|
2014-07-23 06:08:01 +04:00
|
|
|
nsRubyFrame::IsFrameOfType(uint32_t aFlags) const
|
|
|
|
{
|
|
|
|
return nsContainerFrame::IsFrameOfType(aFlags &
|
2014-08-15 21:34:20 +04:00
|
|
|
~(nsIFrame::eLineParticipant));
|
2014-07-23 06:08:01 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DEBUG_FRAME_DUMP
|
|
|
|
nsresult
|
|
|
|
nsRubyFrame::GetFrameName(nsAString& aResult) const
|
|
|
|
{
|
|
|
|
return MakeFrameName(NS_LITERAL_STRING("Ruby"), aResult);
|
|
|
|
}
|
|
|
|
#endif
|
2014-08-15 21:34:20 +04:00
|
|
|
|
2014-11-26 07:52:48 +03:00
|
|
|
class MOZ_STACK_CLASS TextContainerIterator
|
2014-08-15 21:34:20 +04:00
|
|
|
{
|
2014-11-26 07:52:48 +03:00
|
|
|
public:
|
|
|
|
TextContainerIterator(nsRubyBaseContainerFrame* aBaseContainer);
|
|
|
|
void Next();
|
|
|
|
bool AtEnd() const { return !mFrame; }
|
|
|
|
nsRubyTextContainerFrame* GetTextContainer() const
|
|
|
|
{
|
|
|
|
return static_cast<nsRubyTextContainerFrame*>(mFrame);
|
|
|
|
}
|
2014-08-15 21:34:20 +04:00
|
|
|
|
2014-11-26 07:52:48 +03:00
|
|
|
private:
|
|
|
|
nsIFrame* mFrame;
|
|
|
|
};
|
2014-08-15 21:34:20 +04:00
|
|
|
|
2014-11-26 07:52:48 +03:00
|
|
|
TextContainerIterator::TextContainerIterator(
|
|
|
|
nsRubyBaseContainerFrame* aBaseContainer)
|
|
|
|
{
|
|
|
|
mFrame = aBaseContainer;
|
|
|
|
Next();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TextContainerIterator::Next()
|
|
|
|
{
|
|
|
|
if (mFrame) {
|
|
|
|
mFrame = mFrame->GetNextSibling();
|
|
|
|
if (mFrame && mFrame->GetType() != nsGkAtoms::rubyTextContainerFrame) {
|
|
|
|
mFrame = nullptr;
|
2014-08-15 21:34:20 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-26 07:52:48 +03:00
|
|
|
/**
|
|
|
|
* 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);
|
|
|
|
}
|
|
|
|
|
2014-08-15 21:34:20 +04:00
|
|
|
/* virtual */ void
|
|
|
|
nsRubyFrame::AddInlineMinISize(nsRenderingContext *aRenderingContext,
|
|
|
|
nsIFrame::InlineMinISizeData *aData)
|
|
|
|
{
|
|
|
|
nscoord max = 0;
|
2014-11-26 07:52:48 +03:00
|
|
|
for (SegmentEnumerator e(this); !e.AtEnd(); e.Next()) {
|
|
|
|
AutoSetTextContainers holder(e.GetBaseContainer());
|
|
|
|
max = std::max(max, e.GetBaseContainer()->GetMinISize(aRenderingContext));
|
2014-08-15 21:34:20 +04:00
|
|
|
}
|
|
|
|
aData->currentLine += max;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* virtual */ void
|
|
|
|
nsRubyFrame::AddInlinePrefISize(nsRenderingContext *aRenderingContext,
|
|
|
|
nsIFrame::InlinePrefISizeData *aData)
|
|
|
|
{
|
|
|
|
nscoord sum = 0;
|
2014-11-26 07:52:48 +03:00
|
|
|
for (SegmentEnumerator e(this); !e.AtEnd(); e.Next()) {
|
|
|
|
AutoSetTextContainers holder(e.GetBaseContainer());
|
|
|
|
sum += e.GetBaseContainer()->GetPrefISize(aRenderingContext);
|
2014-08-15 21:34:20 +04:00
|
|
|
}
|
|
|
|
aData->currentLine += sum;
|
|
|
|
}
|
|
|
|
|
2014-11-26 07:52:49 +03:00
|
|
|
/* virtual */ LogicalSize
|
|
|
|
nsRubyFrame::ComputeSize(nsRenderingContext *aRenderingContext,
|
|
|
|
WritingMode aWM,
|
|
|
|
const LogicalSize& aCBSize,
|
|
|
|
nscoord aAvailableISize,
|
|
|
|
const LogicalSize& aMargin,
|
|
|
|
const LogicalSize& aBorder,
|
|
|
|
const LogicalSize& aPadding,
|
|
|
|
ComputeSizeFlags aFlags)
|
|
|
|
{
|
|
|
|
// Ruby frame is inline, hence don't compute size before reflow.
|
|
|
|
return LogicalSize(aWM, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
|
|
|
|
}
|
|
|
|
|
2014-08-15 21:34:20 +04:00
|
|
|
/* virtual */ nscoord
|
|
|
|
nsRubyFrame::GetLogicalBaseline(WritingMode aWritingMode) const
|
|
|
|
{
|
|
|
|
return mBaseline;
|
|
|
|
}
|
|
|
|
|
2014-08-15 21:34:20 +04:00
|
|
|
/* virtual */ bool
|
|
|
|
nsRubyFrame::CanContinueTextRun() const
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-08-15 21:34:20 +04:00
|
|
|
/* virtual */ void
|
|
|
|
nsRubyFrame::Reflow(nsPresContext* aPresContext,
|
|
|
|
nsHTMLReflowMetrics& aDesiredSize,
|
|
|
|
const nsHTMLReflowState& aReflowState,
|
|
|
|
nsReflowStatus& aStatus)
|
|
|
|
{
|
|
|
|
DO_GLOBAL_REFLOW_COUNT("nsRubyFrame");
|
|
|
|
DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
|
|
|
|
|
|
|
|
if (!aReflowState.mLineLayout) {
|
|
|
|
NS_ASSERTION(aReflowState.mLineLayout,
|
|
|
|
"No line layout provided to RubyFrame reflow method.");
|
|
|
|
aStatus = NS_FRAME_COMPLETE;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Begin the span for the ruby frame
|
|
|
|
WritingMode frameWM = aReflowState.GetWritingMode();
|
|
|
|
WritingMode lineWM = aReflowState.mLineLayout->GetWritingMode();
|
|
|
|
LogicalMargin borderPadding = aReflowState.ComputedLogicalBorderPadding();
|
2014-11-26 07:52:49 +03:00
|
|
|
nscoord startEdge = borderPadding.IStart(frameWM);
|
|
|
|
nscoord endEdge = aReflowState.AvailableISize() - borderPadding.IEnd(frameWM);
|
|
|
|
NS_ASSERTION(aReflowState.AvailableISize() != NS_UNCONSTRAINEDSIZE,
|
2014-08-15 21:34:20 +04:00
|
|
|
"should no longer use available widths");
|
|
|
|
aReflowState.mLineLayout->BeginSpan(this, &aReflowState,
|
2014-11-26 07:52:49 +03:00
|
|
|
startEdge, endEdge, &mBaseline);
|
2014-08-15 21:34:20 +04:00
|
|
|
|
|
|
|
// FIXME: line breaking / continuations not yet implemented
|
|
|
|
aStatus = NS_FRAME_COMPLETE;
|
|
|
|
LogicalSize availSize(lineWM, aReflowState.AvailableISize(),
|
|
|
|
aReflowState.AvailableBSize());
|
2014-11-26 07:52:48 +03:00
|
|
|
for (SegmentEnumerator e(this); !e.AtEnd(); e.Next()) {
|
|
|
|
nsRubyBaseContainerFrame* baseContainer = e.GetBaseContainer();
|
|
|
|
AutoSetTextContainers holder(baseContainer);
|
|
|
|
nsReflowStatus baseReflowStatus;
|
|
|
|
nsHTMLReflowMetrics baseMetrics(aReflowState, aDesiredSize.mFlags);
|
2014-11-26 07:52:49 +03:00
|
|
|
bool pushedFrame;
|
|
|
|
aReflowState.mLineLayout->ReflowFrame(baseContainer, baseReflowStatus,
|
|
|
|
&baseMetrics, pushedFrame);
|
2014-11-26 07:52:48 +03:00
|
|
|
|
2014-11-26 07:52:49 +03:00
|
|
|
nsRect baseRect = baseContainer->GetRect();
|
2014-11-26 07:52:48 +03:00
|
|
|
for (TextContainerIterator iter(baseContainer);
|
|
|
|
!iter.AtEnd(); iter.Next()) {
|
|
|
|
nsRubyTextContainerFrame* textContainer = iter.GetTextContainer();
|
|
|
|
nsReflowStatus textReflowStatus;
|
|
|
|
nsHTMLReflowMetrics textMetrics(aReflowState, aDesiredSize.mFlags);
|
|
|
|
nsHTMLReflowState textReflowState(aPresContext, aReflowState,
|
|
|
|
textContainer, availSize);
|
|
|
|
textReflowState.mLineLayout = aReflowState.mLineLayout;
|
|
|
|
textContainer->Reflow(aPresContext, textMetrics,
|
|
|
|
textReflowState, textReflowStatus);
|
|
|
|
NS_ASSERTION(textReflowStatus == NS_FRAME_COMPLETE,
|
|
|
|
"Ruby line breaking is not yet implemented");
|
|
|
|
textContainer->SetSize(LogicalSize(lineWM, textMetrics.ISize(lineWM),
|
|
|
|
textMetrics.BSize(lineWM)));
|
2014-11-26 07:52:49 +03:00
|
|
|
nscoord x, y;
|
|
|
|
nscoord bsize = textMetrics.BSize(lineWM);
|
|
|
|
if (lineWM.IsVertical()) {
|
|
|
|
x = lineWM.IsVerticalLR() ? -bsize : baseRect.XMost();
|
|
|
|
y = baseRect.Y();
|
|
|
|
} else {
|
|
|
|
x = baseRect.X();
|
|
|
|
y = -bsize;
|
|
|
|
}
|
2014-11-26 07:52:48 +03:00
|
|
|
FinishReflowChild(textContainer, aPresContext, textMetrics,
|
2014-11-26 07:52:49 +03:00
|
|
|
&textReflowState, x, y, 0);
|
2014-08-15 21:34:20 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
aDesiredSize.ISize(lineWM) = aReflowState.mLineLayout->EndSpan(this);
|
|
|
|
nsLayoutUtils::SetBSizeFromFontMetrics(this, aDesiredSize, aReflowState,
|
|
|
|
borderPadding, lineWM, frameWM);
|
|
|
|
}
|