Bug 397518. Wrap inline non-MathML children of MathML frames in anonymous blocks. r+sr=bzbarsky

This commit is contained in:
roc+@cs.cmu.edu 2007-10-12 01:30:54 -07:00
parent 75627082f2
commit 036806699a
14 changed files with 359 additions and 156 deletions

View File

@ -6744,8 +6744,67 @@ nsCSSFrameConstructor::ResolveStyleContext(nsIFrame* aParentFrame,
}
}
static void
ReparentFrame(nsFrameManager* aFrameManager,
nsIFrame* aNewParentFrame,
nsIFrame* aFrame)
{
aFrame->SetParent(aNewParentFrame);
aFrameManager->ReParentStyleContext(aFrame);
if (aFrame->GetStateBits() &
(NS_FRAME_HAS_VIEW | NS_FRAME_HAS_CHILD_WITH_VIEW)) {
// No need to walk up the tree, since the bits are already set
// right on the parent of aNewParentFrame.
NS_ASSERTION(aNewParentFrame->GetParent()->GetStateBits() &
NS_FRAME_HAS_CHILD_WITH_VIEW,
"aNewParentFrame's parent should have this bit set!");
aNewParentFrame->AddStateBits(NS_FRAME_HAS_CHILD_WITH_VIEW);
}
}
// MathML Mod - RBS
#ifdef MOZ_MATHML
nsresult
nsCSSFrameConstructor::FlushAccumulatedBlock(nsFrameConstructorState& aState,
nsIContent* aContent,
nsIFrame* aParentFrame,
nsFrameItems* aBlockItems,
nsFrameItems* aNewItems)
{
if (!aBlockItems->childList) {
// Nothing to do
return NS_OK;
}
nsStyleContext* parentContext =
nsFrame::CorrectStyleParentFrame(aParentFrame,
nsCSSAnonBoxes::mozMathMLAnonymousBlock)->GetStyleContext();
nsStyleSet *styleSet = mPresShell->StyleSet();
nsRefPtr<nsStyleContext> blockContext;
blockContext = styleSet->ResolvePseudoStyleFor(aContent,
nsCSSAnonBoxes::mozMathMLAnonymousBlock,
parentContext);
// then, create a block frame that will wrap the child frames. Make it a
// MathML frame so that Get(Absolute/Float)ContainingBlockFor know that this
// is not a suitable block.
nsIFrame* blockFrame = NS_NewMathMLmathBlockFrame(mPresShell, blockContext,
NS_BLOCK_SPACE_MGR | NS_BLOCK_MARGIN_ROOT);
if (NS_UNLIKELY(!blockFrame))
return NS_ERROR_OUT_OF_MEMORY;
InitAndRestoreFrame(aState, aContent, aParentFrame, nsnull, blockFrame);
for (nsIFrame* f = aBlockItems->childList; f; f = f->GetNextSibling()) {
ReparentFrame(aState.mFrameManager, blockFrame, f);
}
// abs-pos and floats are disabled in MathML children so we don't have to
// worry about messing up those.
blockFrame->SetInitialChildList(nsnull, aBlockItems->childList);
*aBlockItems = nsFrameItems();
aNewItems->AddChild(blockFrame);
return NS_OK;
}
nsresult
nsCSSFrameConstructor::ConstructMathMLFrame(nsFrameConstructorState& aState,
nsIContent* aContent,
@ -6860,7 +6919,8 @@ nsCSSFrameConstructor::ConstructMathMLFrame(nsFrameConstructorState& aState,
nsIFrame* blockFrame = NS_NewBlockFrame(mPresShell, blockContext,
NS_BLOCK_SPACE_MGR |
NS_BLOCK_MARGIN_ROOT);
if (NS_UNLIKELY(!newFrame)) {
if (NS_UNLIKELY(!blockFrame)) {
newFrame->Destroy();
return NS_ERROR_OUT_OF_MEMORY;
}
InitAndRestoreFrame(aState, aContent, newFrame, nsnull, blockFrame);
@ -6967,6 +7027,37 @@ nsCSSFrameConstructor::ConstructMathMLFrame(nsFrameConstructorState& aState,
CreateAnonymousFrames(aTag, aState, aContent, newFrame, PR_FALSE,
childItems);
// Wrap runs of inline children in a block
if (NS_SUCCEEDED(rv)) {
nsFrameItems newItems;
nsFrameItems currentBlock;
nsIFrame* f;
while ((f = childItems.childList) != nsnull) {
PRBool wrapFrame = IsInlineFrame(f) || IsFrameSpecial(f);
if (!wrapFrame) {
rv = FlushAccumulatedBlock(aState, aContent, newFrame, &currentBlock, &newItems);
if (NS_FAILED(rv))
break;
}
childItems.RemoveChild(f, nsnull);
if (wrapFrame) {
currentBlock.AddChild(f);
} else {
newItems.AddChild(f);
}
}
rv = FlushAccumulatedBlock(aState, aContent, newFrame, &currentBlock, &newItems);
if (childItems.childList) {
// an error must have occurred, delete unprocessed frames
CleanupFrameReferences(aState.mFrameManager, childItems.childList);
nsFrameList(childItems.childList).DestroyFrames();
}
childItems = newItems;
}
// Set the frame's initial child list
newFrame->SetInitialChildList(nsnull, childItems.childList);
@ -8606,6 +8697,11 @@ nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer,
return NS_OK;
}
#ifdef MOZ_MATHML
if (parentFrame->IsFrameOfType(nsIFrame::eMathML))
return RecreateFramesForContent(parentFrame->GetContent());
#endif
// If the frame we are manipulating is a ``special'' frame (that is, one
// that's been created as a result of a block-in-inline situation) then we
// need to append to the last special sibling, not to the frame itself.
@ -8991,7 +9087,12 @@ nsCSSFrameConstructor::ContentInserted(nsIContent* aContainer,
if (parentFrame->IsLeaf()) {
return NS_OK;
}
#ifdef MOZ_MATHML
if (parentFrame->IsFrameOfType(nsIFrame::eMathML))
return RecreateFramesForContent(parentFrame->GetContent());
#endif
nsFrameConstructorState state(mPresShell, mFixedContainingBlock,
GetAbsoluteContainingBlock(parentFrame),
GetFloatContainingBlock(parentFrame),
@ -9456,13 +9557,24 @@ nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer,
// Get the childFrame's parent frame
nsIFrame* parentFrame = childFrame->GetParent();
nsIAtom* parentType = parentFrame->GetType();
if (parentFrame->GetType() == nsGkAtoms::frameSetFrame &&
if (parentType == nsGkAtoms::frameSetFrame &&
IsSpecialFramesetChild(aChild)) {
// Just reframe the parent, since framesets are weird like that.
return RecreateFramesForContent(parentFrame->GetContent());
}
#ifdef MOZ_MATHML
// If we're a child of MathML, then we should reframe the MathML content.
// If we're non-MathML, then we would be wrapped in a block so we need to
// check our grandparent in that case.
nsIFrame* possibleMathMLAncestor = parentType == nsGkAtoms::blockFrame ?
parentFrame->GetParent() : parentFrame;
if (possibleMathMLAncestor->IsFrameOfType(nsIFrame::eMathML))
return RecreateFramesForContent(possibleMathMLAncestor->GetContent());
#endif
// Examine the containing-block for the removed content and see if
// :first-letter style applies.
nsIFrame* containingBlock = GetFloatContainingBlock(parentFrame);
@ -11114,6 +11226,18 @@ nsCSSFrameConstructor::RecreateFramesForContent(nsIContent* aContent)
// containing block reframes, hence the code here.
nsIFrame* frame = mPresShell->GetPrimaryFrameFor(aContent);
if (frame && frame->IsFrameOfType(nsIFrame::eMathML)) {
// Reframe the topmost MathML element to prevent exponential blowup
// (see bug 397518)
while (PR_TRUE) {
nsIContent* parentContent = aContent->GetParent();
nsIFrame* parentContentFrame = mPresShell->GetPrimaryFrameFor(parentContent);
if (!parentContentFrame || !parentContentFrame->IsFrameOfType(nsIFrame::eMathML))
break;
aContent = parentContent;
frame = parentContentFrame;
}
}
nsresult rv = NS_OK;
@ -11331,24 +11455,6 @@ nsCSSFrameConstructor::ProcessChildren(nsFrameConstructorState& aState,
// Support for :first-line style
static void
ReparentFrame(nsFrameManager* aFrameManager,
nsIFrame* aNewParentFrame,
nsIFrame* aFrame)
{
aFrame->SetParent(aNewParentFrame);
aFrameManager->ReParentStyleContext(aFrame);
if (aFrame->GetStateBits() &
(NS_FRAME_HAS_VIEW | NS_FRAME_HAS_CHILD_WITH_VIEW)) {
// No need to walk up the tree, since the bits are already set
// right on the parent of aNewParentFrame.
NS_ASSERTION(aNewParentFrame->GetParent()->GetStateBits() &
NS_FRAME_HAS_CHILD_WITH_VIEW,
"aNewParentFrame's parent should have this bit set!");
aNewParentFrame->AddStateBits(NS_FRAME_HAS_CHILD_WITH_VIEW);
}
}
// Special routine to handle placing a list of frames into a block
// frame that has first-line style. The routine ensures that the first
// collection of inline frames end up in a first-line frame.

View File

@ -594,6 +594,17 @@ private:
//MathML Mod - RBS
#ifdef MOZ_MATHML
/**
* Takes the frames in aBlockItems and wraps them in a new anonymous block
* frame whose content is aContent and whose parent will be aParentFrame.
* The anonymous block is added to aNewItems and aBlockItems is cleared.
*/
nsresult FlushAccumulatedBlock(nsFrameConstructorState& aState,
nsIContent* aContent,
nsIFrame* aParentFrame,
nsFrameItems* aBlockItems,
nsFrameItems* aNewItems);
nsresult ConstructMathMLFrame(nsFrameConstructorState& aState,
nsIContent* aContent,
nsIFrame* aParentFrame,

View File

@ -766,6 +766,15 @@ nsBlockFrame::GetPrefWidth(nsIRenderingContext *aRenderingContext)
return mPrefWidth;
}
nsRect
nsBlockFrame::ComputeTightBounds(gfxContext* aContext) const
{
// be conservative
if (GetStyleContext()->HasTextDecorations())
return GetOverflowRect();
return ComputeSimpleTightBounds(aContext);
}
static nsSize
CalculateContainingBlockSizeForAbsolutes(const nsHTMLReflowState& aReflowState,
nsSize aFrameSize)

View File

@ -241,6 +241,8 @@ public:
virtual nscoord GetMinWidth(nsIRenderingContext *aRenderingContext);
virtual nscoord GetPrefWidth(nsIRenderingContext *aRenderingContext);
virtual nsRect ComputeTightBounds(gfxContext* aContext) const;
NS_IMETHOD Reflow(nsPresContext* aPresContext,
nsHTMLReflowMetrics& aDesiredSize,
const nsHTMLReflowState& aReflowState,

View File

@ -881,9 +881,9 @@ nsIFrame::DisplayCaret(nsDisplayListBuilder* aBuilder,
}
PRBool
nsFrame::HasBorder()
nsFrame::HasBorder() const
{
return GetStyleBorder()->GetBorder() != nsMargin(0,0,0,0);
return GetUsedBorder() != nsMargin(0,0,0,0);
}
nsresult
@ -3065,6 +3065,37 @@ nsFrame::ComputeSize(nsIRenderingContext *aRenderingContext,
return result;
}
nsRect
nsIFrame::ComputeTightBounds(gfxContext* aContext) const
{
return GetOverflowRect();
}
nsRect
nsFrame::ComputeSimpleTightBounds(gfxContext* aContext) const
{
if (GetStyleOutline()->GetOutlineStyle() != NS_STYLE_BORDER_STYLE_NONE ||
HasBorder() || !GetStyleBackground()->IsTransparent() ||
GetStyleDisplay()->mAppearance) {
// Not necessarily tight, due to clipping, negative
// outline-offset, and lots of other issues, but that's OK
return GetOverflowRect();
}
nsRect r(0, 0, 0, 0);
PRInt32 listIndex = 0;
nsIAtom* childList = nsnull;
do {
nsIFrame* child = GetFirstChild(childList);
while (child) {
r.UnionRect(r, child->ComputeTightBounds(aContext) + child->GetPosition());
child = child->GetNextSibling();
}
childList = GetAdditionalChildListName(listIndex++);
} while (childList);
return r;
}
/* virtual */ nsSize
nsFrame::ComputeAutoSize(nsIRenderingContext *aRenderingContext,
nsSize aCBSize, nscoord aAvailableWidth,

View File

@ -288,6 +288,10 @@ public:
nsSize aMargin, nsSize aBorder, nsSize aPadding,
PRBool aShrinkWrap);
// Compute tight bounds assuming this frame honours its border, background
// and outline, its children's tight bounds, and nothing else.
nsRect ComputeSimpleTightBounds(gfxContext* aContext) const;
/**
* A helper, used by |nsFrame::ComputeSize| (for frames that need to
* override only this part of ComputeSize), that computes the size
@ -539,7 +543,7 @@ protected:
/**
* @return PR_FALSE if this frame definitely has no borders at all
*/
PRBool HasBorder();
PRBool HasBorder() const;
/**
* To be called by |BuildDisplayLists| of this class or derived classes to add

View File

@ -92,6 +92,7 @@ class nsDisplayListSet;
class nsDisplayList;
class gfxSkipChars;
class gfxSkipCharsIterator;
class gfxContext;
class nsLineList_iterator;
struct nsPeekOffsetStruct;
@ -1305,6 +1306,16 @@ public:
nsSize aMargin, nsSize aBorder, nsSize aPadding,
PRBool aShrinkWrap) = 0;
/**
* Compute a tight bounding rectangle for the frame. This is a rectangle
* that encloses the pixels that are actually drawn. We're allowed to be
* conservative and currently we don't try very hard. The rectangle is
* in appunits and relative to the origin of this frame.
* @param aContext a rendering context that can be used if we need
* to do measurement
*/
virtual nsRect ComputeTightBounds(gfxContext* aContext) const;
/**
* Pre-reflow hook. Before a frame is reflowed this method will be called.
* This call will always be invoked at least once before a subsequent Reflow

View File

@ -225,6 +225,15 @@ nsInlineFrame::ComputeSize(nsIRenderingContext *aRenderingContext,
return nsSize(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
}
nsRect
nsInlineFrame::ComputeTightBounds(gfxContext* aContext) const
{
// be conservative
if (GetStyleContext()->HasTextDecorations())
return GetOverflowRect();
return ComputeSimpleTightBounds(aContext);
}
void
nsInlineFrame::ReparentFloatsForInlineChild(nsIFrame* aOurLineContainer,
nsIFrame* aFrame,

View File

@ -118,6 +118,7 @@ public:
nsSize aCBSize, nscoord aAvailableWidth,
nsSize aMargin, nsSize aBorder, nsSize aPadding,
PRBool aShrinkWrap);
virtual nsRect ComputeTightBounds(gfxContext* aContext) const;
NS_IMETHOD Reflow(nsPresContext* aPresContext,
nsHTMLReflowMetrics& aDesiredSize,
const nsHTMLReflowState& aReflowState,

View File

@ -196,6 +196,7 @@ public:
nsSize aCBSize, nscoord aAvailableWidth,
nsSize aMargin, nsSize aBorder, nsSize aPadding,
PRBool aShrinkWrap);
virtual nsRect ComputeTightBounds(gfxContext* aContext) const;
NS_IMETHOD Reflow(nsPresContext* aPresContext,
nsHTMLReflowMetrics& aMetrics,
const nsHTMLReflowState& aReflowState,
@ -294,7 +295,7 @@ public:
* to offsets into the textrun; its initial offset is set to this frame's
* content offset
*/
gfxSkipCharsIterator EnsureTextRun(nsIRenderingContext* aRC = nsnull,
gfxSkipCharsIterator EnsureTextRun(gfxContext* aReferenceContext = nsnull,
nsIFrame* aLineContainer = nsnull,
const nsLineList::iterator* aLine = nsnull,
PRUint32* aFlowEndInTextRun = nsnull);

View File

@ -807,7 +807,7 @@ BuildTextRunsScanner::FindBoundaries(nsIFrame* aFrame, FindBoundaryState* aState
* out the block (slowly)
*/
static void
BuildTextRuns(nsIRenderingContext* aRC, nsTextFrame* aForFrame,
BuildTextRuns(gfxContext* aContext, nsTextFrame* aForFrame,
nsIFrame* aLineContainer, const nsLineList::iterator* aForFrameLine)
{
if (!aLineContainer) {
@ -817,9 +817,7 @@ BuildTextRuns(nsIRenderingContext* aRC, nsTextFrame* aForFrame,
}
nsPresContext* presContext = aLineContainer->PresContext();
gfxContext* ctx = static_cast<gfxContext*>
(aRC->GetNativeGraphicData(nsIRenderingContext::NATIVE_THEBES_CONTEXT));
BuildTextRunsScanner scanner(presContext, ctx, aLineContainer);
BuildTextRunsScanner scanner(presContext, aContext, aLineContainer);
nsBlockFrame* block = nsnull;
aLineContainer->QueryInterface(kBlockFrameCID, (void**)&block);
@ -1213,18 +1211,37 @@ GetFontGroupForFrame(nsIFrame* aFrame)
return fm->GetThebesFontGroup();
}
static already_AddRefed<gfxContext>
GetReferenceRenderingContext(nsTextFrame* aTextFrame, nsIRenderingContext* aRC)
{
nsCOMPtr<nsIRenderingContext> tmp = aRC;
if (!tmp) {
nsresult rv = aTextFrame->PresContext()->PresShell()->
CreateRenderingContext(aTextFrame, getter_AddRefs(tmp));
if (NS_FAILED(rv))
return nsnull;
}
gfxContext* ctx = static_cast<gfxContext*>
(tmp->GetNativeGraphicData(nsIRenderingContext::NATIVE_THEBES_CONTEXT));
NS_ADDREF(ctx);
return ctx;
}
/**
* The returned textrun must be released via gfxTextRunCache::ReleaseTextRun
* or gfxTextRunCache::AutoTextRun.
*/
static gfxTextRun*
GetHyphenTextRun(gfxTextRun* aTextRun, nsIRenderingContext* aRefContext)
GetHyphenTextRun(gfxTextRun* aTextRun, gfxContext* aContext, nsTextFrame* aTextFrame)
{
if (NS_UNLIKELY(!aRefContext)) {
return nsnull;
nsRefPtr<gfxContext> ctx = aContext;
if (!ctx) {
ctx = GetReferenceRenderingContext(aTextFrame, nsnull);
}
gfxContext* ctx = static_cast<gfxContext*>
(aRefContext->GetNativeGraphicData(nsIRenderingContext::NATIVE_THEBES_CONTEXT));
if (!ctx)
return nsnull;
gfxFontGroup* fontGroup = aTextRun->GetFontGroup();
PRUint32 flags = gfxFontGroup::TEXT_IS_PERSISTENT;
@ -1685,24 +1702,8 @@ BuildTextRunsScanner::AssignTextRun(gfxTextRun* aTextRun)
}
}
static already_AddRefed<nsIRenderingContext>
GetReferenceRenderingContext(nsTextFrame* aTextFrame, nsIRenderingContext* aRC)
{
if (aRC) {
NS_ADDREF(aRC);
return aRC;
}
nsIRenderingContext* result;
nsresult rv = aTextFrame->PresContext()->PresShell()->
CreateRenderingContext(aTextFrame, &result);
if (NS_FAILED(rv))
return nsnull;
return result;
}
gfxSkipCharsIterator
nsTextFrame::EnsureTextRun(nsIRenderingContext* aRC, nsIFrame* aLineContainer,
nsTextFrame::EnsureTextRun(gfxContext* aReferenceContext, nsIFrame* aLineContainer,
const nsLineList::iterator* aLine,
PRUint32* aFlowEndInTextRun)
{
@ -1711,10 +1712,12 @@ nsTextFrame::EnsureTextRun(nsIRenderingContext* aRC, nsIFrame* aLineContainer,
gTextRuns->MarkUsed(mTextRun);
}
} else {
nsCOMPtr<nsIRenderingContext> rendContext =
GetReferenceRenderingContext(this, aRC);
if (rendContext) {
BuildTextRuns(rendContext, this, aLineContainer, aLine);
nsRefPtr<gfxContext> ctx = aReferenceContext;
if (!ctx) {
ctx = GetReferenceRenderingContext(this, nsnull);
}
if (ctx) {
BuildTextRuns(ctx, this, aLineContainer, aLine);
}
if (!mTextRun) {
// A text run was not constructed for this frame. This is bad. The caller
@ -1858,9 +1861,6 @@ static void ClearMetrics(nsHTMLReflowMetrics& aMetrics)
aMetrics.width = 0;
aMetrics.height = 0;
aMetrics.ascent = 0;
#ifdef MOZ_MATHML
aMetrics.mBoundingMetrics.Clear();
#endif
}
static PRInt32 FindChar(const nsTextFragment* frag,
@ -2258,8 +2258,7 @@ gfxFloat
PropertyProvider::GetHyphenWidth()
{
if (mHyphenWidth < 0) {
nsCOMPtr<nsIRenderingContext> rc = GetReferenceRenderingContext(mFrame, nsnull);
gfxTextRunCache::AutoTextRun hyphenTextRun(GetHyphenTextRun(mTextRun, rc));
gfxTextRunCache::AutoTextRun hyphenTextRun(GetHyphenTextRun(mTextRun, nsnull, mFrame));
mHyphenWidth = mLetterSpacing;
if (hyphenTextRun.get()) {
mHyphenWidth += hyphenTextRun->GetAdvanceWidth(0, hyphenTextRun->GetLength(), nsnull);
@ -2369,8 +2368,7 @@ PropertyProvider::SetupJustificationSpacing()
mTextRun->GetAdvanceWidth(mStart.GetSkippedOffset(),
GetSkippedDistance(mStart, realEnd), this);
if (mFrame->GetStateBits() & TEXT_HYPHEN_BREAK) {
nsCOMPtr<nsIRenderingContext> rc = GetReferenceRenderingContext(mFrame, nsnull);
gfxTextRunCache::AutoTextRun hyphenTextRun(GetHyphenTextRun(mTextRun, rc));
gfxTextRunCache::AutoTextRun hyphenTextRun(GetHyphenTextRun(mTextRun, nsnull, mFrame));
if (hyphenTextRun.get()) {
naturalWidth +=
hyphenTextRun->GetAdvanceWidth(0, hyphenTextRun->GetLength(), nsnull);
@ -3863,8 +3861,7 @@ nsTextFrame::PaintTextWithSelectionColors(gfxContext* aCtx,
gfxFloat hyphenBaselineX = aFramePt.x + xOffset + mTextRun->GetDirection()*advance;
// Get a reference rendering context because aCtx might not have the
// reference matrix currently set
nsCOMPtr<nsIRenderingContext> rc = GetReferenceRenderingContext(this, nsnull);
gfxTextRunCache::AutoTextRun hyphenTextRun(GetHyphenTextRun(mTextRun, rc));
gfxTextRunCache::AutoTextRun hyphenTextRun(GetHyphenTextRun(mTextRun, nsnull, this));
if (hyphenTextRun.get()) {
hyphenTextRun->Draw(aCtx, gfxPoint(hyphenBaselineX, aTextBaselinePt.y),
0, hyphenTextRun->GetLength(), &aDirtyRect, nsnull, nsnull);
@ -3992,8 +3989,10 @@ void
nsTextFrame::PaintText(nsIRenderingContext* aRenderingContext, nsPoint aPt,
const nsRect& aDirtyRect)
{
// Don't pass in aRenderingContext here, because we need a *reference*
// context and aRenderingContext might have some transform in it
// XXX get the block and line passed to us somehow! This is slow!
gfxSkipCharsIterator iter = EnsureTextRun(aRenderingContext);
gfxSkipCharsIterator iter = EnsureTextRun();
if (!mTextRun)
return;
@ -4031,8 +4030,9 @@ nsTextFrame::PaintText(nsIRenderingContext* aRenderingContext, nsPoint aPt,
&dirtyRect, &provider, needAdvanceWidth);
if (GetStateBits() & TEXT_HYPHEN_BREAK) {
gfxFloat hyphenBaselineX = textBaselinePt.x + mTextRun->GetDirection()*advanceWidth;
nsCOMPtr<nsIRenderingContext> rc = GetReferenceRenderingContext(this, nsnull);
gfxTextRunCache::AutoTextRun hyphenTextRun(GetHyphenTextRun(mTextRun, rc));
// Don't use ctx as the context, because we need a reference context here,
// ctx may be transformed.
gfxTextRunCache::AutoTextRun hyphenTextRun(GetHyphenTextRun(mTextRun, nsnull, this));
if (hyphenTextRun.get()) {
hyphenTextRun->Draw(ctx, gfxPoint(hyphenBaselineX, textBaselinePt.y),
0, hyphenTextRun->GetLength(), &dirtyRect, nsnull, nsnull);
@ -4745,8 +4745,10 @@ nsTextFrame::AddInlineMinWidthForFlow(nsIRenderingContext *aRenderingContext,
nsIFrame::InlineMinWidthData *aData)
{
PRUint32 flowEndInTextRun;
gfxContext* ctx = static_cast<gfxContext*>
(aRenderingContext->GetNativeGraphicData(nsIRenderingContext::NATIVE_THEBES_CONTEXT));
gfxSkipCharsIterator iter =
EnsureTextRun(aRenderingContext, nsnull, aData->line, &flowEndInTextRun);
EnsureTextRun(ctx, nsnull, aData->line, &flowEndInTextRun);
if (!mTextRun)
return;
@ -4845,8 +4847,10 @@ nsTextFrame::AddInlinePrefWidthForFlow(nsIRenderingContext *aRenderingContext,
nsIFrame::InlinePrefWidthData *aData)
{
PRUint32 flowEndInTextRun;
gfxContext* ctx = static_cast<gfxContext*>
(aRenderingContext->GetNativeGraphicData(nsIRenderingContext::NATIVE_THEBES_CONTEXT));
gfxSkipCharsIterator iter =
EnsureTextRun(aRenderingContext, nsnull, aData->line, &flowEndInTextRun);
EnsureTextRun(ctx, nsnull, aData->line, &flowEndInTextRun);
if (!mTextRun)
return;
@ -4936,6 +4940,44 @@ nsTextFrame::ComputeSize(nsIRenderingContext *aRenderingContext,
return nsSize(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
}
static nsRect
RoundOut(const gfxRect& aRect)
{
nsRect r;
r.x = NSToCoordFloor(aRect.X());
r.y = NSToCoordFloor(aRect.Y());
r.width = NSToCoordCeil(aRect.XMost()) - r.x;
r.height = NSToCoordCeil(aRect.YMost()) - r.y;
return r;
}
nsRect
nsTextFrame::ComputeTightBounds(gfxContext* aContext) const
{
if ((GetStyleContext()->HasTextDecorations() &&
eCompatibility_NavQuirks == PresContext()->CompatibilityMode()) ||
(GetStateBits() & TEXT_HYPHEN_BREAK)) {
// This is conservative, but OK.
return GetOverflowRect();
}
gfxSkipCharsIterator iter = const_cast<nsTextFrame*>(this)->EnsureTextRun();
if (!mTextRun)
return nsRect(0, 0, 0, 0);
PropertyProvider provider(const_cast<nsTextFrame*>(this), iter);
// Trim trailing whitespace
provider.InitializeForDisplay(PR_TRUE);
gfxTextRun::Metrics metrics =
mTextRun->MeasureText(provider.GetStart().GetSkippedOffset(),
ComputeTransformedLength(provider), PR_TRUE,
aContext, &provider);
// mAscent should be the same as metrics.mAscent, but it's what we use to
// paint so that's the one we'll use.
return RoundOut(metrics.mBoundingBox) + nsPoint(0, mAscent);
}
static void
AddCharToMetrics(gfxTextRun* aCharTextRun, gfxTextRun* aBaseTextRun,
gfxTextRun::Metrics* aMetrics, PRBool aTightBoundingBox,
@ -5111,9 +5153,10 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
PRUint32 flowEndInTextRun;
nsIFrame* lineContainer = lineLayout.GetLineContainerFrame();
gfxContext* ctx = static_cast<gfxContext*>
(aReflowState.rendContext->GetNativeGraphicData(nsIRenderingContext::NATIVE_THEBES_CONTEXT));
gfxSkipCharsIterator iter =
EnsureTextRun(aReflowState.rendContext, lineContainer,
lineLayout.GetLine(), &flowEndInTextRun);
EnsureTextRun(ctx, lineContainer, lineLayout.GetLine(), &flowEndInTextRun);
PRInt32 skippedRunLength;
if (mTextRun && mTextRun->GetLength() == iter.GetSkippedOffset() &&
@ -5124,7 +5167,7 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
// preformatted newline was encountered, and prev-in-flow frames have
// consumed all the text of the textrun. We need a new textrun.
ClearTextRun();
iter = EnsureTextRun(aReflowState.rendContext, lineContainer,
iter = EnsureTextRun(ctx, lineContainer,
lineLayout.GetLine(), &flowEndInTextRun);
}
@ -5161,9 +5204,8 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
gfxTextRun::Metrics textMetrics;
PRBool needTightBoundingBox = (GetStateBits() & TEXT_FIRST_LETTER) != 0;
#ifdef MOZ_MATHML
if (NS_REFLOW_CALC_BOUNDING_METRICS & aMetrics.mFlags) {
needTightBoundingBox = PR_TRUE;
}
NS_ASSERTION(!(NS_REFLOW_CALC_BOUNDING_METRICS & aMetrics.mFlags),
"We shouldn't be passed NS_REFLOW_CALC_BOUNDING_METRICS anymore");
#endif
PRBool suppressInitialBreak = !lineLayout.LineIsBreakable() ||
!lineLayout.HasTrailingTextFrame();
@ -5200,8 +5242,6 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
gfxFloat availWidth = aReflowState.availableWidth;
PRBool canTrimTrailingWhitespace = !textStyle->WhiteSpaceIsSignificant() &&
textStyle->WhiteSpaceCanWrap();
gfxContext* ctx = static_cast<gfxContext*>
(aReflowState.rendContext->GetNativeGraphicData(nsIRenderingContext::NATIVE_THEBES_CONTEXT));
PRUint32 transformedCharsFit =
mTextRun->BreakAndMeasureText(transformedOffset, transformedLength,
(GetStateBits() & TEXT_START_OF_LINE) != 0,
@ -5236,7 +5276,7 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
}
if (usedHyphenation) {
// Fix up metrics to include hyphen
gfxTextRunCache::AutoTextRun hyphenTextRun(GetHyphenTextRun(mTextRun, aReflowState.rendContext));
gfxTextRunCache::AutoTextRun hyphenTextRun(GetHyphenTextRun(mTextRun, ctx, this));
if (hyphenTextRun.get()) {
AddCharToMetrics(hyphenTextRun.get(),
mTextRun, &textMetrics, needTightBoundingBox, ctx);
@ -5294,21 +5334,6 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
aMetrics.mOverflowArea.UnionRect(boundingBox,
nsRect(0, 0, aMetrics.width, aMetrics.height));
#ifdef MOZ_MATHML
// Store MathML bounding metrics. We've already calculated them above.
if (needTightBoundingBox) {
aMetrics.mBoundingMetrics.ascent =
NSToCoordCeil(PR_MAX(0, -textMetrics.mBoundingBox.Y()));
aMetrics.mBoundingMetrics.descent =
NSToCoordCeil(PR_MAX(0, textMetrics.mBoundingBox.YMost()));
aMetrics.mBoundingMetrics.leftBearing =
NSToCoordFloor(textMetrics.mBoundingBox.X());
aMetrics.mBoundingMetrics.rightBearing =
NSToCoordCeil(textMetrics.mBoundingBox.XMost());
aMetrics.mBoundingMetrics.width = aMetrics.width;
}
#endif
/////////////////////////////////////////////////////////////////////
// Clean up, update state
/////////////////////////////////////////////////////////////////////
@ -5400,7 +5425,9 @@ nsTextFrame::TrimTrailingWhiteSpace(nsPresContext* aPresContext,
if (!contentLength)
return NS_OK;
gfxSkipCharsIterator start = EnsureTextRun(&aRC);
gfxContext* ctx = static_cast<gfxContext*>
(aRC.GetNativeGraphicData(nsIRenderingContext::NATIVE_THEBES_CONTEXT));
gfxSkipCharsIterator start = EnsureTextRun(ctx);
if (!mTextRun)
return NS_ERROR_FAILURE;
PRUint32 trimmedStart = start.GetSkippedOffset();
@ -5447,8 +5474,6 @@ nsTextFrame::TrimTrailingWhiteSpace(nsPresContext* aPresContext,
}
}
gfxContext* ctx = static_cast<gfxContext*>
(aRC.GetNativeGraphicData(nsIRenderingContext::NATIVE_THEBES_CONTEXT));
gfxFloat advanceDelta;
mTextRun->SetLineBreaks(trimmedStart, trimmedEnd - trimmedStart,
(GetStateBits() & TEXT_START_OF_LINE) != 0, PR_TRUE,

View File

@ -966,6 +966,15 @@ nsMathMLContainerFrame::AttributeChanged(PRInt32 aNameSpaceID,
NS_FRAME_IS_DIRTY);
}
static PRBool
IsForeignChild(nsIFrame* aFrame)
{
// This counts nsMathMLmathBlockFrame as a foreign child, because it
// uses block reflow
return !(aFrame->IsFrameOfType(nsIFrame::eMathML)) ||
aFrame->GetType() == nsGkAtoms::blockFrame;
}
nsresult
nsMathMLContainerFrame::ReflowChild(nsIFrame* aChildFrame,
nsPresContext* aPresContext,
@ -976,7 +985,12 @@ nsMathMLContainerFrame::ReflowChild(nsIFrame* aChildFrame,
aDesiredSize.width = aDesiredSize.height = 0;
aDesiredSize.ascent = 0;
aDesiredSize.mBoundingMetrics.Clear();
aDesiredSize.mFlags |= NS_REFLOW_CALC_BOUNDING_METRICS;
PRBool isForeign = IsForeignChild(aChildFrame);
if (!isForeign) {
// We only do this for MathML frames now. Non-MathML frames support
// ComputeTightBounds instead.
aDesiredSize.mFlags |= NS_REFLOW_CALC_BOUNDING_METRICS;
}
// Having foreign/hybrid children, e.g., from html markups, is not defined by
// the MathML spec. But it can happen in practice, e.g., <html:img> allows us
@ -988,50 +1002,32 @@ nsMathMLContainerFrame::ReflowChild(nsIFrame* aChildFrame,
// them in the flow, if we can get their desired size. We observed that most
// frames may be reflowed generically, but nsInlineFrames need extra care.
#ifdef DEBUG
nsInlineFrame* inlineFrame;
aChildFrame->QueryInterface(kInlineFrameCID, (void**)&inlineFrame);
if (!inlineFrame)
return nsHTMLContainerFrame::
ReflowChild(aChildFrame, aPresContext, aDesiredSize, aReflowState,
0, 0, NS_FRAME_NO_MOVE_FRAME, aStatus);
// extra care for an nsInlineFrame
return ReflowForeignChild(aChildFrame, aPresContext, aDesiredSize, aReflowState, aStatus);
}
nsresult
nsMathMLContainerFrame::ReflowForeignChild(nsIFrame* aChildFrame,
nsPresContext* aPresContext,
nsHTMLReflowMetrics& aDesiredSize,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
nsAutoSpaceManager autoSpaceManager(const_cast<nsHTMLReflowState &>(aReflowState));
nsresult rv = autoSpaceManager.CreateSpaceManagerFor(aPresContext, this);
NS_ENSURE_SUCCESS(rv, rv);
// provide a local, self-contained linelayout where to reflow the nsInlineFrame
nsSize availSize(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
nsLineLayout ll(aPresContext, aReflowState.mSpaceManager,
aReflowState.parentReflowState, nsnull);
ll.BeginLineReflow(0, 0, availSize.width, availSize.height, PR_FALSE, PR_FALSE);
PRBool pushedFrame;
ll.ReflowFrame(aChildFrame, aStatus, &aDesiredSize, pushedFrame);
NS_ASSERTION(!pushedFrame, "unexpected");
ll.EndLineReflow();
// make up the bounding metrics from the reflow metrics.
aDesiredSize.mBoundingMetrics.ascent = aDesiredSize.ascent;
aDesiredSize.mBoundingMetrics.descent = aDesiredSize.height - aDesiredSize.ascent;
aDesiredSize.mBoundingMetrics.width = aDesiredSize.width;
aDesiredSize.mBoundingMetrics.rightBearing = aDesiredSize.width;
// Note: MathML's vertical & horizontal alignments happen much later in
// Place(), which is ultimately called from within FinalizeReflow().
aStatus = NS_FRAME_COMPLETE;
NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
return NS_OK;
NS_ASSERTION(!inlineFrame, "Inline frames should be wrapped in blocks");
#endif
// XXX should we do something here to compute good bounding metrics for
// the child?
nsresult rv = nsHTMLContainerFrame::
ReflowChild(aChildFrame, aPresContext, aDesiredSize, aReflowState,
0, 0, NS_FRAME_NO_MOVE_FRAME, aStatus);
if (isForeign) {
// use ComputeTightBounds API instead
gfxContext* ctx = static_cast<gfxContext*>
(aReflowState.rendContext->GetNativeGraphicData(nsIRenderingContext::NATIVE_THEBES_CONTEXT));
nsRect r = aChildFrame->ComputeTightBounds(ctx);
aDesiredSize.mBoundingMetrics.leftBearing = r.x;
aDesiredSize.mBoundingMetrics.rightBearing = r.XMost();
nscoord frameAscent = aDesiredSize.ascent == nsHTMLReflowMetrics::ASK_FOR_BASELINE
? aChildFrame->GetBaseline() : aDesiredSize.ascent;
aDesiredSize.mBoundingMetrics.ascent = frameAscent - r.y;
aDesiredSize.mBoundingMetrics.descent = r.YMost() - frameAscent;
aDesiredSize.mBoundingMetrics.width = aDesiredSize.width;
}
return rv;
}
NS_IMETHODIMP
@ -1051,8 +1047,7 @@ nsMathMLContainerFrame::Reflow(nsPresContext* aPresContext,
nsReflowStatus childStatus;
nsSize availSize(aReflowState.ComputedWidth(), aReflowState.ComputedHeight());
nsHTMLReflowMetrics childDesiredSize(
aDesiredSize.mFlags | NS_REFLOW_CALC_BOUNDING_METRICS);
nsHTMLReflowMetrics childDesiredSize(aDesiredSize.mFlags);
nsIFrame* childFrame = mFrames.FirstChild();
while (childFrame) {
nsHTMLReflowState childReflowState(aPresContext, aReflowState,
@ -1443,13 +1438,16 @@ nsMathMLContainerFrame::DidReflowChildren(nsIFrame* aFirst, nsIFrame* aStop)
//==========================
nsIFrame*
NS_NewMathMLmathBlockFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
NS_NewMathMLmathBlockFrame(nsIPresShell* aPresShell, nsStyleContext* aContext,
PRUint32 aFlags)
{
return new (aPresShell) nsMathMLmathBlockFrame(aContext);
nsMathMLmathBlockFrame* it = new (aPresShell) nsMathMLmathBlockFrame(aContext);
if (it) {
it->SetFlags(aFlags);
}
return it;
}
//==========================
nsIFrame*
NS_NewMathMLmathInlineFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
{

View File

@ -120,7 +120,8 @@ public:
virtual PRBool IsFrameOfType(PRUint32 aFlags) const
{
return nsHTMLContainerFrame::IsFrameOfType(aFlags & ~(nsIFrame::eMathML));
return !(aFlags & nsIFrame::eLineParticipant) &&
nsHTMLContainerFrame::IsFrameOfType(aFlags & ~(nsIFrame::eMathML));
}
NS_IMETHOD
@ -222,13 +223,6 @@ public:
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus);
nsresult
ReflowForeignChild(nsIFrame* aKidFrame,
nsPresContext* aPresContext,
nsHTMLReflowMetrics& aDesiredSize,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus);
// helper to add the inter-spacing when <math> is the immediate parent.
// Since we don't (yet) handle the root <math> element ourselves, we need to
// take special care of the inter-frame spacing on elements for which <math>
@ -330,7 +324,8 @@ protected:
// Issues: If/when mathml becomes a pluggable component, the separation will be needed.
class nsMathMLmathBlockFrame : public nsBlockFrame {
public:
friend nsIFrame* NS_NewMathMLmathBlockFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
friend nsIFrame* NS_NewMathMLmathBlockFrame(nsIPresShell* aPresShell,
nsStyleContext* aContext, PRUint32 aFlags);
// beware, mFrames is not set by nsBlockFrame
// cannot use mFrames{.FirstChild()|.etc} since the block code doesn't set mFrames

View File

@ -70,12 +70,12 @@ nsIFrame* NS_NewMathMLmsqrtFrame(nsIPresShell* aPresShell, nsStyleContext* aCont
nsIFrame* NS_NewMathMLmrootFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
nsIFrame* NS_NewMathMLmactionFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
nsIFrame* NS_NewMathMLmathBlockFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
nsIFrame* NS_NewMathMLmathBlockFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, PRUint32 aFlags);
nsIFrame* NS_NewMathMLmathInlineFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
inline nsIFrame* NS_NewMathMLmathFrame(nsIPresShell* aPresShell, PRBool aIsBlock, nsStyleContext* aContext)
{
return (aIsBlock)
? NS_NewMathMLmathBlockFrame(aPresShell, aContext)
? NS_NewMathMLmathBlockFrame(aPresShell, aContext, 0)
: NS_NewMathMLmathInlineFrame(aPresShell, aContext);
}
#endif /* nsMathMLParts_h___ */