nsMathMLTokenFrame.cpp (8386B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "nsMathMLTokenFrame.h" 8 9 #include <algorithm> 10 11 #include "gfxContext.h" 12 #include "mozilla/PresShell.h" 13 #include "nsContentUtils.h" 14 #include "nsLayoutUtils.h" 15 #include "nsPresContext.h" 16 #include "nsTextFrame.h" 17 18 using namespace mozilla; 19 20 nsIFrame* NS_NewMathMLTokenFrame(PresShell* aPresShell, ComputedStyle* aStyle) { 21 return new (aPresShell) 22 nsMathMLTokenFrame(aStyle, aPresShell->GetPresContext()); 23 } 24 25 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLTokenFrame) 26 27 nsMathMLTokenFrame::~nsMathMLTokenFrame() = default; 28 29 NS_IMETHODIMP 30 nsMathMLTokenFrame::InheritAutomaticData(nsIFrame* aParent) { 31 // let the base class get the default from our parent 32 nsMathMLContainerFrame::InheritAutomaticData(aParent); 33 34 return NS_OK; 35 } 36 37 MathMLFrameType nsMathMLTokenFrame::GetMathMLFrameType() { 38 // treat everything other than <mi> as ordinary... 39 if (!mContent->IsMathMLElement(nsGkAtoms::mi)) { 40 return MathMLFrameType::Ordinary; 41 } 42 43 StyleMathVariant mathVariant = StyleFont()->mMathVariant; 44 if ((mathVariant == StyleMathVariant::None && 45 (StyleFont()->mFont.style.IsItalic() || 46 HasAnyStateBits(NS_FRAME_IS_IN_SINGLE_CHAR_MI))) || 47 mathVariant == StyleMathVariant::Italic || 48 mathVariant == StyleMathVariant::BoldItalic || 49 mathVariant == StyleMathVariant::SansSerifItalic || 50 mathVariant == StyleMathVariant::SansSerifBoldItalic) { 51 return MathMLFrameType::ItalicIdentifier; 52 } 53 return MathMLFrameType::UprightIdentifier; 54 } 55 56 void nsMathMLTokenFrame::MarkTextFramesAsTokenMathML() { 57 nsIFrame* child = nullptr; 58 uint32_t childCount = 0; 59 60 // Set flags on child text frames 61 // - to force them to trim their leading and trailing whitespaces. 62 // - Indicate which frames are suitable for mathvariant 63 // - flag single character <mi> frames for special italic treatment 64 for (nsIFrame* childFrame = PrincipalChildList().FirstChild(); childFrame; 65 childFrame = childFrame->GetNextSibling()) { 66 for (nsIFrame* childFrame2 = childFrame->PrincipalChildList().FirstChild(); 67 childFrame2; childFrame2 = childFrame2->GetNextSibling()) { 68 if (childFrame2->IsTextFrame()) { 69 childFrame2->AddStateBits(TEXT_IS_IN_TOKEN_MATHML); 70 child = childFrame2; 71 childCount++; 72 } 73 } 74 } 75 if (mContent->IsMathMLElement(nsGkAtoms::mi) && childCount == 1) { 76 nsAutoString data; 77 nsContentUtils::GetNodeTextContent(mContent, false, data); 78 79 data.CompressWhitespace(); 80 int32_t length = data.Length(); 81 82 bool isSingleCharacter = 83 length == 1 || (length == 2 && NS_IS_HIGH_SURROGATE(data[0])); 84 85 if (isSingleCharacter) { 86 child->AddStateBits(NS_FRAME_IS_IN_SINGLE_CHAR_MI); 87 AddStateBits(NS_FRAME_IS_IN_SINGLE_CHAR_MI); 88 } 89 } 90 } 91 92 void nsMathMLTokenFrame::SetInitialChildList(ChildListID aListID, 93 nsFrameList&& aChildList) { 94 // First, let the base class do its work 95 nsMathMLContainerFrame::SetInitialChildList(aListID, std::move(aChildList)); 96 MarkTextFramesAsTokenMathML(); 97 } 98 99 void nsMathMLTokenFrame::AppendFrames(ChildListID aListID, 100 nsFrameList&& aChildList) { 101 nsMathMLContainerFrame::AppendFrames(aListID, std::move(aChildList)); 102 MarkTextFramesAsTokenMathML(); 103 } 104 105 void nsMathMLTokenFrame::InsertFrames( 106 ChildListID aListID, nsIFrame* aPrevFrame, 107 const nsLineList::iterator* aPrevFrameLine, nsFrameList&& aChildList) { 108 nsMathMLContainerFrame::InsertFrames(aListID, aPrevFrame, aPrevFrameLine, 109 std::move(aChildList)); 110 MarkTextFramesAsTokenMathML(); 111 } 112 113 void nsMathMLTokenFrame::Reflow(nsPresContext* aPresContext, 114 ReflowOutput& aDesiredSize, 115 const ReflowInput& aReflowInput, 116 nsReflowStatus& aStatus) { 117 MarkInReflow(); 118 MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); 119 120 // initializations needed for empty markup like <mtag></mtag> 121 aDesiredSize.ClearSize(); 122 aDesiredSize.SetBlockStartAscent(0); 123 aDesiredSize.mBoundingMetrics = nsBoundingMetrics(); 124 125 for (nsIFrame* childFrame : PrincipalChildList()) { 126 // ask our children to compute their bounding metrics 127 ReflowOutput childDesiredSize(aReflowInput.GetWritingMode()); 128 WritingMode wm = childFrame->GetWritingMode(); 129 LogicalSize availSize = aReflowInput.ComputedSize(wm); 130 availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE; 131 ReflowInput childReflowInput(aPresContext, aReflowInput, childFrame, 132 availSize); 133 nsReflowStatus childStatus; 134 ReflowChild(childFrame, aPresContext, childDesiredSize, childReflowInput, 135 childStatus); 136 NS_ASSERTION(childStatus.IsComplete(), 137 "We gave the child unconstrained available block-size, so its " 138 "status should be complete!"); 139 SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize, 140 childDesiredSize.mBoundingMetrics); 141 } 142 143 // place and size children 144 FinalizeReflow(aReflowInput.mRenderingContext->GetDrawTarget(), aDesiredSize); 145 146 aStatus.Reset(); // This type of frame can't be split. 147 } 148 149 // For token elements, mBoundingMetrics is computed at the ReflowToken 150 // pass, it is not computed here because our children may be text frames 151 // that do not implement the GetBoundingMetrics() interface. 152 /* virtual */ 153 void nsMathMLTokenFrame::Place(DrawTarget* aDrawTarget, 154 const PlaceFlags& aFlags, 155 ReflowOutput& aDesiredSize) { 156 mBoundingMetrics = nsBoundingMetrics(); 157 for (nsIFrame* childFrame : PrincipalChildList()) { 158 ReflowOutput childSize(aDesiredSize.GetWritingMode()); 159 nsBoundingMetrics bmChild; 160 GetReflowAndBoundingMetricsFor(childFrame, childSize, bmChild, nullptr); 161 auto childMargin = GetMarginForPlace(aFlags, childFrame); 162 bmChild.ascent += childMargin.top; 163 bmChild.descent += childMargin.bottom; 164 bmChild.rightBearing += childMargin.LeftRight(); 165 bmChild.width += childMargin.LeftRight(); 166 167 // compute and cache the bounding metrics 168 mBoundingMetrics += bmChild; 169 } 170 171 RefPtr<nsFontMetrics> fm = 172 nsLayoutUtils::GetInflatedFontMetricsForFrame(this); 173 nscoord ascent = fm->MaxAscent(); 174 nscoord descent = fm->MaxDescent(); 175 176 aDesiredSize.mBoundingMetrics = mBoundingMetrics; 177 aDesiredSize.Width() = mBoundingMetrics.width; 178 aDesiredSize.SetBlockStartAscent(std::max(mBoundingMetrics.ascent, ascent)); 179 aDesiredSize.Height() = aDesiredSize.BlockStartAscent() + 180 std::max(mBoundingMetrics.descent, descent); 181 182 // Apply width/height to math content box. 183 auto sizes = GetWidthAndHeightForPlaceAdjustment(aFlags); 184 auto shiftX = ApplyAdjustmentForWidthAndHeight(aFlags, sizes, aDesiredSize, 185 mBoundingMetrics); 186 187 // Add padding+border. 188 auto borderPadding = GetBorderPaddingForPlace(aFlags); 189 InflateReflowAndBoundingMetrics(borderPadding, aDesiredSize, 190 mBoundingMetrics); 191 192 if (!aFlags.contains(PlaceFlag::MeasureOnly)) { 193 nscoord dx = borderPadding.left; 194 dx += shiftX; 195 for (nsIFrame* childFrame : PrincipalChildList()) { 196 ReflowOutput childSize(aDesiredSize.GetWritingMode()); 197 GetReflowAndBoundingMetricsFor(childFrame, childSize, 198 childSize.mBoundingMetrics); 199 auto childMargin = GetMarginForPlace(aFlags, childFrame); 200 201 // place and size the child; (dx,0) makes the caret happy - bug 188146 202 nscoord dy = childSize.Height() == 0 203 ? 0 204 : aDesiredSize.BlockStartAscent() - 205 childSize.BlockStartAscent() + childMargin.top; 206 FinishReflowChild(childFrame, PresContext(), childSize, nullptr, dx, dy, 207 ReflowChildFlags::Default); 208 dx += childSize.Width(); 209 } 210 } 211 212 SetReference(nsPoint(0, aDesiredSize.BlockStartAscent())); 213 }