Bug Summary

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