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