Bumping gaia.json for 2 gaia revision(s) a=gaia-bump
[gecko.git] / layout / mathml / nsMathMLmrootFrame.cpp
blob8e3ed4857dd3eac8d0a79ccb1523bce6935a4603
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsMathMLmrootFrame.h"
7 #include "nsPresContext.h"
8 #include "nsRenderingContext.h"
9 #include <algorithm>
11 using namespace mozilla;
14 // <mroot> -- form a radical - implementation
17 // additional style context to be used by our MathMLChar.
18 #define NS_SQR_CHAR_STYLE_CONTEXT_INDEX 0
20 static const char16_t kSqrChar = char16_t(0x221A);
22 nsIFrame*
23 NS_NewMathMLmrootFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
25 return new (aPresShell) nsMathMLmrootFrame(aContext);
28 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmrootFrame)
30 nsMathMLmrootFrame::nsMathMLmrootFrame(nsStyleContext* aContext) :
31 nsMathMLContainerFrame(aContext),
32 mSqrChar(),
33 mBarRect()
37 nsMathMLmrootFrame::~nsMathMLmrootFrame()
41 void
42 nsMathMLmrootFrame::Init(nsIContent* aContent,
43 nsContainerFrame* aParent,
44 nsIFrame* aPrevInFlow)
46 nsMathMLContainerFrame::Init(aContent, aParent, aPrevInFlow);
48 nsPresContext *presContext = PresContext();
50 // No need to track the style context given to our MathML char.
51 // The Style System will use Get/SetAdditionalStyleContext() to keep it
52 // up-to-date if dynamic changes arise.
53 nsAutoString sqrChar; sqrChar.Assign(kSqrChar);
54 mSqrChar.SetData(presContext, sqrChar);
55 ResolveMathMLCharStyle(presContext, mContent, mStyleContext, &mSqrChar);
58 NS_IMETHODIMP
59 nsMathMLmrootFrame::TransmitAutomaticData()
61 // 1. The REC says:
62 // The <mroot> element increments scriptlevel by 2, and sets displaystyle to
63 // "false", within index, but leaves both attributes unchanged within base.
64 // 2. The TeXbook (Ch 17. p.141) says \sqrt is compressed
65 UpdatePresentationDataFromChildAt(1, 1,
66 NS_MATHML_COMPRESSED,
67 NS_MATHML_COMPRESSED);
68 UpdatePresentationDataFromChildAt(0, 0,
69 NS_MATHML_COMPRESSED, NS_MATHML_COMPRESSED);
71 PropagateFrameFlagFor(mFrames.LastChild(),
72 NS_FRAME_MATHML_SCRIPT_DESCENDANT);
74 return NS_OK;
77 void
78 nsMathMLmrootFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
79 const nsRect& aDirtyRect,
80 const nsDisplayListSet& aLists)
82 /////////////
83 // paint the content we are square-rooting
84 nsMathMLContainerFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
86 /////////////
87 // paint the sqrt symbol
88 if (!NS_MATHML_HAS_ERROR(mPresentationData.flags)) {
89 mSqrChar.Display(aBuilder, this, aLists, 0);
91 DisplayBar(aBuilder, this, mBarRect, aLists);
93 #if defined(DEBUG) && defined(SHOW_BOUNDING_BOX)
94 // for visual debug
95 nsRect rect;
96 mSqrChar.GetRect(rect);
97 nsBoundingMetrics bm;
98 mSqrChar.GetBoundingMetrics(bm);
99 DisplayBoundingMetrics(aBuilder, this, rect.TopLeft(), bm, aLists);
100 #endif
104 void
105 nsMathMLmrootFrame::GetRadicalXOffsets(nscoord aIndexWidth, nscoord aSqrWidth,
106 nsFontMetrics* aFontMetrics,
107 nscoord* aIndexOffset,
108 nscoord* aSqrOffset)
110 // The index is tucked in closer to the radical while making sure
111 // that the kern does not make the index and radical collide
112 nscoord dxIndex, dxSqr;
113 nscoord xHeight = aFontMetrics->XHeight();
114 nscoord indexRadicalKern = NSToCoordRound(1.35f * xHeight);
115 nscoord oneDevPixel = aFontMetrics->AppUnitsPerDevPixel();
116 gfxFont* mathFont = aFontMetrics->GetThebesFontGroup()->GetFirstMathFont();
117 if (mathFont) {
118 indexRadicalKern =
119 mathFont->GetMathConstant(gfxFontEntry::RadicalKernAfterDegree,
120 oneDevPixel);
121 indexRadicalKern = -indexRadicalKern;
123 if (indexRadicalKern > aIndexWidth) {
124 dxIndex = indexRadicalKern - aIndexWidth;
125 dxSqr = 0;
127 else {
128 dxIndex = 0;
129 dxSqr = aIndexWidth - indexRadicalKern;
132 if (mathFont) {
133 // add some kern before the radical index
134 nscoord indexRadicalKernBefore = 0;
135 indexRadicalKernBefore =
136 mathFont->GetMathConstant(gfxFontEntry::RadicalKernBeforeDegree,
137 oneDevPixel);
138 dxIndex += indexRadicalKernBefore;
139 dxSqr += indexRadicalKernBefore;
140 } else {
141 // avoid collision by leaving a minimum space between index and radical
142 nscoord minimumClearance = aSqrWidth / 2;
143 if (dxIndex + aIndexWidth + minimumClearance > dxSqr + aSqrWidth) {
144 if (aIndexWidth + minimumClearance < aSqrWidth) {
145 dxIndex = aSqrWidth - (aIndexWidth + minimumClearance);
146 dxSqr = 0;
148 else {
149 dxIndex = 0;
150 dxSqr = (aIndexWidth + minimumClearance) - aSqrWidth;
155 if (aIndexOffset)
156 *aIndexOffset = dxIndex;
157 if (aSqrOffset)
158 *aSqrOffset = dxSqr;
161 void
162 nsMathMLmrootFrame::Reflow(nsPresContext* aPresContext,
163 nsHTMLReflowMetrics& aDesiredSize,
164 const nsHTMLReflowState& aReflowState,
165 nsReflowStatus& aStatus)
167 nsReflowStatus childStatus;
169 aDesiredSize.ClearSize();
170 aDesiredSize.SetBlockStartAscent(0);
172 nsBoundingMetrics bmSqr, bmBase, bmIndex;
173 nsRenderingContext& renderingContext = *aReflowState.rendContext;
175 //////////////////
176 // Reflow Children
178 int32_t count = 0;
179 nsIFrame* baseFrame = nullptr;
180 nsIFrame* indexFrame = nullptr;
181 nsHTMLReflowMetrics baseSize(aReflowState);
182 nsHTMLReflowMetrics indexSize(aReflowState);
183 nsIFrame* childFrame = mFrames.FirstChild();
184 while (childFrame) {
185 // ask our children to compute their bounding metrics
186 nsHTMLReflowMetrics childDesiredSize(aReflowState,
187 aDesiredSize.mFlags
188 | NS_REFLOW_CALC_BOUNDING_METRICS);
189 WritingMode wm = childFrame->GetWritingMode();
190 LogicalSize availSize = aReflowState.ComputedSize(wm);
191 availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
192 nsHTMLReflowState childReflowState(aPresContext, aReflowState,
193 childFrame, availSize);
194 ReflowChild(childFrame, aPresContext,
195 childDesiredSize, childReflowState, childStatus);
196 //NS_ASSERTION(NS_FRAME_IS_COMPLETE(childStatus), "bad status");
197 if (0 == count) {
198 // base
199 baseFrame = childFrame;
200 baseSize = childDesiredSize;
201 bmBase = childDesiredSize.mBoundingMetrics;
203 else if (1 == count) {
204 // index
205 indexFrame = childFrame;
206 indexSize = childDesiredSize;
207 bmIndex = childDesiredSize.mBoundingMetrics;
209 count++;
210 childFrame = childFrame->GetNextSibling();
212 if (2 != count) {
213 // report an error, encourage people to get their markups in order
214 ReportChildCountError();
215 ReflowError(renderingContext, aDesiredSize);
216 aStatus = NS_FRAME_COMPLETE;
217 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
218 // Call DidReflow() for the child frames we successfully did reflow.
219 DidReflowChildren(mFrames.FirstChild(), childFrame);
220 return;
223 ////////////
224 // Prepare the radical symbol and the overline bar
226 nsRefPtr<nsFontMetrics> fm;
227 float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
228 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm),
229 fontSizeInflation);
231 nscoord ruleThickness, leading, psi;
232 GetRadicalParameters(fm, StyleFont()->mMathDisplay ==
233 NS_MATHML_DISPLAYSTYLE_BLOCK,
234 ruleThickness, leading, psi);
236 // built-in: adjust clearance psi to emulate \mathstrut using '1' (TexBook, p.131)
237 char16_t one = '1';
238 nsBoundingMetrics bmOne =
239 nsLayoutUtils::AppUnitBoundsOfString(&one, 1, *fm, renderingContext);
240 if (bmOne.ascent > bmBase.ascent)
241 psi += bmOne.ascent - bmBase.ascent;
243 // make sure that the rule appears on on screen
244 nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
245 if (ruleThickness < onePixel) {
246 ruleThickness = onePixel;
249 // adjust clearance psi to get an exact number of pixels -- this
250 // gives a nicer & uniform look on stacked radicals (bug 130282)
251 nscoord delta = psi % onePixel;
252 if (delta)
253 psi += onePixel - delta; // round up
255 // Stretch the radical symbol to the appropriate height if it is not big enough.
256 nsBoundingMetrics contSize = bmBase;
257 contSize.descent = bmBase.ascent + bmBase.descent + psi;
258 contSize.ascent = ruleThickness;
260 // height(radical) should be >= height(base) + psi + ruleThickness
261 nsBoundingMetrics radicalSize;
262 mSqrChar.Stretch(aPresContext, renderingContext,
263 fontSizeInflation,
264 NS_STRETCH_DIRECTION_VERTICAL,
265 contSize, radicalSize,
266 NS_STRETCH_LARGER,
267 StyleVisibility()->mDirection);
268 // radicalSize have changed at this point, and should match with
269 // the bounding metrics of the char
270 mSqrChar.GetBoundingMetrics(bmSqr);
272 // Update the desired size for the container (like msqrt, index is not yet included)
273 // the baseline will be that of the base.
274 mBoundingMetrics.ascent = bmBase.ascent + psi + ruleThickness;
275 mBoundingMetrics.descent =
276 std::max(bmBase.descent,
277 (bmSqr.ascent + bmSqr.descent - mBoundingMetrics.ascent));
278 mBoundingMetrics.width = bmSqr.width + bmBase.width;
279 mBoundingMetrics.leftBearing = bmSqr.leftBearing;
280 mBoundingMetrics.rightBearing = bmSqr.width +
281 std::max(bmBase.width, bmBase.rightBearing); // take also care of the rule
283 aDesiredSize.SetBlockStartAscent(mBoundingMetrics.ascent + leading);
284 aDesiredSize.Height() = aDesiredSize.BlockStartAscent() +
285 std::max(baseSize.Height() - baseSize.BlockStartAscent(),
286 mBoundingMetrics.descent + ruleThickness);
287 aDesiredSize.Width() = mBoundingMetrics.width;
289 /////////////
290 // Re-adjust the desired size to include the index.
292 // the index is raised by some fraction of the height
293 // of the radical, see \mroot macro in App. B, TexBook
294 float raiseIndexPercent = 0.6f;
295 gfxFont* mathFont = fm->GetThebesFontGroup()->GetFirstMathFont();
296 if (mathFont) {
297 raiseIndexPercent =
298 mathFont->GetMathConstant(gfxFontEntry::RadicalDegreeBottomRaisePercent);
300 nscoord raiseIndexDelta = NSToCoordRound(raiseIndexPercent *
301 (bmSqr.ascent + bmSqr.descent));
302 nscoord indexRaisedAscent = mBoundingMetrics.ascent // top of radical
303 - (bmSqr.ascent + bmSqr.descent) // to bottom of radical
304 + raiseIndexDelta + bmIndex.ascent + bmIndex.descent; // to top of raised index
306 nscoord indexClearance = 0;
307 if (mBoundingMetrics.ascent < indexRaisedAscent) {
308 indexClearance =
309 indexRaisedAscent - mBoundingMetrics.ascent; // excess gap introduced by a tall index
310 mBoundingMetrics.ascent = indexRaisedAscent;
311 nscoord descent = aDesiredSize.Height() - aDesiredSize.BlockStartAscent();
312 aDesiredSize.SetBlockStartAscent(mBoundingMetrics.ascent + leading);
313 aDesiredSize.Height() = aDesiredSize.BlockStartAscent() + descent;
316 nscoord dxIndex, dxSqr;
317 GetRadicalXOffsets(bmIndex.width, bmSqr.width, fm, &dxIndex, &dxSqr);
319 mBoundingMetrics.width = dxSqr + bmSqr.width + bmBase.width;
320 mBoundingMetrics.leftBearing =
321 std::min(dxIndex + bmIndex.leftBearing, dxSqr + bmSqr.leftBearing);
322 mBoundingMetrics.rightBearing = dxSqr + bmSqr.width +
323 std::max(bmBase.width, bmBase.rightBearing);
325 aDesiredSize.Width() = mBoundingMetrics.width;
326 aDesiredSize.mBoundingMetrics = mBoundingMetrics;
327 GatherAndStoreOverflow(&aDesiredSize);
329 // place the index
330 nscoord dx = dxIndex;
331 nscoord dy = aDesiredSize.BlockStartAscent() -
332 (indexRaisedAscent + indexSize.BlockStartAscent() - bmIndex.ascent);
333 FinishReflowChild(indexFrame, aPresContext, indexSize, nullptr,
334 MirrorIfRTL(aDesiredSize.Width(), indexSize.Width(), dx),
335 dy, 0);
337 // place the radical symbol and the radical bar
338 dx = dxSqr;
339 dy = indexClearance + leading; // leave a leading at the top
340 mSqrChar.SetRect(nsRect(MirrorIfRTL(aDesiredSize.Width(), bmSqr.width, dx),
341 dy, bmSqr.width, bmSqr.ascent + bmSqr.descent));
342 dx += bmSqr.width;
343 mBarRect.SetRect(MirrorIfRTL(aDesiredSize.Width(), bmBase.width, dx),
344 dy, bmBase.width, ruleThickness);
346 // place the base
347 dy = aDesiredSize.BlockStartAscent() - baseSize.BlockStartAscent();
348 FinishReflowChild(baseFrame, aPresContext, baseSize, nullptr,
349 MirrorIfRTL(aDesiredSize.Width(), baseSize.Width(), dx),
350 dy, 0);
352 mReference.x = 0;
353 mReference.y = aDesiredSize.BlockStartAscent();
355 aStatus = NS_FRAME_COMPLETE;
356 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
359 /* virtual */ void
360 nsMathMLmrootFrame::GetIntrinsicISizeMetrics(nsRenderingContext* aRenderingContext, nsHTMLReflowMetrics& aDesiredSize)
362 nsIFrame* baseFrame = mFrames.FirstChild();
363 nsIFrame* indexFrame = nullptr;
364 if (baseFrame)
365 indexFrame = baseFrame->GetNextSibling();
366 if (!indexFrame || indexFrame->GetNextSibling()) {
367 ReflowError(*aRenderingContext, aDesiredSize);
368 return;
371 float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
372 nscoord baseWidth =
373 nsLayoutUtils::IntrinsicForContainer(aRenderingContext, baseFrame,
374 nsLayoutUtils::PREF_ISIZE);
375 nscoord indexWidth =
376 nsLayoutUtils::IntrinsicForContainer(aRenderingContext, indexFrame,
377 nsLayoutUtils::PREF_ISIZE);
378 nscoord sqrWidth = mSqrChar.GetMaxWidth(PresContext(), *aRenderingContext,
379 fontSizeInflation);
381 nscoord dxSqr;
382 nsRefPtr<nsFontMetrics> fm;
383 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm),
384 fontSizeInflation);
385 GetRadicalXOffsets(indexWidth, sqrWidth, fm, nullptr, &dxSqr);
387 nscoord width = dxSqr + sqrWidth + baseWidth;
389 aDesiredSize.Width() = width;
390 aDesiredSize.mBoundingMetrics.width = width;
391 aDesiredSize.mBoundingMetrics.leftBearing = 0;
392 aDesiredSize.mBoundingMetrics.rightBearing = width;
395 // ----------------------
396 // the Style System will use these to pass the proper style context to our MathMLChar
397 nsStyleContext*
398 nsMathMLmrootFrame::GetAdditionalStyleContext(int32_t aIndex) const
400 switch (aIndex) {
401 case NS_SQR_CHAR_STYLE_CONTEXT_INDEX:
402 return mSqrChar.GetStyleContext();
403 break;
404 default:
405 return nullptr;
409 void
410 nsMathMLmrootFrame::SetAdditionalStyleContext(int32_t aIndex,
411 nsStyleContext* aStyleContext)
413 switch (aIndex) {
414 case NS_SQR_CHAR_STYLE_CONTEXT_INDEX:
415 mSqrChar.SetStyleContext(aStyleContext);
416 break;