michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsMathMLTokenFrame.h" michael@0: #include "nsPresContext.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsTextFrame.h" michael@0: #include "RestyleManager.h" michael@0: #include michael@0: michael@0: nsIFrame* michael@0: NS_NewMathMLTokenFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) michael@0: { michael@0: return new (aPresShell) nsMathMLTokenFrame(aContext); michael@0: } michael@0: michael@0: NS_IMPL_FRAMEARENA_HELPERS(nsMathMLTokenFrame) michael@0: michael@0: nsMathMLTokenFrame::~nsMathMLTokenFrame() michael@0: { michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMathMLTokenFrame::InheritAutomaticData(nsIFrame* aParent) michael@0: { michael@0: // let the base class get the default from our parent michael@0: nsMathMLContainerFrame::InheritAutomaticData(aParent); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: eMathMLFrameType michael@0: nsMathMLTokenFrame::GetMathMLFrameType() michael@0: { michael@0: // treat everything other than as ordinary... michael@0: if (mContent->Tag() != nsGkAtoms::mi_) { michael@0: return eMathMLFrameType_Ordinary; michael@0: } michael@0: michael@0: uint8_t mathVariant = StyleFont()->mMathVariant; michael@0: if ((mathVariant == NS_MATHML_MATHVARIANT_NONE && michael@0: (StyleFont()->mFont.style == NS_STYLE_FONT_STYLE_ITALIC || michael@0: HasAnyStateBits(NS_FRAME_IS_IN_SINGLE_CHAR_MI))) || michael@0: mathVariant == NS_MATHML_MATHVARIANT_ITALIC || michael@0: mathVariant == NS_MATHML_MATHVARIANT_BOLD_ITALIC || michael@0: mathVariant == NS_MATHML_MATHVARIANT_SANS_SERIF_ITALIC || michael@0: mathVariant == NS_MATHML_MATHVARIANT_SANS_SERIF_BOLD_ITALIC) { michael@0: return eMathMLFrameType_ItalicIdentifier; michael@0: } michael@0: return eMathMLFrameType_UprightIdentifier; michael@0: } michael@0: michael@0: void michael@0: nsMathMLTokenFrame::MarkTextFramesAsTokenMathML() michael@0: { michael@0: nsIFrame* child = nullptr; michael@0: uint32_t childCount = 0; michael@0: michael@0: // Set flags on child text frames michael@0: // - to force them to trim their leading and trailing whitespaces. michael@0: // - Indicate which frames are suitable for mathvariant michael@0: // - flag single character frames for special italic treatment michael@0: for (nsIFrame* childFrame = GetFirstPrincipalChild(); childFrame; michael@0: childFrame = childFrame->GetNextSibling()) { michael@0: for (nsIFrame* childFrame2 = childFrame->GetFirstPrincipalChild(); michael@0: childFrame2; childFrame2 = childFrame2->GetNextSibling()) { michael@0: if (childFrame2->GetType() == nsGkAtoms::textFrame) { michael@0: childFrame2->AddStateBits(TEXT_IS_IN_TOKEN_MATHML); michael@0: child = childFrame2; michael@0: childCount++; michael@0: } michael@0: } michael@0: } michael@0: if (mContent->Tag() == nsGkAtoms::mi_ && childCount == 1) { michael@0: nsAutoString data; michael@0: if (!nsContentUtils::GetNodeTextContent(mContent, false, data)) { michael@0: NS_RUNTIMEABORT("OOM"); michael@0: } michael@0: michael@0: data.CompressWhitespace(); michael@0: int32_t length = data.Length(); michael@0: michael@0: bool isSingleCharacter = length == 1 || michael@0: (length == 2 && NS_IS_HIGH_SURROGATE(data[0])); michael@0: michael@0: if (isSingleCharacter) { michael@0: child->AddStateBits(NS_FRAME_IS_IN_SINGLE_CHAR_MI); michael@0: AddStateBits(NS_FRAME_IS_IN_SINGLE_CHAR_MI); michael@0: } michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: nsMathMLTokenFrame::SetInitialChildList(ChildListID aListID, michael@0: nsFrameList& aChildList) michael@0: { michael@0: // First, let the base class do its work michael@0: nsresult rv = nsMathMLContainerFrame::SetInitialChildList(aListID, aChildList); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: MarkTextFramesAsTokenMathML(); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsMathMLTokenFrame::AppendFrames(ChildListID aListID, michael@0: nsFrameList& aChildList) michael@0: { michael@0: nsresult rv = nsMathMLContainerFrame::AppendFrames(aListID, aChildList); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: MarkTextFramesAsTokenMathML(); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsMathMLTokenFrame::InsertFrames(ChildListID aListID, michael@0: nsIFrame* aPrevFrame, michael@0: nsFrameList& aChildList) michael@0: { michael@0: nsresult rv = nsMathMLContainerFrame::InsertFrames(aListID, aPrevFrame, michael@0: aChildList); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: MarkTextFramesAsTokenMathML(); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsMathMLTokenFrame::Reflow(nsPresContext* aPresContext, michael@0: nsHTMLReflowMetrics& aDesiredSize, michael@0: const nsHTMLReflowState& aReflowState, michael@0: nsReflowStatus& aStatus) michael@0: { michael@0: nsresult rv = NS_OK; michael@0: michael@0: // initializations needed for empty markup like michael@0: aDesiredSize.Width() = aDesiredSize.Height() = 0; michael@0: aDesiredSize.SetTopAscent(0); michael@0: aDesiredSize.mBoundingMetrics = nsBoundingMetrics(); michael@0: michael@0: nsSize availSize(aReflowState.ComputedWidth(), NS_UNCONSTRAINEDSIZE); michael@0: nsIFrame* childFrame = GetFirstPrincipalChild(); michael@0: while (childFrame) { michael@0: // ask our children to compute their bounding metrics michael@0: nsHTMLReflowMetrics childDesiredSize(aReflowState.GetWritingMode(), michael@0: aDesiredSize.mFlags michael@0: | NS_REFLOW_CALC_BOUNDING_METRICS); michael@0: nsHTMLReflowState childReflowState(aPresContext, aReflowState, michael@0: childFrame, availSize); michael@0: rv = ReflowChild(childFrame, aPresContext, childDesiredSize, michael@0: childReflowState, aStatus); michael@0: //NS_ASSERTION(NS_FRAME_IS_COMPLETE(aStatus), "bad status"); michael@0: if (NS_FAILED(rv)) { michael@0: // Call DidReflow() for the child frames we successfully did reflow. michael@0: DidReflowChildren(GetFirstPrincipalChild(), childFrame); michael@0: return rv; michael@0: } michael@0: michael@0: SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize, michael@0: childDesiredSize.mBoundingMetrics); michael@0: michael@0: childFrame = childFrame->GetNextSibling(); michael@0: } michael@0: michael@0: michael@0: // place and size children michael@0: FinalizeReflow(*aReflowState.rendContext, aDesiredSize); michael@0: michael@0: aStatus = NS_FRAME_COMPLETE; michael@0: NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // For token elements, mBoundingMetrics is computed at the ReflowToken michael@0: // pass, it is not computed here because our children may be text frames michael@0: // that do not implement the GetBoundingMetrics() interface. michael@0: /* virtual */ nsresult michael@0: nsMathMLTokenFrame::Place(nsRenderingContext& aRenderingContext, michael@0: bool aPlaceOrigin, michael@0: nsHTMLReflowMetrics& aDesiredSize) michael@0: { michael@0: mBoundingMetrics = nsBoundingMetrics(); michael@0: for (nsIFrame* childFrame = GetFirstPrincipalChild(); childFrame; michael@0: childFrame = childFrame->GetNextSibling()) { michael@0: nsHTMLReflowMetrics childSize(aDesiredSize.GetWritingMode()); michael@0: GetReflowAndBoundingMetricsFor(childFrame, childSize, michael@0: childSize.mBoundingMetrics, nullptr); michael@0: // compute and cache the bounding metrics michael@0: mBoundingMetrics += childSize.mBoundingMetrics; michael@0: } michael@0: michael@0: nsRefPtr fm; michael@0: nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm)); michael@0: nscoord ascent = fm->MaxAscent(); michael@0: nscoord descent = fm->MaxDescent(); michael@0: michael@0: aDesiredSize.mBoundingMetrics = mBoundingMetrics; michael@0: aDesiredSize.Width() = mBoundingMetrics.width; michael@0: aDesiredSize.SetTopAscent(std::max(mBoundingMetrics.ascent, ascent)); michael@0: aDesiredSize.Height() = aDesiredSize.TopAscent() + michael@0: std::max(mBoundingMetrics.descent, descent); michael@0: michael@0: if (aPlaceOrigin) { michael@0: nscoord dy, dx = 0; michael@0: for (nsIFrame* childFrame = GetFirstPrincipalChild(); childFrame; michael@0: childFrame = childFrame->GetNextSibling()) { michael@0: nsHTMLReflowMetrics childSize(aDesiredSize.GetWritingMode()); michael@0: GetReflowAndBoundingMetricsFor(childFrame, childSize, michael@0: childSize.mBoundingMetrics); michael@0: michael@0: // place and size the child; (dx,0) makes the caret happy - bug 188146 michael@0: dy = childSize.Height() == 0 ? 0 : aDesiredSize.TopAscent() - childSize.TopAscent(); michael@0: FinishReflowChild(childFrame, PresContext(), childSize, nullptr, dx, dy, 0); michael@0: dx += childSize.Width(); michael@0: } michael@0: } michael@0: michael@0: SetReference(nsPoint(0, aDesiredSize.TopAscent())); michael@0: michael@0: return NS_OK; michael@0: } michael@0: