Bug Summary

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