Bumping manifests a=b2g-bump
[gecko.git] / layout / mathml / nsMathMLmrootFrame.cpp
blobd4e41d7d88ea456686a0c3e6d5dcd264dbfe5a32
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 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
228 renderingContext.SetFont(fm);
230 nscoord ruleThickness, leading, psi;
231 GetRadicalParameters(fm, StyleFont()->mMathDisplay ==
232 NS_MATHML_DISPLAYSTYLE_BLOCK,
233 ruleThickness, leading, psi);
235 // built-in: adjust clearance psi to emulate \mathstrut using '1' (TexBook, p.131)
236 char16_t one = '1';
237 nsBoundingMetrics bmOne = renderingContext.GetBoundingMetrics(&one, 1);
238 if (bmOne.ascent > bmBase.ascent)
239 psi += bmOne.ascent - bmBase.ascent;
241 // make sure that the rule appears on on screen
242 nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
243 if (ruleThickness < onePixel) {
244 ruleThickness = onePixel;
247 // adjust clearance psi to get an exact number of pixels -- this
248 // gives a nicer & uniform look on stacked radicals (bug 130282)
249 nscoord delta = psi % onePixel;
250 if (delta)
251 psi += onePixel - delta; // round up
253 // Stretch the radical symbol to the appropriate height if it is not big enough.
254 nsBoundingMetrics contSize = bmBase;
255 contSize.descent = bmBase.ascent + bmBase.descent + psi;
256 contSize.ascent = ruleThickness;
258 // height(radical) should be >= height(base) + psi + ruleThickness
259 nsBoundingMetrics radicalSize;
260 mSqrChar.Stretch(aPresContext, renderingContext,
261 NS_STRETCH_DIRECTION_VERTICAL,
262 contSize, radicalSize,
263 NS_STRETCH_LARGER,
264 StyleVisibility()->mDirection);
265 // radicalSize have changed at this point, and should match with
266 // the bounding metrics of the char
267 mSqrChar.GetBoundingMetrics(bmSqr);
269 // Update the desired size for the container (like msqrt, index is not yet included)
270 // the baseline will be that of the base.
271 mBoundingMetrics.ascent = bmBase.ascent + psi + ruleThickness;
272 mBoundingMetrics.descent =
273 std::max(bmBase.descent,
274 (bmSqr.ascent + bmSqr.descent - mBoundingMetrics.ascent));
275 mBoundingMetrics.width = bmSqr.width + bmBase.width;
276 mBoundingMetrics.leftBearing = bmSqr.leftBearing;
277 mBoundingMetrics.rightBearing = bmSqr.width +
278 std::max(bmBase.width, bmBase.rightBearing); // take also care of the rule
280 aDesiredSize.SetBlockStartAscent(mBoundingMetrics.ascent + leading);
281 aDesiredSize.Height() = aDesiredSize.BlockStartAscent() +
282 std::max(baseSize.Height() - baseSize.BlockStartAscent(),
283 mBoundingMetrics.descent + ruleThickness);
284 aDesiredSize.Width() = mBoundingMetrics.width;
286 /////////////
287 // Re-adjust the desired size to include the index.
289 // the index is raised by some fraction of the height
290 // of the radical, see \mroot macro in App. B, TexBook
291 float raiseIndexPercent = 0.6f;
292 gfxFont* mathFont = fm->GetThebesFontGroup()->GetFirstMathFont();
293 if (mathFont) {
294 raiseIndexPercent =
295 mathFont->GetMathConstant(gfxFontEntry::RadicalDegreeBottomRaisePercent);
297 nscoord raiseIndexDelta = NSToCoordRound(raiseIndexPercent *
298 (bmSqr.ascent + bmSqr.descent));
299 nscoord indexRaisedAscent = mBoundingMetrics.ascent // top of radical
300 - (bmSqr.ascent + bmSqr.descent) // to bottom of radical
301 + raiseIndexDelta + bmIndex.ascent + bmIndex.descent; // to top of raised index
303 nscoord indexClearance = 0;
304 if (mBoundingMetrics.ascent < indexRaisedAscent) {
305 indexClearance =
306 indexRaisedAscent - mBoundingMetrics.ascent; // excess gap introduced by a tall index
307 mBoundingMetrics.ascent = indexRaisedAscent;
308 nscoord descent = aDesiredSize.Height() - aDesiredSize.BlockStartAscent();
309 aDesiredSize.SetBlockStartAscent(mBoundingMetrics.ascent + leading);
310 aDesiredSize.Height() = aDesiredSize.BlockStartAscent() + descent;
313 nscoord dxIndex, dxSqr;
314 GetRadicalXOffsets(bmIndex.width, bmSqr.width, fm, &dxIndex, &dxSqr);
316 mBoundingMetrics.width = dxSqr + bmSqr.width + bmBase.width;
317 mBoundingMetrics.leftBearing =
318 std::min(dxIndex + bmIndex.leftBearing, dxSqr + bmSqr.leftBearing);
319 mBoundingMetrics.rightBearing = dxSqr + bmSqr.width +
320 std::max(bmBase.width, bmBase.rightBearing);
322 aDesiredSize.Width() = mBoundingMetrics.width;
323 aDesiredSize.mBoundingMetrics = mBoundingMetrics;
324 GatherAndStoreOverflow(&aDesiredSize);
326 // place the index
327 nscoord dx = dxIndex;
328 nscoord dy = aDesiredSize.BlockStartAscent() -
329 (indexRaisedAscent + indexSize.BlockStartAscent() - bmIndex.ascent);
330 FinishReflowChild(indexFrame, aPresContext, indexSize, nullptr,
331 MirrorIfRTL(aDesiredSize.Width(), indexSize.Width(), dx),
332 dy, 0);
334 // place the radical symbol and the radical bar
335 dx = dxSqr;
336 dy = indexClearance + leading; // leave a leading at the top
337 mSqrChar.SetRect(nsRect(MirrorIfRTL(aDesiredSize.Width(), bmSqr.width, dx),
338 dy, bmSqr.width, bmSqr.ascent + bmSqr.descent));
339 dx += bmSqr.width;
340 mBarRect.SetRect(MirrorIfRTL(aDesiredSize.Width(), bmBase.width, dx),
341 dy, bmBase.width, ruleThickness);
343 // place the base
344 dy = aDesiredSize.BlockStartAscent() - baseSize.BlockStartAscent();
345 FinishReflowChild(baseFrame, aPresContext, baseSize, nullptr,
346 MirrorIfRTL(aDesiredSize.Width(), baseSize.Width(), dx),
347 dy, 0);
349 mReference.x = 0;
350 mReference.y = aDesiredSize.BlockStartAscent();
352 aStatus = NS_FRAME_COMPLETE;
353 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
356 /* virtual */ void
357 nsMathMLmrootFrame::GetIntrinsicISizeMetrics(nsRenderingContext* aRenderingContext, nsHTMLReflowMetrics& aDesiredSize)
359 nsIFrame* baseFrame = mFrames.FirstChild();
360 nsIFrame* indexFrame = nullptr;
361 if (baseFrame)
362 indexFrame = baseFrame->GetNextSibling();
363 if (!indexFrame || indexFrame->GetNextSibling()) {
364 ReflowError(*aRenderingContext, aDesiredSize);
365 return;
368 nscoord baseWidth =
369 nsLayoutUtils::IntrinsicForContainer(aRenderingContext, baseFrame,
370 nsLayoutUtils::PREF_ISIZE);
371 nscoord indexWidth =
372 nsLayoutUtils::IntrinsicForContainer(aRenderingContext, indexFrame,
373 nsLayoutUtils::PREF_ISIZE);
374 nscoord sqrWidth = mSqrChar.GetMaxWidth(PresContext(), *aRenderingContext);
376 nscoord dxSqr;
377 nsRefPtr<nsFontMetrics> fm;
378 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
379 GetRadicalXOffsets(indexWidth, sqrWidth, fm, nullptr, &dxSqr);
381 nscoord width = dxSqr + sqrWidth + baseWidth;
383 aDesiredSize.Width() = width;
384 aDesiredSize.mBoundingMetrics.width = width;
385 aDesiredSize.mBoundingMetrics.leftBearing = 0;
386 aDesiredSize.mBoundingMetrics.rightBearing = width;
389 // ----------------------
390 // the Style System will use these to pass the proper style context to our MathMLChar
391 nsStyleContext*
392 nsMathMLmrootFrame::GetAdditionalStyleContext(int32_t aIndex) const
394 switch (aIndex) {
395 case NS_SQR_CHAR_STYLE_CONTEXT_INDEX:
396 return mSqrChar.GetStyleContext();
397 break;
398 default:
399 return nullptr;
403 void
404 nsMathMLmrootFrame::SetAdditionalStyleContext(int32_t aIndex,
405 nsStyleContext* aStyleContext)
407 switch (aIndex) {
408 case NS_SQR_CHAR_STYLE_CONTEXT_INDEX:
409 mSqrChar.SetStyleContext(aStyleContext);
410 break;