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