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