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) ||  // &minus;
149         (ch == 0x2264) ||  // &le;
150         (ch == 0x2265) ||  // &ge;
151         (ch == 0x00D7)) {  // &times;
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