Bug Summary

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