Bug Summary

File:root/firefox-clang/obj-x86_64-pc-linux-gnu/layout/mathml/./../../../layout/mathml/nsMathMLmoFrame.cpp
Warning:line 288, column 15
Value stored to 'embellishAncestor' 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 "nsMathMLmoFrame.h"
6
7#include <algorithm>
8
9#include "gfxContext.h"
10#include "mozilla/PresShell.h"
11#include "mozilla/StaticPrefs_mathml.h"
12#include "mozilla/dom/Document.h"
13#include "mozilla/dom/MathMLElement.h"
14#include "nsCSSValue.h"
15#include "nsContentUtils.h"
16#include "nsFrameSelection.h"
17#include "nsGkAtoms.h"
18#include "nsLayoutUtils.h"
19#include "nsPresContext.h"
20
21using namespace mozilla;
22
23//
24// <mo> -- operator, fence, or separator - implementation
25//
26
27nsIFrame* NS_NewMathMLmoFrame(PresShell* aPresShell, ComputedStyle* aStyle) {
28 return new (aPresShell) nsMathMLmoFrame(aStyle, aPresShell->GetPresContext());
29}
30
31NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmoFrame)void* nsMathMLmoFrame ::operator new(size_t sz, mozilla::PresShell
* aShell) { return aShell->AllocateFrame(nsQueryFrame::nsMathMLmoFrame_id
, sz); }
32
33nsMathMLmoFrame::~nsMathMLmoFrame() = default;
34
35static const char16_t kApplyFunction = char16_t(0x2061);
36static const char16_t kInvisibleTimes = char16_t(0x2062);
37static const char16_t kInvisibleSeparator = char16_t(0x2063);
38static const char16_t kInvisiblePlus = char16_t(0x2064);
39
40MathMLFrameType nsMathMLmoFrame::GetMathMLFrameType() {
41 return mFlags.Booleans().contains(OperatorBoolean::Invisible)
42 ? MathMLFrameType::OperatorInvisible
43 : MathMLFrameType::OperatorOrdinary;
44}
45
46// since a mouse click implies selection, we cannot just rely on the
47// frame's state bit in our child text frame. So we will first check
48// its selected state bit, and use this little helper to double check.
49bool nsMathMLmoFrame::IsFrameInSelection(nsIFrame* aFrame) {
50 NS_ASSERTION(aFrame, "null arg")do { if (!(aFrame)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "null arg"
, "aFrame", "./../../../layout/mathml/nsMathMLmoFrame.cpp", 50
); MOZ_PretendNoReturn(); } } while (0)
;
51 if (!aFrame || !aFrame->IsSelected()) {
52 return false;
53 }
54
55 const nsFrameSelection* frameSelection = aFrame->GetConstFrameSelection();
56 UniquePtr<SelectionDetails> details = frameSelection->LookUpSelection(
57 aFrame->GetContent(), 0, 1,
58 aFrame->ShouldPaintNormalSelection()
59 ? nsFrameSelection::IgnoreNormalSelection::No
60 : nsFrameSelection::IgnoreNormalSelection::Yes);
61
62 return details != nullptr;
63}
64
65bool nsMathMLmoFrame::UseMathMLChar() {
66 return (mFlags.Form() != OperatorForm::Unknown &&
67 mFlags.Booleans().contains(OperatorBoolean::Mutable)) ||
68 mFlags.Booleans().contains(OperatorBoolean::ForcesMathMLChar);
69}
70
71void nsMathMLmoFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
72 const nsDisplayListSet& aLists) {
73 bool useMathMLChar = UseMathMLChar();
74
75 if (!useMathMLChar) {
76 // let the base class do everything
77 nsMathMLTokenFrame::BuildDisplayList(aBuilder, aLists);
78 } else {
79 DisplayBorderBackgroundOutline(aBuilder, aLists);
80
81 // make our char selected if our inner child text frame is selected
82 bool isSelected = false;
83 nsRect selectedRect;
84 nsIFrame* firstChild = mFrames.FirstChild();
85 if (IsFrameInSelection(firstChild)) {
86 mMathMLChar.GetRect(selectedRect);
87 // add a one pixel border (it renders better for operators like minus)
88 selectedRect.Inflate(nsPresContext::CSSPixelsToAppUnits(1));
89 isSelected = true;
90 }
91 mMathMLChar.Display(aBuilder, this, aLists, 0,
92 isSelected ? &selectedRect : nullptr);
93 }
94}
95
96// get the text that we enclose and setup our nsMathMLChar
97void nsMathMLmoFrame::ProcessTextData() {
98 mFlags.Clear();
99
100 nsAutoString data;
101 nsContentUtils::GetNodeTextContent(mContent, false, data);
102
103 data.CompressWhitespace();
104 int32_t length = data.Length();
105 char16_t ch = (length == 0) ? char16_t('\0') : data[0];
106
107 if ((length == 1) && (ch == kApplyFunction || ch == kInvisibleSeparator ||
108 ch == kInvisiblePlus || ch == kInvisibleTimes)) {
109 mFlags.Booleans() += OperatorBoolean::Invisible;
110 }
111
112 // don't bother doing anything special if we don't have a single child
113 if (mFrames.GetLength() != 1) {
114 data.Truncate(); // empty data to reset the char
115 mMathMLChar.SetData(data);
116 mMathMLChar.SetComputedStyle(Style());
117 return;
118 }
119
120 // special... in math mode, the usual minus sign '-' looks too short, so
121 // what we do here is to remap <mo>-</mo> to the official Unicode minus
122 // sign (U+2212) which looks much better. For background on this, see
123 // http://groups.google.com/groups?hl=en&th=66488daf1ade7635&rnum=1
124 if (1 == length && ch == '-') {
125 ch = 0x2212;
126 data = ch;
127 mFlags.Booleans() += OperatorBoolean::ForcesMathMLChar;
128 }
129
130 // cache the special bits: mutable, accent, movablelimits.
131 // we need to do this in anticipation of other requirements, and these
132 // bits don't change. Do not reset these bits unless the text gets changed.
133
134 // lookup all the forms under which the operator is listed in the dictionary,
135 // and record whether the operator has accent="true" or movablelimits="true"
136 OperatorBooleans allFlags;
137 for (const auto& form :
138 {OperatorForm::Infix, OperatorForm::Postfix, OperatorForm::Prefix}) {
139 nsOperatorFlags flags;
140 float dummy;
141 if (nsMathMLOperators::LookupOperator(data, form, &flags, &dummy, &dummy)) {
142 allFlags += flags.Booleans();
143 }
144 }
145
146 mFlags.Booleans() +=
147 allFlags &
148 OperatorBooleans({OperatorBoolean::Accent, OperatorBoolean::MovableLimits,
149 OperatorBoolean::LargeOperator});
150
151 // cache the operator
152 mMathMLChar.SetData(data);
153
154 // cache the native direction -- beware of bug 133429...
155 // mEmbellishData.direction must always retain our native direction, whereas
156 // mMathMLChar.GetStretchDirection() may change later, when Stretch() is
157 // called
158 mEmbellishData.direction = mMathMLChar.GetStretchDirection();
159
160 bool isMutable = allFlags.contains(OperatorBoolean::LargeOperator) ||
161 (mEmbellishData.direction != StretchDirection::Unsupported);
162 if (isMutable) {
163 mFlags.Booleans() += OperatorBoolean::Mutable;
164 }
165
166 mMathMLChar.SetComputedStyle(Style());
167}
168
169// get our 'form' and lookup in the Operator Dictionary to fetch
170// our default data that may come from there. Then complete our setup
171// using attributes that we may have. To stay in sync, this function is
172// called very often. We depend on many things that may change around us.
173// However, we re-use unchanged values.
174void nsMathMLmoFrame::ProcessOperatorData() {
175 // if we have been here before, we will just use our cached form
176 auto form = mFlags.Form();
177 nsAutoString value;
178 float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
179
180 // special bits are always kept in mFlags.
181 // remember the mutable bit from ProcessTextData().
182 // Some chars are listed under different forms in the dictionary,
183 // and there could be a form under which the char is mutable.
184 // If the char is the core of an embellished container, we will keep
185 // it mutable irrespective of the form of the embellished container.
186 // Also remember the other special bits that we want to carry forward.
187 mFlags.SetForm(OperatorForm::Unknown);
188 mFlags.SetDirection(OperatorDirection::Unknown);
189 mFlags.Booleans() &=
190 {OperatorBoolean::Mutable, OperatorBoolean::Accent,
191 OperatorBoolean::MovableLimits, OperatorBoolean::Invisible,
192 OperatorBoolean::ForcesMathMLChar, OperatorBoolean::LargeOperator};
193
194 if (!mEmbellishData.coreFrame) {
195 // i.e., we haven't been here before, the default form is infix
196 form = OperatorForm::Infix;
197
198 // reset everything so that we don't keep outdated values around
199 // in case of dynamic changes
200 mEmbellishData.flags.clear();
201 mEmbellishData.coreFrame = nullptr;
202 mEmbellishData.leadingSpace = 0;
203 mEmbellishData.trailingSpace = 0;
204 if (mMathMLChar.Length() != 1) {
205 mEmbellishData.direction = StretchDirection::Unsupported;
206 }
207 // else... retain the native direction obtained in ProcessTextData()
208
209 if (!mFrames.FirstChild()) {
210 return;
211 }
212
213 mEmbellishData.flags += MathMLEmbellishFlag::EmbellishedOperator;
214 mEmbellishData.coreFrame = this;
215
216 // there are two particular things that we also need to record so that if
217 // our parent is <mover>, <munder>, or <munderover>, they will treat us
218 // properly: 1) do we have accent="true" 2) do we have movablelimits="true"
219
220 // they need the extra information to decide how to treat their
221 // scripts/limits (note: <mover>, <munder>, or <munderover> need not
222 // necessarily be our direct parent -- case of embellished operators)
223
224 // default values from the Operator Dictionary were obtained in
225 // ProcessTextData() and these special bits are always kept in mFlags
226 if (mFlags.Booleans().contains(OperatorBoolean::Accent)) {
227 mEmbellishData.flags += MathMLEmbellishFlag::Accent;
228 }
229 if (mFlags.Booleans().contains(OperatorBoolean::MovableLimits)) {
230 mEmbellishData.flags += MathMLEmbellishFlag::MovableLimits;
231 }
232 if (mFlags.Booleans().contains(OperatorBoolean::LargeOperator)) {
233 mEmbellishData.flags += MathMLEmbellishFlag::LargeOp;
234 }
235
236 // see if the accent attribute is there
237 if (mContent->AsElement()->GetAttr(nsGkAtoms::accent, value)) {
238 [&]() {
239 AutoTArray<nsString, 2> params;
240 auto parentName = GetParent()->GetContent()->NodeInfo()->NameAtom();
241 if (parentName == nsGkAtoms::mover) {
242 params.AppendElement(u"accent");
243 params.AppendElement(u"mover");
244 } else if (parentName == nsGkAtoms::munder) {
245 params.AppendElement(u"accentunder");
246 params.AppendElement(u"munder");
247 } else if (parentName == nsGkAtoms::munderover) {
248 params.AppendElement(u"accent/accentunder");
249 params.AppendElement(u"munderover");
250 } else {
251 return;
252 }
253 PresContext()->Document()->WarnOnceAndReportAbout(
254 dom::DeprecatedOperations::eMathML_DeprecatedMoExplicitAccent,
255 false, params);
256 }();
257 if (value.LowerCaseEqualsLiteral("true")) {
258 mEmbellishData.flags += MathMLEmbellishFlag::Accent;
259 } else if (value.LowerCaseEqualsLiteral("false")) {
260 mEmbellishData.flags -= MathMLEmbellishFlag::Accent;
261 }
262 }
263
264 // see if the movablelimits attribute is there
265 mContent->AsElement()->GetAttr(nsGkAtoms::movablelimits, value);
266 if (value.LowerCaseEqualsLiteral("true")) {
267 mEmbellishData.flags += MathMLEmbellishFlag::MovableLimits;
268 } else if (value.LowerCaseEqualsLiteral("false")) {
269 mEmbellishData.flags -= MathMLEmbellishFlag::MovableLimits;
270 }
271
272 // ---------------------------------------------------------------------
273 // we will be called again to re-sync the rest of our state next time...
274 // (nobody needs the other values below at this stage)
275 mFlags.SetForm(form);
276 return;
277 }
278
279 // beware of bug 133814 - there is a two-way dependency in the
280 // embellished hierarchy: our embellished ancestors need to set
281 // their flags based on some of our state (set above), and here we
282 // need to re-sync our 'form' depending on our outermost embellished
283 // container. A null form here means that an earlier attempt to stretch
284 // our mMathMLChar failed, in which case we don't bother re-stretching again
285 if (form != OperatorForm::Unknown) {
286 // get our outermost embellished container and its parent.
287 // (we ensure that we are the core, not just a sibling of the core)
288 nsIFrame* embellishAncestor = this;
Value stored to 'embellishAncestor' during its initialization is never read
289 nsEmbellishData embellishData;
290 nsIFrame* parentAncestor = this;
291 do {
292 embellishAncestor = parentAncestor;
293 parentAncestor = embellishAncestor->GetParent();
294 GetEmbellishDataFrom(parentAncestor, embellishData);
295 } while (embellishData.coreFrame == this);
296
297 // flag if we have an embellished ancestor
298 if (embellishAncestor != this) {
299 mFlags.Booleans() += OperatorBoolean::HasEmbellishAncestor;
300 } else {
301 mFlags.Booleans() -= OperatorBoolean::HasEmbellishAncestor;
302 }
303
304 // find the position of our outermost embellished container w.r.t
305 // its siblings.
306
307 nsIFrame* nextSibling = embellishAncestor->GetNextSibling();
308 nsIFrame* prevSibling = embellishAncestor->GetPrevSibling();
309
310 // flag to distinguish from a real infix. Set for (embellished) operators
311 // that live in (inferred) mrows.
312 nsIMathMLFrame* mathAncestor = do_QueryFrame(parentAncestor);
313 bool zeroSpacing = false;
314 if (mathAncestor) {
315 zeroSpacing = !mathAncestor->IsMrowLike();
316 } else {
317 nsMathMLmathBlockFrame* blockFrame = do_QueryFrame(parentAncestor);
318 if (blockFrame) {
319 zeroSpacing = !blockFrame->IsMrowLike();
320 }
321 }
322 if (zeroSpacing) {
323 mFlags.Booleans() += OperatorBoolean::EmbellishIsIsolated;
324 } else {
325 mFlags.Booleans() -= OperatorBoolean::EmbellishIsIsolated;
326 }
327
328 // find our form
329 form = OperatorForm::Infix;
330 mContent->AsElement()->GetAttr(nsGkAtoms::form, value);
331 if (!value.IsEmpty()) {
332 if (value.EqualsLiteral("prefix")) {
333 form = OperatorForm::Prefix;
334 } else if (value.EqualsLiteral("postfix")) {
335 form = OperatorForm::Postfix;
336 }
337 } else {
338 // set our form flag depending on the position
339 if (!prevSibling && nextSibling) {
340 form = OperatorForm::Prefix;
341 } else if (prevSibling && !nextSibling) {
342 form = OperatorForm::Postfix;
343 }
344 }
345 mFlags.SetForm(form);
346
347 // Use the default value suggested by the MathML REC.
348 // http://www.w3.org/TR/MathML/chapter3.html#presm.mo.attrs
349 // thickmathspace = 5/18em
350 float lspace = 5.0f / 18.0f;
351 float rspace = 5.0f / 18.0f;
352 // lookup the operator dictionary
353 nsAutoString data;
354 mMathMLChar.GetData(data);
355 nsOperatorFlags flags;
356 if (nsMathMLOperators::LookupOperatorWithFallback(data, form, &flags,
357 &lspace, &rspace)) {
358 mFlags.SetForm(flags.Form());
359 mFlags.SetDirection(flags.Direction());
360 mFlags.Booleans() +=
361 flags.Booleans(); // just add bits without overwriting
362 }
363
364 // Spacing is zero if our outermost embellished operator is not in an
365 // inferred mrow.
366 if (!mFlags.Booleans().contains(OperatorBoolean::EmbellishIsIsolated) &&
367 (lspace || rspace)) {
368 // Cache the default values of lspace and rspace.
369 // since these values are relative to the 'em' unit, convert to twips now
370 nscoord em;
371 RefPtr<nsFontMetrics> fm =
372 nsLayoutUtils::GetFontMetricsForFrame(this, fontSizeInflation);
373 GetEmHeight(fm, em);
374
375 mEmbellishData.leadingSpace = NSToCoordRound(lspace * em);
376 mEmbellishData.trailingSpace = NSToCoordRound(rspace * em);
377
378 // tuning if we don't want too much extra space when we are a script.
379 // (with its fonts, TeX sets lspace=0 & rspace=0 as soon as scriptlevel>0.
380 // Our fonts can be anything, so...)
381 if (!StaticPrefs::
382 mathml_adjust_default_lspace_rspace_for_positive_scriptlevel_disabled() &&
383 StyleFont()->mMathDepth > 0 &&
384 !mFlags.Booleans().contains(OperatorBoolean::HasEmbellishAncestor)) {
385 mEmbellishData.leadingSpace /= 2;
386 mEmbellishData.trailingSpace /= 2;
387 }
388 }
389 }
390
391 // If we are an accent without explicit lspace="." or rspace=".",
392 // we will ignore our default leading/trailing space
393
394 // lspace
395 //
396 // "Specifies the leading space appearing before the operator"
397 //
398 // values: length
399 // default: set by dictionary (thickmathspace)
400 //
401 // XXXfredw Support for negative and relative values is not implemented
402 // (bug 805926).
403 // Relative values will give a multiple of the current leading space,
404 // which is not necessarily the default one.
405 //
406 nscoord leadingSpace = mEmbellishData.leadingSpace;
407 mContent->AsElement()->GetAttr(nsGkAtoms::lspace, value);
408 if (!value.IsEmpty()) {
409 nsCSSValue cssValue;
410 if (dom::MathMLElement::ParseNumericValue(value, cssValue,
411 mContent->OwnerDoc())) {
412 if ((eCSSUnit_Number == cssValue.GetUnit()) &&
413 !cssValue.GetFloatValue()) {
414 leadingSpace = 0;
415 } else if (cssValue.IsLengthUnit()) {
416 leadingSpace = CalcLength(cssValue, fontSizeInflation, this);
417 }
418 mFlags.Booleans() += OperatorBoolean::HasLSpaceAttribute;
419 }
420 }
421
422 // rspace
423 //
424 // "Specifies the trailing space appearing after the operator"
425 //
426 // values: length
427 // default: set by dictionary (thickmathspace)
428 //
429 // XXXfredw Support for negative and relative values is not implemented
430 // (bug 805926).
431 // Relative values will give a multiple of the current leading space,
432 // which is not necessarily the default one.
433 //
434 nscoord trailingSpace = mEmbellishData.trailingSpace;
435 mContent->AsElement()->GetAttr(nsGkAtoms::rspace, value);
436 if (!value.IsEmpty()) {
437 nsCSSValue cssValue;
438 if (dom::MathMLElement::ParseNumericValue(value, cssValue,
439 mContent->OwnerDoc())) {
440 if ((eCSSUnit_Number == cssValue.GetUnit()) &&
441 !cssValue.GetFloatValue()) {
442 trailingSpace = 0;
443 } else if (cssValue.IsLengthUnit()) {
444 trailingSpace = CalcLength(cssValue, fontSizeInflation, this);
445 }
446 mFlags.Booleans() += OperatorBoolean::HasRSpaceAttribute;
447 }
448 }
449
450 // little extra tuning to round lspace & rspace to at least a pixel so that
451 // operators don't look as if they are colliding with their operands
452 if (leadingSpace || trailingSpace) {
453 nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
454 if (leadingSpace && leadingSpace < onePixel) {
455 leadingSpace = onePixel;
456 }
457 if (trailingSpace && trailingSpace < onePixel) {
458 trailingSpace = onePixel;
459 }
460 }
461
462 // the values that we get from our attributes override the dictionary
463 mEmbellishData.leadingSpace = leadingSpace;
464 mEmbellishData.trailingSpace = trailingSpace;
465
466 // Now see if there are user-defined attributes that override the dictionary.
467 // XXX Bug 1197771 - forcing an attribute to true when it is false in the
468 // dictionary can cause conflicts in the rest of the stretching algorithms
469 // (e.g. all largeops are assumed to have a vertical direction)
470
471 // For each attribute overriden by the user, turn off its bit flag.
472 // symmetric|movablelimits|separator|largeop|accent|fence|stretchy|form
473 // special: accent and movablelimits are handled above,
474 // don't process them here
475
476 mContent->AsElement()->GetAttr(nsGkAtoms::stretchy, value);
477 if (value.LowerCaseEqualsLiteral("false")) {
478 mFlags.Booleans() -= OperatorBoolean::Stretchy;
479 } else if (value.LowerCaseEqualsLiteral("true")) {
480 mFlags.Booleans() += OperatorBoolean::Stretchy;
481 }
482 if (mFlags.Booleans().contains(OperatorBoolean::Fence)) {
483 mContent->AsElement()->GetAttr(nsGkAtoms::fence, value);
484 if (value.LowerCaseEqualsLiteral("false")) {
485 mFlags.Booleans() -= OperatorBoolean::Fence;
486 } else {
487 mEmbellishData.flags += MathMLEmbellishFlag::Fence;
488 }
489 }
490 mContent->AsElement()->GetAttr(nsGkAtoms::largeop, value);
491 if (value.LowerCaseEqualsLiteral("false")) {
492 mFlags.Booleans() -= OperatorBoolean::LargeOperator;
493 mEmbellishData.flags -= MathMLEmbellishFlag::LargeOp;
494 } else if (value.LowerCaseEqualsLiteral("true")) {
495 mFlags.Booleans() += OperatorBoolean::LargeOperator;
496 mEmbellishData.flags += MathMLEmbellishFlag::LargeOp;
497 }
498 if (mFlags.Booleans().contains(OperatorBoolean::Separator)) {
499 mContent->AsElement()->GetAttr(nsGkAtoms::separator, value);
500 if (value.LowerCaseEqualsLiteral("false")) {
501 mFlags.Booleans() -= OperatorBoolean::Separator;
502 } else {
503 mEmbellishData.flags += MathMLEmbellishFlag::Separator;
504 }
505 }
506 mContent->AsElement()->GetAttr(nsGkAtoms::symmetric, value);
507 if (value.LowerCaseEqualsLiteral("false")) {
508 mFlags.Booleans() -= OperatorBoolean::Symmetric;
509 } else if (value.LowerCaseEqualsLiteral("true")) {
510 mFlags.Booleans() += OperatorBoolean::Symmetric;
511 }
512
513 // minsize
514 //
515 // "Specifies the minimum size of the operator when stretchy"
516 //
517 // values: length
518 // default: set by dictionary (1em)
519 //
520 // We don't allow negative values.
521 // Note: Contrary to other "length" values, unitless and percentage do not
522 // give a multiple of the defaut value but a multiple of the operator at
523 // normal size.
524 //
525 mMinSize = 0;
526 mContent->AsElement()->GetAttr(nsGkAtoms::minsize, value);
527 if (!value.IsEmpty()) {
528 nsCSSValue cssValue;
529 if (dom::MathMLElement::ParseNumericValue(value, cssValue,
530 mContent->OwnerDoc())) {
531 nsCSSUnit unit = cssValue.GetUnit();
532 if (eCSSUnit_Number == unit) {
533 mMinSize = cssValue.GetFloatValue();
534 } else if (eCSSUnit_Percent == unit) {
535 mMinSize = cssValue.GetPercentValue();
536 } else if (eCSSUnit_Null != unit) {
537 mMinSize = float(CalcLength(cssValue, fontSizeInflation, this));
538 mFlags.Booleans() += OperatorBoolean::MinSizeIsAbsolute;
539 }
540 }
541 }
542
543 // maxsize
544 //
545 // "Specifies the maximum size of the operator when stretchy"
546 //
547 // values: length | "infinity"
548 // default: set by dictionary (infinity)
549 //
550 // We don't allow negative values.
551 // Note: Contrary to other "length" values, unitless and percentage do not
552 // give a multiple of the defaut value but a multiple of the operator at
553 // normal size.
554 //
555 mMaxSize = kMathMLOperatorSizeInfinity;
556 mContent->AsElement()->GetAttr(nsGkAtoms::maxsize, value);
557 if (!value.IsEmpty()) {
558 nsCSSValue cssValue;
559 if (dom::MathMLElement::ParseNumericValue(value, cssValue,
560 mContent->OwnerDoc())) {
561 nsCSSUnit unit = cssValue.GetUnit();
562 if (eCSSUnit_Number == unit) {
563 mMaxSize = cssValue.GetFloatValue();
564 } else if (eCSSUnit_Percent == unit) {
565 mMaxSize = cssValue.GetPercentValue();
566 } else if (eCSSUnit_Null != unit) {
567 mMaxSize = float(CalcLength(cssValue, fontSizeInflation, this));
568 mFlags.Booleans() += OperatorBoolean::MaxSizeIsAbsolute;
569 }
570 }
571 }
572}
573
574static MathMLStretchFlags GetStretchFlags(nsOperatorFlags aFlags,
575 nsPresentationData aPresentationData,
576 bool aIsVertical,
577 const nsStyleFont* aStyleFont) {
578 MathMLStretchFlags stretchFlags;
579 // See if it is okay to stretch,
580 // starting from what the Operator Dictionary said
581 if (aFlags.Booleans().contains(OperatorBoolean::Mutable)) {
582 // set the largeop or largeopOnly flags to suitably cover all the
583 // 8 possible cases depending on whether displaystyle, largeop,
584 // stretchy are true or false (see bug 69325).
585 // . largeopOnly is taken if largeop=true and stretchy=false
586 // . largeop is taken if largeop=true and stretchy=true
587 if (aStyleFont->mMathStyle == StyleMathStyle::Normal &&
588 aFlags.Booleans().contains(OperatorBoolean::LargeOperator)) {
589 stretchFlags =
590 MathMLStretchFlag::LargeOperator; // (largeopOnly, not mask!)
591 if (aFlags.Booleans().contains(OperatorBoolean::Stretchy)) {
592 stretchFlags += MathMLStretchFlag::Nearer;
593 stretchFlags += MathMLStretchFlag::Larger;
594 }
595 } else if (aFlags.Booleans().contains(OperatorBoolean::Stretchy)) {
596 if (aIsVertical) {
597 // TeX hint. Can impact some sloppy markups missing <mrow></mrow>
598 stretchFlags = MathMLStretchFlag::Nearer;
599 } else {
600 stretchFlags = MathMLStretchFlag::Normal;
601 }
602 }
603 // else if the stretchy and largeop attributes have been disabled,
604 // the operator is not mutable
605 }
606 return stretchFlags;
607}
608
609// NOTE: aDesiredStretchSize is an IN/OUT parameter
610// On input - it contains our current size
611// On output - the same size or the new size that we want
612NS_IMETHODIMPnsresult
613nsMathMLmoFrame::Stretch(DrawTarget* aDrawTarget,
614 StretchDirection aStretchDirection,
615 nsBoundingMetrics& aContainerSize,
616 ReflowOutput& aDesiredStretchSize) {
617 if (mPresentationData.flags.contains(MathMLPresentationFlag::StretchDone)) {
618 NS_WARNING("it is wrong to fire stretch more than once on a frame")NS_DebugBreak(NS_DEBUG_WARNING, "it is wrong to fire stretch more than once on a frame"
, nullptr, "./../../../layout/mathml/nsMathMLmoFrame.cpp", 618
)
;
619 return NS_OK;
620 }
621 mPresentationData.flags += MathMLPresentationFlag::StretchDone;
622
623 nsIFrame* firstChild = mFrames.FirstChild();
624
625 // get the axis height;
626 float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
627 RefPtr<nsFontMetrics> fm =
628 nsLayoutUtils::GetFontMetricsForFrame(this, fontSizeInflation);
629 nscoord axisHeight, height;
630 GetAxisHeight(aDrawTarget, fm, axisHeight);
631
632 // Operators that are stretchy are handled by the MathMLChar
633 // ('form' is reset if stretch fails -- i.e., we don't bother to stretch next
634 // time)
635 bool useMathMLChar = UseMathMLChar();
636
637 nsBoundingMetrics charSize;
638 nsBoundingMetrics container = aDesiredStretchSize.mBoundingMetrics;
639 bool isVertical = false;
640
641 if (((aStretchDirection == StretchDirection::Vertical) ||
642 (aStretchDirection == StretchDirection::Default)) &&
643 (mEmbellishData.direction == StretchDirection::Vertical)) {
644 isVertical = true;
645 }
646
647 auto stretchFlags =
648 GetStretchFlags(mFlags, mPresentationData, isVertical, StyleFont());
649
650 if (useMathMLChar) {
651 nsBoundingMetrics initialSize = aDesiredStretchSize.mBoundingMetrics;
652
653 if (!stretchFlags.isEmpty()) {
654 container = aContainerSize;
655
656 // some adjustments if the operator is symmetric and vertical
657
658 if (isVertical &&
659 mFlags.Booleans().contains(OperatorBoolean::Symmetric)) {
660 // we need to center about the axis
661 nscoord delta = std::max(container.ascent - axisHeight,
662 container.descent + axisHeight);
663 container.ascent = delta + axisHeight;
664 container.descent = delta - axisHeight;
665
666 // get ready in case we encounter user-desired min-max size
667 delta = std::max(initialSize.ascent - axisHeight,
668 initialSize.descent + axisHeight);
669 initialSize.ascent = delta + axisHeight;
670 initialSize.descent = delta - axisHeight;
671 }
672
673 // check for user-desired min-max size
674
675 if (mMaxSize != kMathMLOperatorSizeInfinity && mMaxSize > 0.0f) {
676 // if we are here, there is a user defined maxsize ...
677 // XXX Set stretchFlags = MathMLStretchFlag::Normal? to honor the
678 // maxsize as close as possible?
679 if (mFlags.Booleans().contains(OperatorBoolean::MaxSizeIsAbsolute)) {
680 // there is an explicit value like maxsize="20pt"
681 // try to maintain the aspect ratio of the char
682 float aspect =
683 mMaxSize / float(initialSize.ascent + initialSize.descent);
684 container.ascent =
685 std::min(container.ascent, nscoord(initialSize.ascent * aspect));
686 container.descent = std::min(container.descent,
687 nscoord(initialSize.descent * aspect));
688 // below we use a type cast instead of a conversion to avoid a VC++
689 // bug see
690 // http://support.microsoft.com/support/kb/articles/Q115/7/05.ASP
691 container.width = std::min(container.width, (nscoord)mMaxSize);
692 } else { // multiplicative value
693 container.ascent = std::min(container.ascent,
694 nscoord(initialSize.ascent * mMaxSize));
695 container.descent = std::min(container.descent,
696 nscoord(initialSize.descent * mMaxSize));
697 container.width =
698 std::min(container.width, nscoord(initialSize.width * mMaxSize));
699 }
700
701 if (isVertical &&
702 !mFlags.Booleans().contains(OperatorBoolean::Symmetric)) {
703 // re-adjust to align the char with the bottom of the initial
704 // container
705 height = container.ascent + container.descent;
706 container.descent = aContainerSize.descent;
707 container.ascent = height - container.descent;
708 }
709 }
710
711 if (mMinSize > 0.0f) {
712 // if we are here, there is a user defined minsize ...
713 // always allow the char to stretch in its natural direction,
714 // even if it is different from the caller's direction
715 if (aStretchDirection != StretchDirection::Default &&
716 aStretchDirection != mEmbellishData.direction) {
717 aStretchDirection = StretchDirection::Default;
718 // but when we are not honoring the requested direction
719 // we should not use the caller's container size either
720 container = initialSize;
721 }
722 if (mFlags.Booleans().contains(OperatorBoolean::MinSizeIsAbsolute)) {
723 // there is an explicit value like minsize="20pt"
724 // try to maintain the aspect ratio of the char
725 float aspect =
726 mMinSize / float(initialSize.ascent + initialSize.descent);
727 container.ascent =
728 std::max(container.ascent, nscoord(initialSize.ascent * aspect));
729 container.descent = std::max(container.descent,
730 nscoord(initialSize.descent * aspect));
731 container.width = std::max(container.width, (nscoord)mMinSize);
732 } else { // multiplicative value
733 container.ascent = std::max(container.ascent,
734 nscoord(initialSize.ascent * mMinSize));
735 container.descent = std::max(container.descent,
736 nscoord(initialSize.descent * mMinSize));
737 container.width =
738 std::max(container.width, nscoord(initialSize.width * mMinSize));
739 }
740
741 if (isVertical &&
742 !mFlags.Booleans().contains(OperatorBoolean::Symmetric)) {
743 // re-adjust to align the char with the bottom of the initial
744 // container
745 height = container.ascent + container.descent;
746 container.descent = aContainerSize.descent;
747 container.ascent = height - container.descent;
748 }
749 }
750 }
751
752 // let the MathMLChar stretch itself...
753 nsresult res = mMathMLChar.Stretch(
754 this, aDrawTarget, fontSizeInflation, aStretchDirection, container,
755 charSize, stretchFlags, GetWritingMode().IsBidiRTL());
756 if (NS_FAILED(res)((bool)(__builtin_expect(!!(NS_FAILED_impl(res)), 0)))) {
757 // gracefully handle cases where stretching the char failed (i.e.,
758 // GetBoundingMetrics failed) clear our 'form' to behave as if the
759 // operator wasn't in the dictionary
760 mFlags.SetForm(OperatorForm::Unknown);
761 useMathMLChar = false;
762 }
763 }
764
765 // Place our children using the default method and no border/padding.
766 // This will allow our child text frame to get its DidReflow()
767 PlaceFlags flags(PlaceFlag::IgnoreBorderPadding,
768 PlaceFlag::DoNotAdjustForWidthAndHeight);
769 Place(aDrawTarget, flags, aDesiredStretchSize);
770
771 if (useMathMLChar) {
772 // update our bounding metrics... it becomes that of our MathML char
773 mBoundingMetrics = charSize;
774
775 // if the returned direction is 'unsupported', the char didn't actually
776 // change. So we do the centering only if necessary
777 if (mMathMLChar.GetStretchDirection() != StretchDirection::Unsupported) {
778 bool largeopOnly =
779 stretchFlags.contains(MathMLStretchFlag::LargeOperator) &&
780 (stretchFlags & kMathMLStretchVariableSet).isEmpty();
781
782 if (isVertical) {
783 // the desired size returned by mMathMLChar maybe different
784 // from the size of the container.
785 // the mMathMLChar.mRect.y calculation is subtle, watch out!!!
786
787 height = mBoundingMetrics.ascent + mBoundingMetrics.descent;
788 if (mFlags.Booleans().contains(OperatorBoolean::Symmetric)) {
789 // For symmetric and vertical operators, we want to center about the
790 // axis of the container
791 mBoundingMetrics.descent = height / 2 - axisHeight;
792 } else if (!largeopOnly) {
793 // Align the center of the char with the center of the container
794 mBoundingMetrics.descent =
795 height / 2 + (container.ascent + container.descent) / 2 -
796 container.ascent;
797 } // else align the baselines
798 mBoundingMetrics.ascent = height - mBoundingMetrics.descent;
799 }
800 }
801 }
802
803 // Fixup for the final height.
804 // On one hand, our stretchy height can sometimes be shorter than surrounding
805 // ASCII chars, e.g., arrow symbols have |mBoundingMetrics.ascent|
806 // that is smaller than the ASCII's ascent, hence when painting the background
807 // later, it won't look uniform along the line.
808 // On the other hand, sometimes we may leave too much gap when our glyph
809 // happens to come from a font with tall glyphs. For example, since CMEX10 has
810 // very tall glyphs, its natural font metrics are large, even if we pick a
811 // small glyph whose size is comparable to the size of a normal ASCII glyph.
812 // So to avoid uneven spacing in either of these two cases, we use the height
813 // of the ASCII font as a reference and try to match it if possible.
814
815 // special case for accents... keep them short to improve mouse operations...
816 // an accent can only be the non-first child of <mover>, <munder>,
817 // <munderover>
818 bool isAccent = mEmbellishData.flags.contains(MathMLEmbellishFlag::Accent);
819 if (isAccent) {
820 nsEmbellishData parentData;
821 GetEmbellishDataFrom(GetParent(), parentData);
822 isAccent = (parentData.flags.contains(MathMLEmbellishFlag::AccentOver) ||
823 parentData.flags.contains(MathMLEmbellishFlag::AccentUnder)) &&
824 parentData.coreFrame != this;
825 }
826 if (isAccent && firstChild) {
827 // see bug 188467 for what is going on here
828 nscoord dy =
829 aDesiredStretchSize.BlockStartAscent() - (mBoundingMetrics.ascent);
830 aDesiredStretchSize.SetBlockStartAscent(mBoundingMetrics.ascent);
831 aDesiredStretchSize.Height() =
832 aDesiredStretchSize.BlockStartAscent() + mBoundingMetrics.descent;
833
834 firstChild->SetPosition(firstChild->GetPosition() - nsPoint(0, dy));
835 } else if (useMathMLChar) {
836 nscoord ascent = fm->MaxAscent();
837 nscoord descent = fm->MaxDescent();
838 aDesiredStretchSize.SetBlockStartAscent(
839 std::max(mBoundingMetrics.ascent, ascent));
840 aDesiredStretchSize.Height() = aDesiredStretchSize.BlockStartAscent() +
841 std::max(mBoundingMetrics.descent, descent);
842 }
843 aDesiredStretchSize.Width() = mBoundingMetrics.width;
844 aDesiredStretchSize.mBoundingMetrics = mBoundingMetrics;
845 mReference.x = 0;
846 mReference.y = aDesiredStretchSize.BlockStartAscent();
847 // Place our mMathMLChar, its origin is in our coordinate system
848 if (useMathMLChar) {
849 nscoord dy =
850 aDesiredStretchSize.BlockStartAscent() - mBoundingMetrics.ascent;
851 mMathMLChar.SetRect(
852 nsRect(0, dy, charSize.width, charSize.ascent + charSize.descent));
853 }
854
855 // Before we leave... there is a last item in the check-list:
856 // If our parent is not embellished, it means we are the outermost embellished
857 // container and so we put the spacing, otherwise we don't include the
858 // spacing, the outermost embellished container will take care of it.
859
860 nscoord leadingSpace = 0, trailingSpace = 0;
861 if (!StaticPrefs::
862 mathml_lspace_rspace_for_child_spacing_during_mrow_layout_enabled() &&
863 !mFlags.Booleans().contains(OperatorBoolean::HasEmbellishAncestor)) {
864 // Account the spacing if we are not an accent with explicit attributes
865 if (!isAccent ||
866 mFlags.Booleans().contains(OperatorBoolean::HasLSpaceAttribute)) {
867 leadingSpace = mEmbellishData.leadingSpace;
868 }
869 if (!isAccent ||
870 mFlags.Booleans().contains(OperatorBoolean::HasRSpaceAttribute)) {
871 trailingSpace = mEmbellishData.trailingSpace;
872 }
873 }
874
875 flags = PlaceFlags();
876 auto sizes = GetWidthAndHeightForPlaceAdjustment(flags);
877 auto borderPadding = GetBorderPaddingForPlace(flags);
878 if (leadingSpace || trailingSpace || !borderPadding.IsAllZero() ||
879 sizes.width || sizes.height) {
880 mBoundingMetrics.width += leadingSpace + trailingSpace;
881 aDesiredStretchSize.Width() = mBoundingMetrics.width;
882 aDesiredStretchSize.mBoundingMetrics.width = mBoundingMetrics.width;
883
884 nscoord dx = GetWritingMode().IsBidiRTL() ? trailingSpace : leadingSpace;
885 mBoundingMetrics.leftBearing += dx;
886 mBoundingMetrics.rightBearing += dx;
887 aDesiredStretchSize.mBoundingMetrics.leftBearing += dx;
888 aDesiredStretchSize.mBoundingMetrics.rightBearing += dx;
889
890 // Apply inline/block sizes to math content box.
891 dx += ApplyAdjustmentForWidthAndHeight(flags, sizes, aDesiredStretchSize,
892 mBoundingMetrics);
893
894 // Add border/padding.
895 InflateReflowAndBoundingMetrics(borderPadding, aDesiredStretchSize,
896 mBoundingMetrics);
897 dx += borderPadding.left;
898 nscoord dy = borderPadding.top;
899
900 if (dx || dy) {
901 // adjust the offsets
902
903 if (useMathMLChar) {
904 nsRect rect;
905 mMathMLChar.GetRect(rect);
906 mMathMLChar.SetRect(
907 nsRect(rect.x + dx, rect.y + dy, rect.width, rect.height));
908 } else {
909 nsIFrame* childFrame = firstChild;
910 while (childFrame) {
911 childFrame->SetPosition(childFrame->GetPosition() + nsPoint(dx, dy));
912 childFrame = childFrame->GetNextSibling();
913 }
914 }
915 }
916 }
917
918 // Finished with these:
919 ClearSavedChildMetrics();
920 // Set our overflow area
921 GatherAndStoreOverflow(&aDesiredStretchSize);
922
923 // There used to be code here to change the height of the child frame to
924 // change the caret height, but the text frame that manages the caret is now
925 // not a direct child but wrapped in a block frame. See also bug 412033.
926
927 return NS_OK;
928}
929
930NS_IMETHODIMPnsresult
931nsMathMLmoFrame::InheritAutomaticData(nsIFrame* aParent) {
932 // retain our native direction, it only changes if our text content changes
933 StretchDirection direction = mEmbellishData.direction;
934 nsMathMLTokenFrame::InheritAutomaticData(aParent);
935 ProcessTextData();
936 mEmbellishData.direction = direction;
937 return NS_OK;
938}
939
940NS_IMETHODIMPnsresult
941nsMathMLmoFrame::TransmitAutomaticData() {
942 // this will cause us to re-sync our flags from scratch
943 // but our returned 'form' is still not final (bug 133429), it will
944 // be recomputed to its final value during the next call in Reflow()
945 mEmbellishData.coreFrame = nullptr;
946 ProcessOperatorData();
947 return NS_OK;
948}
949
950void nsMathMLmoFrame::SetInitialChildList(ChildListID aListID,
951 nsFrameList&& aChildList) {
952 // First, let the parent class do its work
953 nsMathMLTokenFrame::SetInitialChildList(aListID, std::move(aChildList));
954 ProcessTextData();
955}
956
957void nsMathMLmoFrame::Reflow(nsPresContext* aPresContext,
958 ReflowOutput& aDesiredSize,
959 const ReflowInput& aReflowInput,
960 nsReflowStatus& aStatus) {
961 MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aStatus.IsEmpty())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aStatus.IsEmpty()))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("aStatus.IsEmpty()"
" (" "Caller should pass a fresh reflow status!" ")", "./../../../layout/mathml/nsMathMLmoFrame.cpp"
, 961); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aStatus.IsEmpty()"
") (" "Caller should pass a fresh reflow status!" ")"); do {
MOZ_CrashSequence(__null, 961); __attribute__((nomerge)) ::abort
(); } while (false); } } while (false)
;
962
963 // certain values use units that depend on our ComputedStyle, so
964 // it is safer to just process the whole lot here
965 ProcessOperatorData();
966
967 nsMathMLTokenFrame::Reflow(aPresContext, aDesiredSize, aReflowInput, aStatus);
968}
969
970void nsMathMLmoFrame::Place(DrawTarget* aDrawTarget, const PlaceFlags& aFlags,
971 ReflowOutput& aDesiredSize) {
972 nsMathMLTokenFrame::Place(aDrawTarget, aFlags, aDesiredSize);
973
974 /* Special behaviour for largeops.
975 In MathML "stretchy" and displaystyle "largeop" are different notions,
976 even if we use the same technique to draw them (picking size variants).
977 So largeop display operators should be considered "non-stretchy" and
978 thus their sizes should be taken into account for the stretch size of
979 other elements.
980
981 This is a preliminary stretch - exact sizing/placement is handled by the
982 Stretch() method.
983 */
984
985 if (aFlags.contains(PlaceFlag::MeasureOnly) &&
986 StyleFont()->mMathStyle == StyleMathStyle::Normal &&
987 mFlags.Booleans().contains(OperatorBoolean::LargeOperator) &&
988 UseMathMLChar()) {
989 nsBoundingMetrics newMetrics;
990 nsresult rv = mMathMLChar.Stretch(
991 this, aDrawTarget, nsLayoutUtils::FontSizeInflationFor(this),
992 StretchDirection::Vertical, aDesiredSize.mBoundingMetrics, newMetrics,
993 MathMLStretchFlag::LargeOperator, GetWritingMode().IsBidiRTL());
994
995 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) {
996 // Just use the initial size
997 return;
998 }
999
1000 aDesiredSize.mBoundingMetrics = newMetrics;
1001 /* Treat the ascent/descent values calculated in the TokenFrame place
1002 calculations as the minimum for aDesiredSize calculations, rather
1003 than fetching them from font metrics again.
1004 */
1005 aDesiredSize.SetBlockStartAscent(
1006 std::max(mBoundingMetrics.ascent, newMetrics.ascent));
1007 aDesiredSize.Height() =
1008 aDesiredSize.BlockStartAscent() +
1009 std::max(mBoundingMetrics.descent, newMetrics.descent);
1010 aDesiredSize.Width() = newMetrics.width;
1011 mBoundingMetrics = newMetrics;
1012 }
1013}
1014
1015/* virtual */
1016void nsMathMLmoFrame::MarkIntrinsicISizesDirty() {
1017 // if we get this, it may mean that something changed in the text
1018 // content. So blow away everything an re-build the automatic data
1019 // from the parent of our outermost embellished container (we ensure
1020 // that we are the core, not just a sibling of the core)
1021
1022 ProcessTextData();
1023
1024 nsIFrame* target = this;
1025 nsEmbellishData embellishData;
1026 do {
1027 target = target->GetParent();
1028 GetEmbellishDataFrom(target, embellishData);
1029 } while (embellishData.coreFrame == this);
1030
1031 // we have automatic data to update in the children of the target frame
1032 // XXXldb This should really be marking dirty rather than rebuilding
1033 // so that we don't rebuild multiple times for the same change.
1034 RebuildAutomaticDataForChildren(target);
1035
1036 nsMathMLContainerFrame::MarkIntrinsicISizesDirty();
1037}
1038
1039/* virtual */
1040void nsMathMLmoFrame::GetIntrinsicISizeMetrics(gfxContext* aRenderingContext,
1041 ReflowOutput& aDesiredSize) {
1042 ProcessOperatorData();
1043 if (UseMathMLChar()) {
1044 auto stretchFlags =
1045 GetStretchFlags(mFlags, mPresentationData, true, StyleFont());
1046 aDesiredSize.Width() = mMathMLChar.GetMaxWidth(
1047 this, aRenderingContext->GetDrawTarget(),
1048 nsLayoutUtils::FontSizeInflationFor(this), stretchFlags);
1049 aDesiredSize.Width() += IntrinsicISizeOffsets().BorderPadding();
1050 } else {
1051 nsMathMLTokenFrame::GetIntrinsicISizeMetrics(aRenderingContext,
1052 aDesiredSize);
1053 }
1054
1055 // leadingSpace and trailingSpace are actually applied to the outermost
1056 // embellished container but for determining total intrinsic width it should
1057 // be safe to include it for the core here instead.
1058 nscoord leadingSpace = 0, trailingSpace = 0;
1059 if (!StaticPrefs::
1060 mathml_lspace_rspace_for_child_spacing_during_mrow_layout_enabled()) {
1061 leadingSpace = mEmbellishData.leadingSpace;
1062 trailingSpace = mEmbellishData.trailingSpace;
1063 }
1064 bool isRTL = GetWritingMode().IsBidiRTL();
1065 aDesiredSize.Width() += leadingSpace + trailingSpace;
1066 aDesiredSize.mBoundingMetrics.width = aDesiredSize.Width();
1067 if (isRTL) {
1068 aDesiredSize.mBoundingMetrics.leftBearing += trailingSpace;
1069 aDesiredSize.mBoundingMetrics.rightBearing += trailingSpace;
1070 } else {
1071 aDesiredSize.mBoundingMetrics.leftBearing += leadingSpace;
1072 aDesiredSize.mBoundingMetrics.rightBearing += leadingSpace;
1073 }
1074}
1075
1076nsresult nsMathMLmoFrame::AttributeChanged(int32_t aNameSpaceID,
1077 nsAtom* aAttribute,
1078 AttrModType aModType) {
1079 // check if this is an attribute that can affect the embellished hierarchy
1080 // in a significant way and re-layout the entire hierarchy.
1081 // This is not needed for the fence and separator
1082 // attributes, since they have no visual effect.
1083 if (aAttribute == nsGkAtoms::accent || aAttribute == nsGkAtoms::form ||
1084 aAttribute == nsGkAtoms::largeop || aAttribute == nsGkAtoms::maxsize ||
1085 aAttribute == nsGkAtoms::minsize ||
1086 aAttribute == nsGkAtoms::movablelimits ||
1087 aAttribute == nsGkAtoms::rspace || aAttribute == nsGkAtoms::stretchy ||
1088 aAttribute == nsGkAtoms::symmetric || aAttribute == nsGkAtoms::lspace) {
1089 // set the target as the parent of our outermost embellished container
1090 // (we ensure that we are the core, not just a sibling of the core)
1091 nsIFrame* target = this;
1092 nsEmbellishData embellishData;
1093 do {
1094 target = target->GetParent();
1095 GetEmbellishDataFrom(target, embellishData);
1096 } while (embellishData.coreFrame == this);
1097
1098 // we have automatic data to update in the children of the target frame
1099 return ReLayoutChildren(target);
1100 }
1101
1102 return nsMathMLTokenFrame::AttributeChanged(aNameSpaceID, aAttribute,
1103 aModType);
1104}
1105
1106void nsMathMLmoFrame::DidSetComputedStyle(ComputedStyle* aOldStyle) {
1107 nsMathMLTokenFrame::DidSetComputedStyle(aOldStyle);
1108 mMathMLChar.SetComputedStyle(Style());
1109}
1110
1111nscoord nsMathMLmoFrame::ItalicCorrection() {
1112 return UseMathMLChar() ? mMathMLChar.ItalicCorrection() : 0;
1113}
1114
1115nscoord nsMathMLmoFrame::FixInterFrameSpacing(ReflowOutput& aDesiredSize) {
1116 nscoord gap = nsMathMLContainerFrame::FixInterFrameSpacing(aDesiredSize);
1117 if (!gap) {
1118 return 0;
1119 }
1120
1121 // Move the MathML character.
1122 nsRect rect;
1123 mMathMLChar.GetRect(rect);
1124 rect.MoveBy(gap, 0);
1125 mMathMLChar.SetRect(rect);
1126
1127 return gap;
1128}