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 |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 | |
20 | using namespace mozilla; |
21 | |
22 | // |
23 | // <mo> -- operator, fence, or separator - implementation |
24 | // |
25 | |
26 | nsIFrame* NS_NewMathMLmoFrame(PresShell* aPresShell, ComputedStyle* aStyle) { |
27 | return new (aPresShell) nsMathMLmoFrame(aStyle, aPresShell->GetPresContext()); |
28 | } |
29 | |
30 | NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmoFrame)void* nsMathMLmoFrame ::operator new(size_t sz, mozilla::PresShell * aShell) { return aShell->AllocateFrame(nsQueryFrame::nsMathMLmoFrame_id , sz); } |
31 | |
32 | nsMathMLmoFrame::~nsMathMLmoFrame() = default; |
33 | |
34 | static const char16_t kApplyFunction = char16_t(0x2061); |
35 | static const char16_t kInvisibleTimes = char16_t(0x2062); |
36 | static const char16_t kInvisibleSeparator = char16_t(0x2063); |
37 | static const char16_t kInvisiblePlus = char16_t(0x2064); |
38 | |
39 | eMathMLFrameType 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. |
48 | bool 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 | |
59 | bool 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 | |
65 | void 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 |
91 | void 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) || // − |
150 | (ch == 0x2264) || // ≤ |
151 | (ch == 0x2265) || // ≥ |
152 | (ch == 0x00D7)) { // × |
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. |
181 | void 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 | |
542 | static 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 |
578 | NS_IMETHODIMPnsresult |
579 | nsMathMLmoFrame::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 | |
905 | NS_IMETHODIMPnsresult |
906 | nsMathMLmoFrame::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 | |
915 | NS_IMETHODIMPnsresult |
916 | nsMathMLmoFrame::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 | |
925 | void 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 | |
932 | void 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 | |
945 | nsresult 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 */ |
997 | void 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 */ |
1021 | void 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 | |
1052 | nsresult 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 | |
1082 | void nsMathMLmoFrame::DidSetComputedStyle(ComputedStyle* aOldStyle) { |
1083 | nsMathMLTokenFrame::DidSetComputedStyle(aOldStyle); |
1084 | mMathMLChar.SetComputedStyle(Style()); |
1085 | } |