Bug Summary

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