Bug Summary

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