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) || // −
172 (ch == 0x2264) || // ≤
173 (ch == 0x2265) || // ≥
174 (ch == 0x00D7)) { // ×
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