Bug 785860 - fix sts preload list tests to skip private mode tests if private browsin...
[gecko.git] / layout / mathml / nsMathMLmrootFrame.cpp
blob8a9e1bdea19af07d8eb274ec7947472aeaade5cc
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/. */
7 #include "nsCOMPtr.h"
8 #include "nsFrame.h"
9 #include "nsPresContext.h"
10 #include "nsStyleContext.h"
11 #include "nsStyleConsts.h"
12 #include "nsRenderingContext.h"
14 #include "nsMathMLmrootFrame.h"
17 // <msqrt> and <mroot> -- form a radical - implementation
20 //NOTE:
21 // The code assumes that TeX fonts are picked.
22 // There is no fall-back to draw the branches of the sqrt explicitly
23 // in the case where TeX fonts are not there. In general, there are no
24 // fall-back(s) in MathML when some (freely-downloadable) fonts are missing.
25 // Otherwise, this will add much work and unnecessary complexity to the core
26 // MathML engine. Assuming that authors have the free fonts is part of the
27 // deal. We are not responsible for cases of misconfigurations out there.
29 // additional style context to be used by our MathMLChar.
30 #define NS_SQR_CHAR_STYLE_CONTEXT_INDEX 0
32 static const PRUnichar kSqrChar = PRUnichar(0x221A);
34 nsIFrame*
35 NS_NewMathMLmrootFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
37 return new (aPresShell) nsMathMLmrootFrame(aContext);
40 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmrootFrame)
42 nsMathMLmrootFrame::nsMathMLmrootFrame(nsStyleContext* aContext) :
43 nsMathMLContainerFrame(aContext),
44 mSqrChar(),
45 mBarRect()
49 nsMathMLmrootFrame::~nsMathMLmrootFrame()
53 NS_IMETHODIMP
54 nsMathMLmrootFrame::Init(nsIContent* aContent,
55 nsIFrame* aParent,
56 nsIFrame* aPrevInFlow)
58 nsresult rv = nsMathMLContainerFrame::Init(aContent, aParent, aPrevInFlow);
60 nsPresContext *presContext = PresContext();
62 // No need to track the style context given to our MathML char.
63 // The Style System will use Get/SetAdditionalStyleContext() to keep it
64 // up-to-date if dynamic changes arise.
65 nsAutoString sqrChar; sqrChar.Assign(kSqrChar);
66 mSqrChar.SetData(presContext, sqrChar);
67 ResolveMathMLCharStyle(presContext, mContent, mStyleContext, &mSqrChar, true);
69 return rv;
72 NS_IMETHODIMP
73 nsMathMLmrootFrame::TransmitAutomaticData()
75 // 1. The REC says:
76 // The <mroot> element increments scriptlevel by 2, and sets displaystyle to
77 // "false", within index, but leaves both attributes unchanged within base.
78 // 2. The TeXbook (Ch 17. p.141) says \sqrt is compressed
79 UpdatePresentationDataFromChildAt(1, 1,
80 ~NS_MATHML_DISPLAYSTYLE | NS_MATHML_COMPRESSED,
81 NS_MATHML_DISPLAYSTYLE | NS_MATHML_COMPRESSED);
82 UpdatePresentationDataFromChildAt(0, 0,
83 NS_MATHML_COMPRESSED, NS_MATHML_COMPRESSED);
85 return NS_OK;
88 NS_IMETHODIMP
89 nsMathMLmrootFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
90 const nsRect& aDirtyRect,
91 const nsDisplayListSet& aLists)
93 /////////////
94 // paint the content we are square-rooting
95 nsresult rv = nsMathMLContainerFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
96 NS_ENSURE_SUCCESS(rv, rv);
98 /////////////
99 // paint the sqrt symbol
100 if (!NS_MATHML_HAS_ERROR(mPresentationData.flags)) {
101 rv = mSqrChar.Display(aBuilder, this, aLists, 0);
102 NS_ENSURE_SUCCESS(rv, rv);
104 rv = DisplayBar(aBuilder, this, mBarRect, aLists);
105 NS_ENSURE_SUCCESS(rv, rv);
107 #if defined(DEBUG) && defined(SHOW_BOUNDING_BOX)
108 // for visual debug
109 nsRect rect;
110 mSqrChar.GetRect(rect);
111 nsBoundingMetrics bm;
112 mSqrChar.GetBoundingMetrics(bm);
113 rv = DisplayBoundingMetrics(aBuilder, this, rect.TopLeft(), bm, aLists);
114 #endif
117 return rv;
120 static void
121 GetRadicalXOffsets(nscoord aIndexWidth, nscoord aSqrWidth,
122 nsFontMetrics* aFontMetrics,
123 nscoord* aIndexOffset, nscoord* aSqrOffset)
125 // The index is tucked in closer to the radical while making sure
126 // that the kern does not make the index and radical collide
127 nscoord dxIndex, dxSqr;
128 nscoord xHeight = aFontMetrics->XHeight();
129 nscoord indexRadicalKern = NSToCoordRound(1.35f * xHeight);
130 if (indexRadicalKern > aIndexWidth) {
131 dxIndex = indexRadicalKern - aIndexWidth;
132 dxSqr = 0;
134 else {
135 dxIndex = 0;
136 dxSqr = aIndexWidth - indexRadicalKern;
138 // avoid collision by leaving a minimum space between index and radical
139 nscoord minimumClearance = aSqrWidth/2;
140 if (dxIndex + aIndexWidth + minimumClearance > dxSqr + aSqrWidth) {
141 if (aIndexWidth + minimumClearance < aSqrWidth) {
142 dxIndex = aSqrWidth - (aIndexWidth + minimumClearance);
143 dxSqr = 0;
145 else {
146 dxIndex = 0;
147 dxSqr = (aIndexWidth + minimumClearance) - aSqrWidth;
151 if (aIndexOffset)
152 *aIndexOffset = dxIndex;
153 if (aSqrOffset)
154 *aSqrOffset = dxSqr;
157 NS_IMETHODIMP
158 nsMathMLmrootFrame::Reflow(nsPresContext* aPresContext,
159 nsHTMLReflowMetrics& aDesiredSize,
160 const nsHTMLReflowState& aReflowState,
161 nsReflowStatus& aStatus)
163 nsresult rv = NS_OK;
164 nsSize availSize(aReflowState.ComputedWidth(), NS_UNCONSTRAINEDSIZE);
165 nsReflowStatus childStatus;
167 aDesiredSize.width = aDesiredSize.height = 0;
168 aDesiredSize.ascent = 0;
170 nsBoundingMetrics bmSqr, bmBase, bmIndex;
171 nsRenderingContext& renderingContext = *aReflowState.rendContext;
173 //////////////////
174 // Reflow Children
176 int32_t count = 0;
177 nsIFrame* baseFrame = nullptr;
178 nsIFrame* indexFrame = nullptr;
179 nsHTMLReflowMetrics baseSize;
180 nsHTMLReflowMetrics indexSize;
181 nsIFrame* childFrame = mFrames.FirstChild();
182 while (childFrame) {
183 // ask our children to compute their bounding metrics
184 nsHTMLReflowMetrics childDesiredSize(aDesiredSize.mFlags
185 | NS_REFLOW_CALC_BOUNDING_METRICS);
186 nsHTMLReflowState childReflowState(aPresContext, aReflowState,
187 childFrame, availSize);
188 rv = ReflowChild(childFrame, aPresContext,
189 childDesiredSize, childReflowState, childStatus);
190 //NS_ASSERTION(NS_FRAME_IS_COMPLETE(childStatus), "bad status");
191 if (NS_FAILED(rv)) {
192 // Call DidReflow() for the child frames we successfully did reflow.
193 DidReflowChildren(mFrames.FirstChild(), childFrame);
194 return rv;
196 if (0 == count) {
197 // base
198 baseFrame = childFrame;
199 baseSize = childDesiredSize;
200 bmBase = childDesiredSize.mBoundingMetrics;
202 else if (1 == count) {
203 // index
204 indexFrame = childFrame;
205 indexSize = childDesiredSize;
206 bmIndex = childDesiredSize.mBoundingMetrics;
208 count++;
209 childFrame = childFrame->GetNextSibling();
211 if (2 != count) {
212 // report an error, encourage people to get their markups in order
213 rv = ReflowError(renderingContext, aDesiredSize);
214 aStatus = NS_FRAME_COMPLETE;
215 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
216 // Call DidReflow() for the child frames we successfully did reflow.
217 DidReflowChildren(mFrames.FirstChild(), childFrame);
218 return rv;
221 ////////////
222 // Prepare the radical symbol and the overline bar
224 nsRefPtr<nsFontMetrics> fm;
225 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
226 renderingContext.SetFont(fm);
228 // For radical glyphs from TeX fonts and some of the radical glyphs from
229 // Mathematica fonts, the thickness of the overline can be obtained from the
230 // ascent of the glyph. Most fonts however have radical glyphs above the
231 // baseline so no assumption can be made about the meaning of the ascent.
232 nscoord ruleThickness, leading, em;
233 GetRuleThickness(renderingContext, fm, ruleThickness);
235 PRUnichar one = '1';
236 nsBoundingMetrics bmOne = renderingContext.GetBoundingMetrics(&one, 1);
238 // get the leading to be left at the top of the resulting frame
239 // this seems more reliable than using fm->GetLeading() on suspicious fonts
240 GetEmHeight(fm, em);
241 leading = nscoord(0.2f * em);
243 // Rule 11, App. G, TeXbook
244 // psi = clearance between rule and content
245 nscoord phi = 0, psi = 0;
246 if (NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags))
247 phi = fm->XHeight();
248 else
249 phi = ruleThickness;
250 psi = ruleThickness + phi/4;
252 // built-in: adjust clearance psi to emulate \mathstrut using '1' (TexBook, p.131)
253 if (bmOne.ascent > bmBase.ascent)
254 psi += bmOne.ascent - bmBase.ascent;
256 // make sure that the rule appears on on screen
257 nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
258 if (ruleThickness < onePixel) {
259 ruleThickness = onePixel;
262 // adjust clearance psi to get an exact number of pixels -- this
263 // gives a nicer & uniform look on stacked radicals (bug 130282)
264 nscoord delta = psi % onePixel;
265 if (delta)
266 psi += onePixel - delta; // round up
268 // Stretch the radical symbol to the appropriate height if it is not big enough.
269 nsBoundingMetrics contSize = bmBase;
270 contSize.descent = bmBase.ascent + bmBase.descent + psi;
271 contSize.ascent = ruleThickness;
273 // height(radical) should be >= height(base) + psi + ruleThickness
274 nsBoundingMetrics radicalSize;
275 mSqrChar.Stretch(aPresContext, renderingContext,
276 NS_STRETCH_DIRECTION_VERTICAL,
277 contSize, radicalSize,
278 NS_STRETCH_LARGER,
279 NS_MATHML_IS_RTL(mPresentationData.flags));
280 // radicalSize have changed at this point, and should match with
281 // the bounding metrics of the char
282 mSqrChar.GetBoundingMetrics(bmSqr);
284 // Update the desired size for the container (like msqrt, index is not yet included)
285 // the baseline will be that of the base.
286 mBoundingMetrics.ascent = bmBase.ascent + psi + ruleThickness;
287 mBoundingMetrics.descent =
288 NS_MAX(bmBase.descent,
289 (bmSqr.ascent + bmSqr.descent - mBoundingMetrics.ascent));
290 mBoundingMetrics.width = bmSqr.width + bmBase.width;
291 mBoundingMetrics.leftBearing = bmSqr.leftBearing;
292 mBoundingMetrics.rightBearing = bmSqr.width +
293 NS_MAX(bmBase.width, bmBase.rightBearing); // take also care of the rule
295 aDesiredSize.ascent = mBoundingMetrics.ascent + leading;
296 aDesiredSize.height = aDesiredSize.ascent +
297 NS_MAX(baseSize.height - baseSize.ascent,
298 mBoundingMetrics.descent + ruleThickness);
299 aDesiredSize.width = mBoundingMetrics.width;
301 /////////////
302 // Re-adjust the desired size to include the index.
304 // the index is raised by some fraction of the height
305 // of the radical, see \mroot macro in App. B, TexBook
306 nscoord raiseIndexDelta = NSToCoordRound(0.6f * (bmSqr.ascent + bmSqr.descent));
307 nscoord indexRaisedAscent = mBoundingMetrics.ascent // top of radical
308 - (bmSqr.ascent + bmSqr.descent) // to bottom of radical
309 + raiseIndexDelta + bmIndex.ascent + bmIndex.descent; // to top of raised index
311 nscoord indexClearance = 0;
312 if (mBoundingMetrics.ascent < indexRaisedAscent) {
313 indexClearance =
314 indexRaisedAscent - mBoundingMetrics.ascent; // excess gap introduced by a tall index
315 mBoundingMetrics.ascent = indexRaisedAscent;
316 nscoord descent = aDesiredSize.height - aDesiredSize.ascent;
317 aDesiredSize.ascent = mBoundingMetrics.ascent + leading;
318 aDesiredSize.height = aDesiredSize.ascent + descent;
321 nscoord dxIndex, dxSqr;
322 GetRadicalXOffsets(bmIndex.width, bmSqr.width, fm, &dxIndex, &dxSqr);
324 mBoundingMetrics.width = dxSqr + bmSqr.width + bmBase.width;
325 mBoundingMetrics.leftBearing =
326 NS_MIN(dxIndex + bmIndex.leftBearing, dxSqr + bmSqr.leftBearing);
327 mBoundingMetrics.rightBearing = dxSqr + bmSqr.width +
328 NS_MAX(bmBase.width, bmBase.rightBearing);
330 aDesiredSize.width = mBoundingMetrics.width;
331 aDesiredSize.mBoundingMetrics = mBoundingMetrics;
332 GatherAndStoreOverflow(&aDesiredSize);
334 // place the index
335 nscoord dx = dxIndex;
336 nscoord dy = aDesiredSize.ascent - (indexRaisedAscent + indexSize.ascent - bmIndex.ascent);
337 FinishReflowChild(indexFrame, aPresContext, nullptr, indexSize,
338 MirrorIfRTL(aDesiredSize.width, indexSize.width, dx),
339 dy, 0);
341 // place the radical symbol and the radical bar
342 dx = dxSqr;
343 dy = indexClearance + leading; // leave a leading at the top
344 mSqrChar.SetRect(nsRect(MirrorIfRTL(aDesiredSize.width, bmSqr.width, dx),
345 dy, bmSqr.width, bmSqr.ascent + bmSqr.descent));
346 dx += bmSqr.width;
347 mBarRect.SetRect(MirrorIfRTL(aDesiredSize.width, bmBase.width, dx),
348 dy, bmBase.width, ruleThickness);
350 // place the base
351 dy = aDesiredSize.ascent - baseSize.ascent;
352 FinishReflowChild(baseFrame, aPresContext, nullptr, baseSize,
353 MirrorIfRTL(aDesiredSize.width, baseSize.width, dx),
354 dy, 0);
356 mReference.x = 0;
357 mReference.y = aDesiredSize.ascent;
359 aStatus = NS_FRAME_COMPLETE;
360 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
361 return NS_OK;
364 /* virtual */ nscoord
365 nsMathMLmrootFrame::GetIntrinsicWidth(nsRenderingContext* aRenderingContext)
367 nsIFrame* baseFrame = mFrames.FirstChild();
368 nsIFrame* indexFrame = nullptr;
369 if (baseFrame)
370 indexFrame = baseFrame->GetNextSibling();
371 if (!indexFrame || indexFrame->GetNextSibling()) {
372 nsHTMLReflowMetrics desiredSize;
373 ReflowError(*aRenderingContext, desiredSize);
374 return desiredSize.width;
377 nscoord baseWidth =
378 nsLayoutUtils::IntrinsicForContainer(aRenderingContext, baseFrame,
379 nsLayoutUtils::PREF_WIDTH);
380 nscoord indexWidth =
381 nsLayoutUtils::IntrinsicForContainer(aRenderingContext, indexFrame,
382 nsLayoutUtils::PREF_WIDTH);
383 nscoord sqrWidth = mSqrChar.GetMaxWidth(PresContext(), *aRenderingContext);
385 nscoord dxSqr;
386 GetRadicalXOffsets(indexWidth, sqrWidth, aRenderingContext->FontMetrics(),
387 nullptr, &dxSqr);
389 return dxSqr + sqrWidth + baseWidth;
392 // ----------------------
393 // the Style System will use these to pass the proper style context to our MathMLChar
394 nsStyleContext*
395 nsMathMLmrootFrame::GetAdditionalStyleContext(int32_t aIndex) const
397 switch (aIndex) {
398 case NS_SQR_CHAR_STYLE_CONTEXT_INDEX:
399 return mSqrChar.GetStyleContext();
400 break;
401 default:
402 return nullptr;
406 void
407 nsMathMLmrootFrame::SetAdditionalStyleContext(int32_t aIndex,
408 nsStyleContext* aStyleContext)
410 switch (aIndex) {
411 case NS_SQR_CHAR_STYLE_CONTEXT_INDEX:
412 mSqrChar.SetStyleContext(aStyleContext);
413 break;