Bug Summary

File:root/firefox-clang/obj-x86_64-pc-linux-gnu/layout/mathml/./../../../layout/mathml/nsMathMLmmultiscriptsFrame.cpp
Warning:line 245, column 11
Value stored to 'trySupScriptShift' during its initialization is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -O2 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name Unified_cpp_layout_mathml0.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -ffp-contract=off -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/layout/mathml -fcoverage-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/layout/mathml -resource-dir /usr/lib/llvm-23/lib/clang/23 -include /root/firefox-clang/config/gcc_hidden.h -include /root/firefox-clang/obj-x86_64-pc-linux-gnu/mozilla-config.h -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/stl_wrappers -D _GLIBCXX_ASSERTIONS=1 -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D DEBUG=1 -D MOZ_HAS_MOZGLUE -D MOZILLA_INTERNAL_API -D IMPL_LIBXUL -D MOZ_SUPPORT_LEAKCHECKING -D STATIC_EXPORTABLE_JS_API -I /root/firefox-clang/layout/mathml -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/layout/mathml -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/ipc/ipdl/_ipdlheaders -I /root/firefox-clang/ipc/chromium/src -I /root/firefox-clang/third_party/abseil-cpp -I /root/firefox-clang/toolkit/components/telemetry -I /root/firefox-clang/xpcom/base -I /root/firefox-clang/layout/base -I /root/firefox-clang/layout/generic -I /root/firefox-clang/layout/painting -I /root/firefox-clang/layout/style -I /root/firefox-clang/layout/tables -I /root/firefox-clang/layout/xul -I /root/firefox-clang/dom/base -I /root/firefox-clang/dom/mathml -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nspr -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nss -D MOZILLA_CLIENT -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/x86_64-linux-gnu/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16/backward -internal-isystem /usr/lib/llvm-23/lib/clang/23/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-error=pessimizing-move -Wno-error=large-by-value-copy=128 -Wno-error=implicit-int-float-conversion -Wno-error=thread-safety-analysis -Wno-error=tautological-type-limit-compare -Wno-invalid-offsetof -Wno-range-loop-analysis -Wno-deprecated-anon-enum-enum-conversion -Wno-deprecated-enum-enum-conversion -Wno-inline-new-delete -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=free-nonheap-object -Wno-error=atomic-alignment -Wno-error=deprecated-builtins -Wno-psabi -Wno-error=builtin-macro-redefined -Wno-vla-cxx-extension -Wno-unknown-warning-option -Wno-character-conversion -std=gnu++20 -fdeprecated-macro -ferror-limit 19 -fstrict-flex-arrays=1 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fno-rtti -fgnuc-version=4.2.1 -fno-implicit-modules -fskip-odr-check-in-gmf -fno-sized-deallocation -fno-aligned-allocation -fdiagnostics-absolute-paths -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -mllvm -dwarf-linkage-names=Abstract -faddrsig -fdwarf2-cfi-asm -o /tmp/scan-build-2026-09-01-224014-2642839-1 -x c++ Unified_cpp_layout_mathml0.cpp
1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5#include "nsMathMLmmultiscriptsFrame.h"
6
7#include <algorithm>
8
9#include "gfxContext.h"
10#include "gfxMathTable.h"
11#include "gfxTextRun.h"
12#include "mozilla/PresShell.h"
13#include "mozilla/StaticPrefs_mathml.h"
14#include "mozilla/dom/Document.h"
15#include "mozilla/dom/Element.h"
16#include "nsLayoutUtils.h"
17#include "nsPresContext.h"
18
19using namespace mozilla;
20
21//
22// <mmultiscripts> -- attach prescripts and tensor indices to a base -
23// implementation <msub> -- attach a subscript to a base - implementation
24// <msubsup> -- attach a subscript-superscript pair to a base - implementation
25// <msup> -- attach a superscript to a base - implementation
26//
27
28nsIFrame* NS_NewMathMLmmultiscriptsFrame(PresShell* aPresShell,
29 ComputedStyle* aStyle) {
30 return new (aPresShell)
31 nsMathMLmmultiscriptsFrame(aStyle, aPresShell->GetPresContext());
32}
33
34NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmmultiscriptsFrame)void* nsMathMLmmultiscriptsFrame ::operator new(size_t sz, mozilla
::PresShell * aShell) { return aShell->AllocateFrame(nsQueryFrame
::nsMathMLmmultiscriptsFrame_id, sz); }
35
36nsMathMLmmultiscriptsFrame::~nsMathMLmmultiscriptsFrame() = default;
37
38uint8_t nsMathMLmmultiscriptsFrame::ScriptIncrement(nsIFrame* aFrame) {
39 if (!aFrame) {
40 return 0;
41 }
42 if (mFrames.ContainsFrame(aFrame)) {
43 if (mFrames.FirstChild() == aFrame ||
44 aFrame->GetContent()->IsMathMLElement(nsGkAtoms::mprescripts)) {
45 return 0; // No script increment for base frames or prescript markers
46 }
47 return 1;
48 }
49 return 0; // not a child
50}
51
52NS_IMETHODIMPnsresult
53nsMathMLmmultiscriptsFrame::TransmitAutomaticData() {
54 // if our base is an embellished operator, let its state bubble to us
55 mPresentationData.baseFrame = mFrames.FirstChild();
56 GetEmbellishDataFrom(mPresentationData.baseFrame, mEmbellishData);
57
58 int32_t count = 0;
59 bool isSubScript = !mContent->IsMathMLElement(nsGkAtoms::msup);
60
61 nsIFrame* childFrame = mFrames.FirstChild();
62 while (childFrame) {
63 if (childFrame->GetContent()->IsMathMLElement(nsGkAtoms::mprescripts)) {
64 // mprescripts frame
65 } else if (0 == count) {
66 // base frame
67 } else {
68 // super/subscript block
69 PropagateFrameFlagFor(childFrame, NS_FRAME_MATHML_SCRIPT_DESCENDANT);
70 isSubScript = !isSubScript;
71 }
72 count++;
73 childFrame = childFrame->GetNextSibling();
74 }
75
76 return NS_OK;
77}
78
79/* virtual */
80void nsMathMLmmultiscriptsFrame::Place(DrawTarget* aDrawTarget,
81 const PlaceFlags& aFlags,
82 ReflowOutput& aDesiredSize) {
83 nscoord subScriptShift = 0;
84 nscoord supScriptShift = 0;
85 float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
86
87 return PlaceMultiScript(PresContext(), aDrawTarget, aFlags, aDesiredSize,
88 this, subScriptShift, supScriptShift,
89 fontSizeInflation);
90}
91
92// exported routine that both munderover and mmultiscripts share.
93// munderover uses this when movablelimits is set.
94void nsMathMLmmultiscriptsFrame::PlaceMultiScript(
95 nsPresContext* aPresContext, DrawTarget* aDrawTarget,
96 const PlaceFlags& aFlags, ReflowOutput& aDesiredSize,
97 nsMathMLContainerFrame* aFrame, nscoord aUserSubScriptShift,
98 nscoord aUserSupScriptShift, float aFontSizeInflation) {
99 nsAtom* tag = aFrame->GetContent()->NodeInfo()->NameAtom();
100
101 // This function deals with both munderover etc. as well as msubsup etc.
102 // As the former behaves identically to the later, we treat it as such
103 // to avoid additional checks later.
104 if (aFrame->GetContent()->IsMathMLElement(nsGkAtoms::mover)) {
105 tag = nsGkAtoms::msup;
106 } else if (aFrame->GetContent()->IsMathMLElement(nsGkAtoms::munder)) {
107 tag = nsGkAtoms::msub;
108 } else if (aFrame->GetContent()->IsMathMLElement(nsGkAtoms::munderover)) {
109 tag = nsGkAtoms::msubsup;
110 }
111
112 nsBoundingMetrics bmFrame;
113
114 nscoord minShiftFromXHeight, subDrop, supDrop;
115
116 ////////////////////////////////////////
117 // Initialize super/sub shifts that
118 // depend only on the current font
119 ////////////////////////////////////////
120
121 nsIFrame* baseFrame = aFrame->PrincipalChildList().FirstChild();
122
123 if (!baseFrame) {
124 if (tag == nsGkAtoms::mmultiscripts) {
125 aFrame->ReportErrorToConsole("NoBase");
126 } else {
127 aFrame->ReportChildCountError();
128 }
129 return aFrame->PlaceAsMrow(aDrawTarget, aFlags, aDesiredSize);
130 }
131
132 // get x-height (an ex)
133 const nsStyleFont* font = aFrame->StyleFont();
134 RefPtr<nsFontMetrics> fm =
135 nsLayoutUtils::GetFontMetricsForFrame(baseFrame, aFontSizeInflation);
136
137 nscoord xHeight = fm->XHeight();
138
139 nscoord oneDevPixel = fm->AppUnitsPerDevPixel();
140 RefPtr<gfxFont> mathFont = fm->GetThebesFontGroup()->GetFirstMathFont();
141 // scriptspace from TeX for extra spacing after sup/subscript
142 nscoord scriptSpace;
143 if (mathFont) {
144 scriptSpace = mathFont->MathTable()->Constant(
145 gfxMathTable::SpaceAfterScript, oneDevPixel);
146 } else {
147 // (0.5pt in plain TeX)
148 scriptSpace = nsPresContext::CSSPointsToAppUnits(0.5f);
149 }
150
151 // Try and read sub and sup drops from the MATH table.
152 if (mathFont) {
153 subDrop = mathFont->MathTable()->Constant(
154 gfxMathTable::SubscriptBaselineDropMin, oneDevPixel);
155 supDrop = mathFont->MathTable()->Constant(
156 gfxMathTable::SuperscriptBaselineDropMax, oneDevPixel);
157 }
158
159 // force the scriptSpace to be at least 1 pixel
160 nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
161 scriptSpace = std::max(onePixel, scriptSpace);
162
163 /////////////////////////////////////
164 // first the shift for the subscript
165
166 nscoord subScriptShift;
167 if (mathFont) {
168 // Try and get the sub script shift from the MATH table. Note that contrary
169 // to TeX we only have one parameter.
170 subScriptShift = mathFont->MathTable()->Constant(
171 gfxMathTable::SubscriptShiftDown, oneDevPixel);
172 } else {
173 // subScriptShift{1,2}
174 // = minimum amount to shift the subscript down
175 // = sub{1,2} in TeXbook
176 // subScriptShift1 = subscriptshift attribute * x-height
177 nscoord subScriptShift1, subScriptShift2;
178 // Get subScriptShift{1,2} default from font
179 GetSubScriptShifts(fm, subScriptShift1, subScriptShift2);
180 if (tag == nsGkAtoms::msub) {
181 subScriptShift = subScriptShift1;
182 } else {
183 subScriptShift = std::max(subScriptShift1, subScriptShift2);
184 }
185 }
186
187 if (0 < aUserSubScriptShift) {
188 // the user has set the subscriptshift attribute
189 subScriptShift = std::max(subScriptShift, aUserSubScriptShift);
190 }
191
192 /////////////////////////////////////
193 // next the shift for the superscript
194
195 nscoord supScriptShift;
196 nsPresentationData presentationData;
197 aFrame->GetPresentationData(presentationData);
198 bool compressed = font->mMathShift == StyleMathShift::Compact;
199 if (mathFont) {
200 // Try and get the super script shift from the MATH table. Note that
201 // contrary to TeX we only have two parameters.
202 supScriptShift = mathFont->MathTable()->Constant(
203 compressed ? gfxMathTable::SuperscriptShiftUpCramped
204 : gfxMathTable::SuperscriptShiftUp,
205 oneDevPixel);
206 } else {
207 // supScriptShift{1,2,3}
208 // = minimum amount to shift the supscript up
209 // = sup{1,2,3} in TeX
210 // supScriptShift1 = superscriptshift attribute * x-height
211 // Note that there are THREE values for supscript shifts depending
212 // on the current style
213 nscoord supScriptShift1, supScriptShift2, supScriptShift3;
214 // Set supScriptShift{1,2,3} default from font
215 GetSupScriptShifts(fm, supScriptShift1, supScriptShift2, supScriptShift3);
216
217 // get sup script shift depending on current script level and display style
218 // Rule 18c, App. G, TeXbook
219 if (font->mMathDepth == 0 && font->mMathStyle == StyleMathStyle::Normal &&
220 !compressed) {
221 // Style D in TeXbook
222 supScriptShift = supScriptShift1;
223 } else if (compressed) {
224 // Style C' in TeXbook = D',T',S',SS'
225 supScriptShift = supScriptShift3;
226 } else {
227 // everything else = T,S,SS
228 supScriptShift = supScriptShift2;
229 }
230 }
231
232 if (0 < aUserSupScriptShift) {
233 // the user has set the supscriptshift attribute
234 supScriptShift = std::max(supScriptShift, aUserSupScriptShift);
235 }
236
237 ////////////////////////////////////
238 // Get the children's sizes
239 ////////////////////////////////////
240
241 const WritingMode wm(aDesiredSize.GetWritingMode());
242 nscoord width = 0, prescriptsWidth = 0, rightBearing = 0;
243 nscoord minSubScriptShift = 0, minSupScriptShift = 0;
244 nscoord trySubScriptShift = subScriptShift;
245 nscoord trySupScriptShift = supScriptShift;
Value stored to 'trySupScriptShift' during its initialization is never read
246 nscoord maxSubScriptShift = subScriptShift;
247 nscoord maxSupScriptShift = supScriptShift;
248 ReflowOutput baseSize(wm);
249 ReflowOutput subScriptSize(wm);
250 ReflowOutput supScriptSize(wm);
251 ReflowOutput multiSubSize(wm), multiSupSize(wm);
252 baseFrame = nullptr;
253 nsIFrame* subScriptFrame = nullptr;
254 nsIFrame* supScriptFrame = nullptr;
255 nsIFrame* prescriptsFrame = nullptr; // frame of <mprescripts/>, if there.
256
257 bool firstPrescriptsPair = false;
258 nsBoundingMetrics bmBase, bmSubScript, bmSupScript, bmMultiSub, bmMultiSup;
259 nsMargin baseMargin, subScriptMargin, supScriptMargin;
260 multiSubSize.SetBlockStartAscent(-0x7FFFFFFF);
261 multiSupSize.SetBlockStartAscent(-0x7FFFFFFF);
262 bmMultiSub.ascent = bmMultiSup.ascent = -0x7FFFFFFF;
263 bmMultiSub.descent = bmMultiSup.descent = -0x7FFFFFFF;
264 nscoord italicCorrection = 0;
265 nscoord largeOpItalicCorrection = 0;
266
267 nsBoundingMetrics boundingMetrics;
268 boundingMetrics.width = 0;
269 boundingMetrics.ascent = boundingMetrics.descent = -0x7FFFFFFF;
270 aDesiredSize.Width() = aDesiredSize.Height() = 0;
271
272 int32_t count = 0;
273
274 // Boolean to determine whether the current child is a subscript.
275 // Note that only msup starts with a superscript.
276 bool isSubScript = (tag != nsGkAtoms::msup);
277
278 nsIFrame* childFrame = aFrame->PrincipalChildList().FirstChild();
279 while (childFrame) {
280 if (childFrame->GetContent()->IsMathMLElement(nsGkAtoms::mprescripts)) {
281 if (tag != nsGkAtoms::mmultiscripts) {
282 if (!aFlags.contains(PlaceFlag::MeasureOnly)) {
283 aFrame->ReportInvalidChildError(nsGkAtoms::mprescripts);
284 }
285 return aFrame->PlaceAsMrow(aDrawTarget, aFlags, aDesiredSize);
286 }
287 if (prescriptsFrame) {
288 // duplicate <mprescripts/> found
289 // report an error, encourage people to get their markups in order
290 if (!aFlags.contains(PlaceFlag::MeasureOnly)) {
291 aFrame->ReportErrorToConsole("DuplicateMprescripts");
292 }
293 return aFrame->PlaceAsMrow(aDrawTarget, aFlags, aDesiredSize);
294 }
295 if (!isSubScript) {
296 if (!aFlags.contains(PlaceFlag::MeasureOnly)) {
297 aFrame->ReportErrorToConsole("SubSupMismatch");
298 }
299 return aFrame->PlaceAsMrow(aDrawTarget, aFlags, aDesiredSize);
300 }
301
302 prescriptsFrame = childFrame;
303 firstPrescriptsPair = true;
304 } else if (0 == count) {
305 // base
306 baseFrame = childFrame;
307 GetReflowAndBoundingMetricsFor(baseFrame, baseSize, bmBase);
308 baseMargin = GetMarginForPlace(aFlags, baseFrame);
309
310 if (tag != nsGkAtoms::msub) {
311 // Apply italics correction if there is the potential for a
312 // postsupscript.
313 GetItalicCorrection(bmBase, italicCorrection);
314 // If italics correction is applied, we always add "a little to spare"
315 // (see TeXbook Ch.11, p.64), as we estimate the italic creation
316 // ourselves and it isn't the same as TeX.
317 italicCorrection += onePixel;
318 }
319
320 if (tag != nsGkAtoms::msup) {
321 // If the base is a largeop, determine its italic correction.
322 // https://w3c.github.io/mathml-core/#base-with-subscript
323 if (nsIMathMLFrame* mathMLFrame = do_QueryFrame(baseFrame)) {
324 nsEmbellishData baseFrameEmbellishData;
325 mathMLFrame->GetEmbellishData(baseFrameEmbellishData);
326 if (baseFrameEmbellishData.flags.contains(
327 MathMLEmbellishFlag::LargeOp)) {
328 largeOpItalicCorrection = mathMLFrame->ItalicCorrection();
329 }
330 }
331 }
332
333 // we update boundingMetrics.{ascent,descent} with that
334 // of the baseFrame only after processing all the sup/sub pairs
335 boundingMetrics.width = bmBase.width + baseMargin.LeftRight();
336 boundingMetrics.rightBearing =
337 bmBase.rightBearing + baseMargin.LeftRight();
338 boundingMetrics.leftBearing = bmBase.leftBearing; // until overwritten
339 } else {
340 // super/subscript block
341 if (isSubScript) {
342 // subscript
343 subScriptFrame = childFrame;
344 GetReflowAndBoundingMetricsFor(subScriptFrame, subScriptSize,
345 bmSubScript);
346 subScriptMargin = GetMarginForPlace(aFlags, subScriptFrame);
347
348 if (!mathFont) {
349 // get the subdrop from the subscript font
350 GetSubDropFromChild(subScriptFrame, subDrop, aFontSizeInflation);
351 }
352
353 // parameter v, Rule 18a, App. G, TeXbook
354 minSubScriptShift = bmBase.descent + baseMargin.bottom + subDrop;
355 trySubScriptShift = std::max(minSubScriptShift, subScriptShift);
356 multiSubSize.SetBlockStartAscent(
357 std::max(multiSubSize.BlockStartAscent(),
358 subScriptSize.BlockStartAscent() + subScriptMargin.top));
359 bmMultiSub.ascent = std::max(bmMultiSub.ascent,
360 bmSubScript.ascent + subScriptMargin.top);
361 bmMultiSub.descent = std::max(
362 bmMultiSub.descent, bmSubScript.descent + subScriptMargin.bottom);
363 multiSubSize.Height() =
364 std::max(multiSubSize.Height(),
365 subScriptSize.Height() - subScriptSize.BlockStartAscent() +
366 subScriptMargin.bottom);
367 if (bmSubScript.width) {
368 width = bmSubScript.width + subScriptMargin.LeftRight() + scriptSpace;
369 }
370 rightBearing = bmSubScript.rightBearing + subScriptMargin.LeftRight();
371
372 if (tag == nsGkAtoms::msub) {
373 boundingMetrics.rightBearing = boundingMetrics.width + rightBearing;
374 boundingMetrics.width += width;
375
376 nscoord subscriptTopMax;
377 if (mathFont) {
378 subscriptTopMax = mathFont->MathTable()->Constant(
379 gfxMathTable::SubscriptTopMax, oneDevPixel);
380 } else {
381 // get min subscript shift limit from x-height
382 // = h(x) - 4/5 * sigma_5, Rule 18b, App. G, TeXbook
383 subscriptTopMax = NSToCoordRound((4.0f / 5.0f) * xHeight);
384 }
385 nscoord minShiftFromXHeight =
386 bmSubScript.ascent + subScriptMargin.top - subscriptTopMax;
387 maxSubScriptShift = std::max(trySubScriptShift, minShiftFromXHeight);
388
389 maxSubScriptShift = std::max(maxSubScriptShift, trySubScriptShift);
390 trySubScriptShift = subScriptShift;
391 }
392 } else {
393 // supscript
394 supScriptFrame = childFrame;
395 GetReflowAndBoundingMetricsFor(supScriptFrame, supScriptSize,
396 bmSupScript);
397 supScriptMargin = GetMarginForPlace(aFlags, supScriptFrame);
398 if (!mathFont) {
399 // get the supdrop from the supscript font
400 GetSupDropFromChild(supScriptFrame, supDrop, aFontSizeInflation);
401 }
402 // parameter u, Rule 18a, App. G, TeXbook
403 minSupScriptShift = bmBase.ascent + baseMargin.top - supDrop;
404 nscoord superscriptBottomMin;
405 if (mathFont) {
406 superscriptBottomMin = mathFont->MathTable()->Constant(
407 gfxMathTable::SuperscriptBottomMin, oneDevPixel);
408 } else {
409 // get min supscript shift limit from x-height
410 // = d(x) + 1/4 * sigma_5, Rule 18c, App. G, TeXbook
411 superscriptBottomMin = NSToCoordRound((1.0f / 4.0f) * xHeight);
412 }
413 minShiftFromXHeight =
414 bmSupScript.descent + supScriptMargin.bottom + superscriptBottomMin;
415 trySupScriptShift = std::max(
416 minSupScriptShift, std::max(minShiftFromXHeight, supScriptShift));
417 multiSupSize.SetBlockStartAscent(
418 std::max(multiSupSize.BlockStartAscent(),
419 supScriptSize.BlockStartAscent() + supScriptMargin.top));
420 bmMultiSup.ascent = std::max(bmMultiSup.ascent,
421 bmSupScript.ascent + supScriptMargin.top);
422 bmMultiSup.descent = std::max(
423 bmMultiSup.descent, bmSupScript.descent + supScriptMargin.bottom);
424 multiSupSize.Height() =
425 std::max(multiSupSize.Height(),
426 supScriptSize.Height() - supScriptSize.BlockStartAscent() +
427 supScriptMargin.bottom);
428
429 if (bmSupScript.width) {
430 width =
431 std::max(width, bmSupScript.width + supScriptMargin.LeftRight() +
432 scriptSpace);
433 }
434
435 if (!prescriptsFrame) { // we are still looping over base & postscripts
436 rightBearing = std::max(rightBearing,
437 italicCorrection + bmSupScript.rightBearing +
438 supScriptMargin.LeftRight());
439 boundingMetrics.rightBearing = boundingMetrics.width + rightBearing;
440 boundingMetrics.width += width;
441 } else {
442 prescriptsWidth += width;
443 if (firstPrescriptsPair) {
444 firstPrescriptsPair = false;
445 boundingMetrics.leftBearing =
446 std::min(bmSubScript.leftBearing, bmSupScript.leftBearing);
447 }
448 }
449 width = rightBearing = 0;
450
451 // negotiate between the various shifts so that
452 // there is enough gap between the sup and subscripts
453 // Rule 18e, App. G, TeXbook
454 if (tag == nsGkAtoms::mmultiscripts || tag == nsGkAtoms::msubsup) {
455 nscoord subSuperscriptGapMin;
456 if (mathFont) {
457 subSuperscriptGapMin = mathFont->MathTable()->Constant(
458 gfxMathTable::SubSuperscriptGapMin, oneDevPixel);
459 } else {
460 nscoord ruleSize;
461 GetRuleThickness(aDrawTarget, fm, ruleSize);
462 subSuperscriptGapMin = 4 * ruleSize;
463 }
464 nscoord gap =
465 (trySupScriptShift - bmSupScript.descent -
466 supScriptMargin.bottom) -
467 (subScriptMargin.top + bmSubScript.ascent - trySubScriptShift);
468 if (gap < subSuperscriptGapMin) {
469 // adjust trySubScriptShift to get a gap of subSuperscriptGapMin
470 trySubScriptShift += subSuperscriptGapMin - gap;
471 }
472
473 // next we want to ensure that the bottom of the superscript
474 // will be > superscriptBottomMaxWithSubscript
475 nscoord superscriptBottomMaxWithSubscript;
476 if (mathFont) {
477 superscriptBottomMaxWithSubscript = mathFont->MathTable()->Constant(
478 gfxMathTable::SuperscriptBottomMaxWithSubscript, oneDevPixel);
479 } else {
480 superscriptBottomMaxWithSubscript =
481 NSToCoordRound((4.0f / 5.0f) * xHeight);
482 }
483 gap = superscriptBottomMaxWithSubscript -
484 (trySupScriptShift - bmSupScript.descent -
485 supScriptMargin.bottom);
486 if (gap > 0) {
487 trySupScriptShift += gap;
488 trySubScriptShift -= gap;
489 }
490 }
491
492 maxSubScriptShift = std::max(maxSubScriptShift, trySubScriptShift);
493 maxSupScriptShift = std::max(maxSupScriptShift, trySupScriptShift);
494
495 trySubScriptShift = subScriptShift;
496 trySupScriptShift = supScriptShift;
497 }
498
499 isSubScript = !isSubScript;
500 }
501 count++;
502 childFrame = childFrame->GetNextSibling();
503 }
504
505 // NoBase error may also have been reported above
506 if ((count != 2 && (tag == nsGkAtoms::msup || tag == nsGkAtoms::msub)) ||
507 (count != 3 && tag == nsGkAtoms::msubsup) || !baseFrame ||
508 (!isSubScript && tag == nsGkAtoms::mmultiscripts)) {
509 // report an error, encourage people to get their markups in order
510 if (!aFlags.contains(PlaceFlag::MeasureOnly)) {
511 if ((count != 2 && (tag == nsGkAtoms::msup || tag == nsGkAtoms::msub)) ||
512 (count != 3 && tag == nsGkAtoms::msubsup)) {
513 aFrame->ReportChildCountError();
514 } else if (!baseFrame) {
515 aFrame->ReportErrorToConsole("NoBase");
516 } else {
517 aFrame->ReportErrorToConsole("SubSupMismatch");
518 }
519 }
520 return aFrame->PlaceAsMrow(aDrawTarget, aFlags, aDesiredSize);
521 }
522
523 // we left out the width of prescripts, so ...
524 boundingMetrics.rightBearing += prescriptsWidth;
525 boundingMetrics.width += prescriptsWidth;
526
527 // Zero out the shifts in where a frame isn't present to avoid the potential
528 // for overflow.
529 if (!subScriptFrame) {
530 maxSubScriptShift = 0;
531 }
532 if (!supScriptFrame) {
533 maxSupScriptShift = 0;
534 }
535
536 // we left out the base during our bounding box updates, so ...
537 if (tag == nsGkAtoms::msub) {
538 boundingMetrics.ascent = std::max(bmBase.ascent + baseMargin.top,
539 bmMultiSub.ascent - maxSubScriptShift);
540 } else {
541 boundingMetrics.ascent = std::max(bmBase.ascent + baseMargin.top,
542 (bmMultiSup.ascent + maxSupScriptShift));
543 }
544 if (tag == nsGkAtoms::msup) {
545 boundingMetrics.descent = std::max(bmBase.descent + baseMargin.bottom,
546 bmMultiSup.descent - maxSupScriptShift);
547 } else {
548 boundingMetrics.descent =
549 std::max(bmBase.descent + baseMargin.bottom,
550 (bmMultiSub.descent + maxSubScriptShift));
551 }
552
553 // get the reflow metrics ...
554 aDesiredSize.SetBlockStartAscent(
555 std::max(baseSize.BlockStartAscent() + baseMargin.top,
556 std::max(multiSubSize.BlockStartAscent() - maxSubScriptShift,
557 multiSupSize.BlockStartAscent() + maxSupScriptShift)));
558 aDesiredSize.Height() =
559 aDesiredSize.BlockStartAscent() +
560 std::max(
561 baseSize.Height() - baseSize.BlockStartAscent() + baseMargin.bottom,
562 std::max(multiSubSize.Height() + maxSubScriptShift,
563 multiSupSize.Height() - maxSupScriptShift));
564 aDesiredSize.Width() = boundingMetrics.width;
565 aDesiredSize.mBoundingMetrics = boundingMetrics;
566
567 // Apply width/height to math content box.
568 auto sizes = aFrame->GetWidthAndHeightForPlaceAdjustment(aFlags);
569 aFrame->ApplyAdjustmentForWidthAndHeight(aFlags, sizes, aDesiredSize,
570 boundingMetrics);
571
572 // Add padding+border.
573 auto borderPadding = aFrame->GetBorderPaddingForPlace(aFlags);
574 InflateReflowAndBoundingMetrics(borderPadding, aDesiredSize, boundingMetrics);
575
576 aFrame->SetBoundingMetrics(boundingMetrics);
577 aFrame->SetReference(nsPoint(0, aDesiredSize.BlockStartAscent()));
578
579 //////////////////
580 // Place Children
581
582 // Place prescripts, followed by base, and then postscripts.
583 // The list of frames is in the order: {base} {postscripts} {prescripts}
584 // We go over the list in a circular manner, starting at <prescripts/>
585
586 if (!aFlags.contains(PlaceFlag::MeasureOnly)) {
587 const bool isRTL = aFrame->GetWritingMode().IsBidiRTL();
588 nscoord dx = isRTL ? borderPadding.right : borderPadding.left;
589 nscoord dy = 0;
590
591 // With msub and msup there is only one element and
592 // subscriptFrame/supScriptFrame have already been set above where
593 // relevant. In these cases we skip to the reflow part.
594 if (tag == nsGkAtoms::msub || tag == nsGkAtoms::msup) {
595 count = 1;
596 } else {
597 count = 0;
598 }
599 childFrame = prescriptsFrame;
600 bool isPreScript = true;
601 do {
602 if (!childFrame) { // end of prescripts,
603 isPreScript = false;
604 // place the base ...
605 childFrame = baseFrame;
606 dy = aDesiredSize.BlockStartAscent() - baseSize.BlockStartAscent();
607 baseMargin = GetMarginForPlace(aFlags, baseFrame);
608 nscoord dx_base = dx + (isRTL ? baseMargin.right : baseMargin.left);
609 FinishReflowChild(baseFrame, aPresContext, baseSize, nullptr,
610 aFrame->MirrorIfRTL(aDesiredSize.Width(),
611 baseSize.Width(), dx_base),
612 dy, ReflowChildFlags::Default);
613 if (prescriptsFrame) {
614 // place the <mprescripts/>
615 ReflowOutput prescriptsSize(wm);
616 nsBoundingMetrics unusedBm;
617 GetReflowAndBoundingMetricsFor(prescriptsFrame, prescriptsSize,
618 unusedBm);
619 nsMargin prescriptsMargin =
620 GetMarginForPlace(aFlags, prescriptsFrame);
621 nscoord dx_prescripts =
622 dx + (isRTL ? prescriptsMargin.right : prescriptsMargin.left);
623 dy = aDesiredSize.BlockStartAscent() -
624 prescriptsSize.BlockStartAscent();
625 FinishReflowChild(
626 prescriptsFrame, aPresContext, prescriptsSize, nullptr,
627 aFrame->MirrorIfRTL(aDesiredSize.Width(), prescriptsSize.Width(),
628 dx_prescripts),
629 dy, ReflowChildFlags::Default);
630 }
631 dx += bmBase.width + baseMargin.LeftRight();
632 } else if (childFrame != prescriptsFrame) {
633 // process each sup/sub pair
634 if (0 == count) {
635 subScriptFrame = childFrame;
636 count = 1;
637 } else if (1 == count) {
638 if (tag != nsGkAtoms::msub) {
639 supScriptFrame = childFrame;
640 }
641 count = 0;
642
643 // get the ascent/descent of sup/subscripts stored in their rects
644 // rect.x = descent, rect.y = ascent
645 if (subScriptFrame) {
646 GetReflowAndBoundingMetricsFor(subScriptFrame, subScriptSize,
647 bmSubScript);
648 subScriptMargin = GetMarginForPlace(aFlags, subScriptFrame);
649 }
650 if (supScriptFrame) {
651 GetReflowAndBoundingMetricsFor(supScriptFrame, supScriptSize,
652 bmSupScript);
653 supScriptMargin = GetMarginForPlace(aFlags, supScriptFrame);
654 }
655
656 width = std::max(subScriptSize.Width() + subScriptMargin.LeftRight(),
657 supScriptSize.Width() + supScriptMargin.LeftRight());
658
659 if (subScriptFrame) {
660 nscoord x =
661 dx + (isRTL ? subScriptMargin.right : subScriptMargin.left);
662 // prescripts should be right aligned
663 // https://bugzilla.mozilla.org/show_bug.cgi?id=928675
664 if (isPreScript) {
665 x += width - subScriptSize.Width() - subScriptMargin.LeftRight();
666 } else {
667 // post subscripts are shifted by the largeOpItalicCorrection
668 // value.
669 x -= largeOpItalicCorrection;
670 }
671 dy = aDesiredSize.BlockStartAscent() -
672 subScriptSize.BlockStartAscent() + maxSubScriptShift;
673 FinishReflowChild(subScriptFrame, aPresContext, subScriptSize,
674 nullptr,
675 aFrame->MirrorIfRTL(aDesiredSize.Width(),
676 subScriptSize.Width(), x),
677 dy, ReflowChildFlags::Default);
678 }
679
680 if (supScriptFrame) {
681 nscoord x =
682 dx + (isRTL ? supScriptMargin.right : supScriptMargin.left);
683 if (isPreScript) {
684 x += width - supScriptSize.Width() - supScriptMargin.LeftRight();
685 } else {
686 // post superscripts are shifted by the italic correction value
687 x += italicCorrection;
688 }
689 dy = aDesiredSize.BlockStartAscent() -
690 supScriptSize.BlockStartAscent() - maxSupScriptShift;
691 FinishReflowChild(supScriptFrame, aPresContext, supScriptSize,
692 nullptr,
693 aFrame->MirrorIfRTL(aDesiredSize.Width(),
694 supScriptSize.Width(), x),
695 dy, ReflowChildFlags::Default);
696 }
697 dx += width + scriptSpace;
698 }
699 }
700 childFrame = childFrame->GetNextSibling();
701 } while (prescriptsFrame != childFrame);
702 }
703}