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