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