nsMathMLmencloseFrame.cpp (26269B)
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 "nsMathMLmencloseFrame.h" 8 9 #include <algorithm> 10 11 #include "gfx2DGlue.h" 12 #include "gfxContext.h" 13 #include "gfxUtils.h" 14 #include "mozilla/PresShell.h" 15 #include "mozilla/StaticPrefs_mathml.h" 16 #include "mozilla/dom/Document.h" 17 #include "mozilla/dom/Element.h" 18 #include "mozilla/gfx/2D.h" 19 #include "mozilla/gfx/PathHelpers.h" 20 #include "nsDisplayList.h" 21 #include "nsLayoutUtils.h" 22 #include "nsMathMLChar.h" 23 #include "nsPresContext.h" 24 #include "nsWhitespaceTokenizer.h" 25 26 using namespace mozilla; 27 using namespace mozilla::gfx; 28 29 // 30 // <menclose> -- enclose content with a stretching symbol such 31 // as a long division sign. - implementation 32 33 // longdiv: 34 // Unicode 5.1 assigns U+27CC to LONG DIVISION, but a right parenthesis 35 // renders better with current font support. 36 static const char16_t kLongDivChar = ')'; 37 38 // updiagonalstrike 39 static const uint8_t kArrowHeadSize = 10; 40 41 // phasorangle 42 static const uint8_t kPhasorangleWidth = 8; 43 44 nsIFrame* NS_NewMathMLmencloseFrame(PresShell* aPresShell, 45 ComputedStyle* aStyle) { 46 return new (aPresShell) 47 nsMathMLmencloseFrame(aStyle, aPresShell->GetPresContext()); 48 } 49 50 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmencloseFrame) 51 52 nsMathMLmencloseFrame::nsMathMLmencloseFrame(ComputedStyle* aStyle, 53 nsPresContext* aPresContext) 54 : nsMathMLContainerFrame(aStyle, aPresContext, kClassID), 55 mRuleThickness(0), 56 mLongDivCharIndex(-1), 57 mContentWidth(0) {} 58 59 nsMathMLmencloseFrame::~nsMathMLmencloseFrame() = default; 60 61 nsresult nsMathMLmencloseFrame::AllocateMathMLChar(nsMencloseNotation mask) { 62 // Is the char already allocated? 63 if (mask == NOTATION_LONGDIV && mLongDivCharIndex >= 0) { 64 return NS_OK; 65 } 66 67 // No need to track the ComputedStyle given to our MathML chars. 68 // The Style System will use Get/SetAdditionalComputedStyle() to keep it 69 // up-to-date if dynamic changes arise. 70 uint32_t i = mMathMLChar.Length(); 71 nsAutoString Char; 72 73 // XXX(Bug 1631371) Check if this should use a fallible operation as it 74 // pretended earlier, or change the return type to void. 75 mMathMLChar.AppendElement(); 76 77 if (mask == NOTATION_LONGDIV) { 78 Char.Assign(kLongDivChar); 79 mLongDivCharIndex = i; 80 } 81 82 mMathMLChar[i].SetData(Char); 83 mMathMLChar[i].SetComputedStyle(Style()); 84 85 return NS_OK; 86 } 87 88 /* 89 * Add a notation to draw, if the argument is the name of a known notation. 90 * @param aNotation string name of a notation 91 */ 92 nsresult nsMathMLmencloseFrame::AddNotation(const nsAString& aNotation) { 93 nsresult rv; 94 95 if (aNotation.EqualsLiteral("longdiv")) { 96 rv = AllocateMathMLChar(NOTATION_LONGDIV); 97 NS_ENSURE_SUCCESS(rv, rv); 98 mNotationsToDraw += NOTATION_LONGDIV; 99 } else if (aNotation.EqualsLiteral("actuarial")) { 100 mNotationsToDraw += NOTATION_RIGHT; 101 mNotationsToDraw += NOTATION_TOP; 102 } else if (aNotation.EqualsLiteral("box")) { 103 mNotationsToDraw += NOTATION_LEFT; 104 mNotationsToDraw += NOTATION_RIGHT; 105 mNotationsToDraw += NOTATION_TOP; 106 mNotationsToDraw += NOTATION_BOTTOM; 107 } else if (aNotation.EqualsLiteral("roundedbox")) { 108 mNotationsToDraw += NOTATION_ROUNDEDBOX; 109 } else if (aNotation.EqualsLiteral("circle")) { 110 mNotationsToDraw += NOTATION_CIRCLE; 111 } else if (aNotation.EqualsLiteral("left")) { 112 mNotationsToDraw += NOTATION_LEFT; 113 } else if (aNotation.EqualsLiteral("right")) { 114 mNotationsToDraw += NOTATION_RIGHT; 115 } else if (aNotation.EqualsLiteral("top")) { 116 mNotationsToDraw += NOTATION_TOP; 117 } else if (aNotation.EqualsLiteral("bottom")) { 118 mNotationsToDraw += NOTATION_BOTTOM; 119 } else if (aNotation.EqualsLiteral("updiagonalstrike")) { 120 mNotationsToDraw += NOTATION_UPDIAGONALSTRIKE; 121 } else if (aNotation.EqualsLiteral("updiagonalarrow")) { 122 mNotationsToDraw += NOTATION_UPDIAGONALARROW; 123 } else if (aNotation.EqualsLiteral("downdiagonalstrike")) { 124 mNotationsToDraw += NOTATION_DOWNDIAGONALSTRIKE; 125 } else if (aNotation.EqualsLiteral("verticalstrike")) { 126 mNotationsToDraw += NOTATION_VERTICALSTRIKE; 127 } else if (aNotation.EqualsLiteral("horizontalstrike")) { 128 mNotationsToDraw += NOTATION_HORIZONTALSTRIKE; 129 } else if (aNotation.EqualsLiteral("madruwb")) { 130 mNotationsToDraw += NOTATION_RIGHT; 131 mNotationsToDraw += NOTATION_BOTTOM; 132 } else if (aNotation.EqualsLiteral("phasorangle")) { 133 mNotationsToDraw += NOTATION_BOTTOM; 134 mNotationsToDraw += NOTATION_PHASORANGLE; 135 } 136 137 return NS_OK; 138 } 139 140 /* 141 * Initialize the list of notations to draw 142 */ 143 void nsMathMLmencloseFrame::InitNotations() { 144 MarkNeedsDisplayItemRebuild(); 145 mNotationsToDraw.clear(); 146 mLongDivCharIndex = -1; 147 mMathMLChar.Clear(); 148 149 nsAutoString value; 150 151 if (mContent->AsElement()->GetAttr(nsGkAtoms::notation, value)) { 152 // parse the notation attribute 153 nsWhitespaceTokenizer tokenizer(value); 154 155 while (tokenizer.hasMoreTokens()) { 156 AddNotation(tokenizer.nextToken()); 157 } 158 159 if (IsToDraw(NOTATION_UPDIAGONALARROW)) { 160 // For <menclose notation="updiagonalstrike updiagonalarrow">, if 161 // the two notations are drawn then the strike line may cause the point of 162 // the arrow to be too wide. Hence we will only draw the updiagonalarrow 163 // and the arrow shaft may be thought to be the updiagonalstrike. 164 mNotationsToDraw -= NOTATION_UPDIAGONALSTRIKE; 165 } 166 } else { 167 // default: longdiv 168 if (NS_FAILED(AllocateMathMLChar(NOTATION_LONGDIV))) { 169 return; 170 } 171 mNotationsToDraw += NOTATION_LONGDIV; 172 } 173 } 174 175 NS_IMETHODIMP 176 nsMathMLmencloseFrame::InheritAutomaticData(nsIFrame* aParent) { 177 // let the base class get the default from our parent 178 nsMathMLContainerFrame::InheritAutomaticData(aParent); 179 180 mPresentationData.flags += 181 MathMLPresentationFlag::StretchAllChildrenVertically; 182 183 InitNotations(); 184 185 return NS_OK; 186 } 187 188 void nsMathMLmencloseFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, 189 const nsDisplayListSet& aLists) { 190 ///////////// 191 // paint the menclosed content 192 nsMathMLContainerFrame::BuildDisplayList(aBuilder, aLists); 193 194 nsRect mencloseRect = nsIFrame::GetContentRectRelativeToSelf(); 195 196 if (IsToDraw(NOTATION_PHASORANGLE)) { 197 DisplayNotation(aBuilder, this, mencloseRect, aLists, mRuleThickness, 198 NOTATION_PHASORANGLE); 199 } 200 201 if (IsToDraw(NOTATION_LONGDIV)) { 202 mMathMLChar[mLongDivCharIndex].Display(aBuilder, this, aLists, 1); 203 204 nsRect rect; 205 mMathMLChar[mLongDivCharIndex].GetRect(rect); 206 rect.SizeTo(rect.width + mContentWidth, mRuleThickness); 207 DisplayBar(aBuilder, this, rect, aLists, NOTATION_LONGDIV); 208 } 209 210 if (IsToDraw(NOTATION_TOP)) { 211 nsRect rect(0, 0, mencloseRect.width, mRuleThickness); 212 DisplayBar(aBuilder, this, rect, aLists, NOTATION_TOP); 213 } 214 215 if (IsToDraw(NOTATION_BOTTOM)) { 216 nsRect rect(0, mencloseRect.height - mRuleThickness, mencloseRect.width, 217 mRuleThickness); 218 DisplayBar(aBuilder, this, rect, aLists, NOTATION_BOTTOM); 219 } 220 221 if (IsToDraw(NOTATION_LEFT)) { 222 nsRect rect(0, 0, mRuleThickness, mencloseRect.height); 223 DisplayBar(aBuilder, this, rect, aLists, NOTATION_LEFT); 224 } 225 226 if (IsToDraw(NOTATION_RIGHT)) { 227 nsRect rect(mencloseRect.width - mRuleThickness, 0, mRuleThickness, 228 mencloseRect.height); 229 DisplayBar(aBuilder, this, rect, aLists, NOTATION_RIGHT); 230 } 231 232 if (IsToDraw(NOTATION_ROUNDEDBOX)) { 233 DisplayNotation(aBuilder, this, mencloseRect, aLists, mRuleThickness, 234 NOTATION_ROUNDEDBOX); 235 } 236 237 if (IsToDraw(NOTATION_CIRCLE)) { 238 DisplayNotation(aBuilder, this, mencloseRect, aLists, mRuleThickness, 239 NOTATION_CIRCLE); 240 } 241 242 if (IsToDraw(NOTATION_UPDIAGONALSTRIKE)) { 243 DisplayNotation(aBuilder, this, mencloseRect, aLists, mRuleThickness, 244 NOTATION_UPDIAGONALSTRIKE); 245 } 246 247 if (IsToDraw(NOTATION_UPDIAGONALARROW)) { 248 DisplayNotation(aBuilder, this, mencloseRect, aLists, mRuleThickness, 249 NOTATION_UPDIAGONALARROW); 250 } 251 252 if (IsToDraw(NOTATION_DOWNDIAGONALSTRIKE)) { 253 DisplayNotation(aBuilder, this, mencloseRect, aLists, mRuleThickness, 254 NOTATION_DOWNDIAGONALSTRIKE); 255 } 256 257 if (IsToDraw(NOTATION_HORIZONTALSTRIKE)) { 258 nsRect rect(0, mencloseRect.height / 2 - mRuleThickness / 2, 259 mencloseRect.width, mRuleThickness); 260 DisplayBar(aBuilder, this, rect, aLists, NOTATION_HORIZONTALSTRIKE); 261 } 262 263 if (IsToDraw(NOTATION_VERTICALSTRIKE)) { 264 nsRect rect(mencloseRect.width / 2 - mRuleThickness / 2, 0, mRuleThickness, 265 mencloseRect.height); 266 DisplayBar(aBuilder, this, rect, aLists, NOTATION_VERTICALSTRIKE); 267 } 268 } 269 270 /* virtual */ 271 void nsMathMLmencloseFrame::Place(DrawTarget* aDrawTarget, 272 const PlaceFlags& aFlags, 273 ReflowOutput& aDesiredSize) { 274 /////////////// 275 // Measure the size of our content using the base class to format like an 276 // inferred mrow, without border/padding. 277 ReflowOutput baseSize(aDesiredSize.GetWritingMode()); 278 PlaceFlags flags = aFlags + PlaceFlag::MeasureOnly + 279 PlaceFlag::IgnoreBorderPadding + 280 PlaceFlag::DoNotAdjustForWidthAndHeight; 281 nsMathMLContainerFrame::Place(aDrawTarget, flags, baseSize); 282 283 nsBoundingMetrics bmBase = baseSize.mBoundingMetrics; 284 nscoord dx_left = 0, dx_right = 0; 285 nsBoundingMetrics bmLongdivChar; 286 nscoord longdivAscent = 0, longdivDescent = 0; 287 nscoord psi = 0; 288 nscoord leading = 0; 289 290 /////////////// 291 // Thickness of bars and font metrics 292 nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1); 293 294 float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this); 295 RefPtr<nsFontMetrics> fm = 296 nsLayoutUtils::GetFontMetricsForFrame(this, fontSizeInflation); 297 GetRuleThickness(aDrawTarget, fm, mRuleThickness); 298 if (mRuleThickness < onePixel) { 299 mRuleThickness = onePixel; 300 } 301 302 char16_t one = '1'; 303 nsBoundingMetrics bmOne = 304 nsLayoutUtils::AppUnitBoundsOfString(&one, 1, *fm, aDrawTarget); 305 306 /////////////// 307 // General rules: the menclose element takes the size of the enclosed content. 308 // We add a padding when needed. 309 310 // determine padding & psi 311 nscoord padding = 3 * mRuleThickness; 312 nscoord delta = padding % onePixel; 313 if (delta) { 314 padding += onePixel - delta; // round up 315 } 316 317 if (IsToDraw(NOTATION_LONGDIV)) { 318 // The MathML spec does not define precise layout rules for menclose. Here 319 // we draw longdiv using the same parameter as for radicals. 320 // See https://github.com/w3c/mathml-core/issues/245 321 nscoord dummy; 322 GetRadicalParameters(fm, StyleFont()->mMathStyle == StyleMathStyle::Normal, 323 dummy, leading, psi); 324 325 // adjust clearance psi to get an exact number of pixels -- this 326 // gives a nicer & uniform look on stacked radicals (bug 130282) 327 delta = psi % onePixel; 328 if (delta) { 329 psi += onePixel - delta; // round up 330 } 331 } 332 333 // Set horizontal parameters 334 if (IsToDraw(NOTATION_ROUNDEDBOX) || IsToDraw(NOTATION_TOP) || 335 IsToDraw(NOTATION_LEFT) || IsToDraw(NOTATION_BOTTOM) || 336 IsToDraw(NOTATION_CIRCLE)) { 337 dx_left = padding; 338 } 339 340 if (IsToDraw(NOTATION_ROUNDEDBOX) || IsToDraw(NOTATION_TOP) || 341 IsToDraw(NOTATION_RIGHT) || IsToDraw(NOTATION_BOTTOM) || 342 IsToDraw(NOTATION_CIRCLE)) { 343 dx_right = padding; 344 } 345 346 // Set vertical parameters 347 if (IsToDraw(NOTATION_RIGHT) || IsToDraw(NOTATION_LEFT) || 348 IsToDraw(NOTATION_UPDIAGONALSTRIKE) || 349 IsToDraw(NOTATION_UPDIAGONALARROW) || 350 IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) || 351 IsToDraw(NOTATION_VERTICALSTRIKE) || IsToDraw(NOTATION_CIRCLE) || 352 IsToDraw(NOTATION_ROUNDEDBOX) || IsToDraw(NOTATION_LONGDIV) || 353 IsToDraw(NOTATION_PHASORANGLE)) { 354 // set a minimal value for the base height 355 bmBase.ascent = std::max(bmOne.ascent, bmBase.ascent); 356 bmBase.descent = std::max(0, bmBase.descent); 357 } 358 359 mBoundingMetrics.ascent = bmBase.ascent; 360 mBoundingMetrics.descent = bmBase.descent; 361 362 if (IsToDraw(NOTATION_ROUNDEDBOX) || IsToDraw(NOTATION_TOP) || 363 IsToDraw(NOTATION_LEFT) || IsToDraw(NOTATION_RIGHT) || 364 IsToDraw(NOTATION_CIRCLE)) { 365 mBoundingMetrics.ascent += padding; 366 } 367 368 if (IsToDraw(NOTATION_ROUNDEDBOX) || IsToDraw(NOTATION_LEFT) || 369 IsToDraw(NOTATION_RIGHT) || IsToDraw(NOTATION_BOTTOM) || 370 IsToDraw(NOTATION_CIRCLE)) { 371 mBoundingMetrics.descent += padding; 372 } 373 374 /////////////// 375 // phasorangle notation 376 if (IsToDraw(NOTATION_PHASORANGLE)) { 377 nscoord phasorangleWidth = kPhasorangleWidth * mRuleThickness; 378 // Update horizontal parameters 379 dx_left = std::max(dx_left, phasorangleWidth); 380 } 381 382 /////////////// 383 // updiagonal arrow notation. We need enough space at the top right corner to 384 // draw the arrow head. 385 if (IsToDraw(NOTATION_UPDIAGONALARROW)) { 386 // This is an estimate, see nsDisplayNotation::Paint for the exact head size 387 nscoord arrowHeadSize = kArrowHeadSize * mRuleThickness; 388 389 // We want that the arrow shaft strikes the menclose content and that the 390 // arrow head does not overlap with that content. Hence we add some space 391 // on the right. We don't add space on the top but only ensure that the 392 // ascent is large enough. 393 dx_right = std::max(dx_right, arrowHeadSize); 394 mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent, arrowHeadSize); 395 } 396 397 /////////////// 398 // circle notation: we don't want the ellipse to overlap the enclosed 399 // content. Hence, we need to increase the size of the bounding box by a 400 // factor of at least sqrt(2). 401 if (IsToDraw(NOTATION_CIRCLE)) { 402 double ratio = (sqrt(2.0) - 1.0) / 2.0; 403 nscoord padding2; 404 405 // Update horizontal parameters 406 padding2 = ratio * bmBase.width; 407 408 dx_left = std::max(dx_left, padding2); 409 dx_right = std::max(dx_right, padding2); 410 411 // Update vertical parameters 412 padding2 = ratio * (bmBase.ascent + bmBase.descent); 413 414 mBoundingMetrics.ascent = 415 std::max(mBoundingMetrics.ascent, bmBase.ascent + padding2); 416 mBoundingMetrics.descent = 417 std::max(mBoundingMetrics.descent, bmBase.descent + padding2); 418 } 419 420 /////////////// 421 // longdiv notation: 422 if (IsToDraw(NOTATION_LONGDIV)) { 423 if (aFlags.contains(PlaceFlag::IntrinsicSize)) { 424 nscoord longdiv_width = mMathMLChar[mLongDivCharIndex].GetMaxWidth( 425 this, aDrawTarget, fontSizeInflation); 426 427 // Update horizontal parameters 428 dx_left = std::max(dx_left, longdiv_width); 429 } else { 430 // Stretch the parenthesis to the appropriate height if it is not 431 // big enough. 432 nsBoundingMetrics contSize = bmBase; 433 contSize.ascent = mRuleThickness; 434 contSize.descent = bmBase.ascent + bmBase.descent + psi; 435 436 // height(longdiv) should be >= height(base) + psi + mRuleThickness 437 mMathMLChar[mLongDivCharIndex].Stretch( 438 this, aDrawTarget, fontSizeInflation, NS_STRETCH_DIRECTION_VERTICAL, 439 contSize, bmLongdivChar, NS_STRETCH_LARGER, false); 440 mMathMLChar[mLongDivCharIndex].GetBoundingMetrics(bmLongdivChar); 441 442 // Update horizontal parameters 443 dx_left = std::max(dx_left, bmLongdivChar.width); 444 445 // Update vertical parameters 446 longdivAscent = bmBase.ascent + psi + mRuleThickness; 447 longdivDescent = std::max( 448 bmBase.descent, 449 (bmLongdivChar.ascent + bmLongdivChar.descent - longdivAscent)); 450 451 mBoundingMetrics.ascent = 452 std::max(mBoundingMetrics.ascent, longdivAscent); 453 mBoundingMetrics.descent = 454 std::max(mBoundingMetrics.descent, longdivDescent); 455 } 456 } 457 458 /////////////// 459 // 460 if (IsToDraw(NOTATION_CIRCLE) || IsToDraw(NOTATION_ROUNDEDBOX) || 461 (IsToDraw(NOTATION_LEFT) && IsToDraw(NOTATION_RIGHT))) { 462 // center the menclose around the content (horizontally) 463 dx_left = dx_right = std::max(dx_left, dx_right); 464 } 465 466 /////////////// 467 // The maximum size is now computed: set the remaining parameters 468 mBoundingMetrics.width = dx_left + bmBase.width + dx_right; 469 470 mBoundingMetrics.leftBearing = std::min(0, dx_left + bmBase.leftBearing); 471 mBoundingMetrics.rightBearing = 472 std::max(mBoundingMetrics.width, dx_left + bmBase.rightBearing); 473 474 aDesiredSize.Width() = mBoundingMetrics.width; 475 476 aDesiredSize.SetBlockStartAscent( 477 std::max(mBoundingMetrics.ascent, baseSize.BlockStartAscent())); 478 aDesiredSize.Height() = 479 aDesiredSize.BlockStartAscent() + 480 std::max(mBoundingMetrics.descent, 481 baseSize.Height() - baseSize.BlockStartAscent()); 482 483 if (IsToDraw(NOTATION_LONGDIV)) { 484 nscoord desiredSizeAscent = aDesiredSize.BlockStartAscent(); 485 nscoord desiredSizeDescent = 486 aDesiredSize.Height() - aDesiredSize.BlockStartAscent(); 487 488 if (IsToDraw(NOTATION_LONGDIV)) { 489 desiredSizeAscent = std::max(desiredSizeAscent, longdivAscent + leading); 490 desiredSizeDescent = 491 std::max(desiredSizeDescent, longdivDescent + mRuleThickness); 492 } 493 494 aDesiredSize.SetBlockStartAscent(desiredSizeAscent); 495 aDesiredSize.Height() = desiredSizeAscent + desiredSizeDescent; 496 } 497 498 if (IsToDraw(NOTATION_CIRCLE) || IsToDraw(NOTATION_ROUNDEDBOX) || 499 (IsToDraw(NOTATION_TOP) && IsToDraw(NOTATION_BOTTOM))) { 500 // center the menclose around the content (vertically) 501 nscoord dy = std::max(aDesiredSize.BlockStartAscent() - bmBase.ascent, 502 aDesiredSize.Height() - 503 aDesiredSize.BlockStartAscent() - bmBase.descent); 504 505 aDesiredSize.SetBlockStartAscent(bmBase.ascent + dy); 506 aDesiredSize.Height() = 507 aDesiredSize.BlockStartAscent() + bmBase.descent + dy; 508 } 509 510 // Update mBoundingMetrics ascent/descent 511 if (IsToDraw(NOTATION_TOP) || IsToDraw(NOTATION_RIGHT) || 512 IsToDraw(NOTATION_LEFT) || IsToDraw(NOTATION_UPDIAGONALSTRIKE) || 513 IsToDraw(NOTATION_UPDIAGONALARROW) || 514 IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) || 515 IsToDraw(NOTATION_VERTICALSTRIKE) || IsToDraw(NOTATION_CIRCLE) || 516 IsToDraw(NOTATION_ROUNDEDBOX)) { 517 mBoundingMetrics.ascent = aDesiredSize.BlockStartAscent(); 518 } 519 520 if (IsToDraw(NOTATION_BOTTOM) || IsToDraw(NOTATION_RIGHT) || 521 IsToDraw(NOTATION_LEFT) || IsToDraw(NOTATION_UPDIAGONALSTRIKE) || 522 IsToDraw(NOTATION_UPDIAGONALARROW) || 523 IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) || 524 IsToDraw(NOTATION_VERTICALSTRIKE) || IsToDraw(NOTATION_CIRCLE) || 525 IsToDraw(NOTATION_ROUNDEDBOX)) { 526 mBoundingMetrics.descent = 527 aDesiredSize.Height() - aDesiredSize.BlockStartAscent(); 528 } 529 530 // phasorangle notation: 531 // move up from the bottom by the angled line height 532 if (IsToDraw(NOTATION_PHASORANGLE)) { 533 mBoundingMetrics.ascent = std::max( 534 mBoundingMetrics.ascent, 535 2 * kPhasorangleWidth * mRuleThickness - mBoundingMetrics.descent); 536 } 537 538 aDesiredSize.mBoundingMetrics = mBoundingMetrics; 539 540 // Apply width/height to math content box. 541 auto sizes = GetWidthAndHeightForPlaceAdjustment(aFlags); 542 dx_left += ApplyAdjustmentForWidthAndHeight(aFlags, sizes, aDesiredSize, 543 mBoundingMetrics); 544 545 // Add padding+border. 546 auto borderPadding = GetBorderPaddingForPlace(aFlags); 547 InflateReflowAndBoundingMetrics(borderPadding, aDesiredSize, 548 mBoundingMetrics); 549 550 mReference.x = 0; 551 mReference.y = aDesiredSize.BlockStartAscent(); 552 553 if (!aFlags.contains(PlaceFlag::MeasureOnly)) { 554 ////////////////// 555 // Set position and size of MathMLChars 556 if (IsToDraw(NOTATION_LONGDIV)) { 557 mMathMLChar[mLongDivCharIndex].SetRect(nsRect( 558 dx_left - bmLongdivChar.width + borderPadding.left, 559 aDesiredSize.BlockStartAscent() - longdivAscent, bmLongdivChar.width, 560 bmLongdivChar.ascent + bmLongdivChar.descent)); 561 } 562 563 mContentWidth = bmBase.width; 564 565 ////////////////// 566 // Finish reflowing child frames 567 PositionRowChildFrames(dx_left + borderPadding.left, 568 aDesiredSize.BlockStartAscent()); 569 } 570 } 571 572 nscoord nsMathMLmencloseFrame::FixInterFrameSpacing( 573 ReflowOutput& aDesiredSize) { 574 nscoord gap = nsMathMLContainerFrame::FixInterFrameSpacing(aDesiredSize); 575 if (!gap) { 576 return 0; 577 } 578 579 // Move the MathML characters 580 nsRect rect; 581 for (uint32_t i = 0; i < mMathMLChar.Length(); i++) { 582 mMathMLChar[i].GetRect(rect); 583 rect.MoveBy(gap, 0); 584 mMathMLChar[i].SetRect(rect); 585 } 586 587 return gap; 588 } 589 590 nsresult nsMathMLmencloseFrame::AttributeChanged(int32_t aNameSpaceID, 591 nsAtom* aAttribute, 592 AttrModType aModType) { 593 if (aNameSpaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::notation) { 594 InitNotations(); 595 PresShell()->FrameNeedsReflow(this, IntrinsicDirty::FrameAndAncestors, 596 NS_FRAME_IS_DIRTY); 597 return NS_OK; 598 } 599 600 return nsMathMLContainerFrame::AttributeChanged(aNameSpaceID, aAttribute, 601 aModType); 602 } 603 604 void nsMathMLmencloseFrame::DidSetComputedStyle(ComputedStyle* aOldStyle) { 605 nsMathMLContainerFrame::DidSetComputedStyle(aOldStyle); 606 for (auto& ch : mMathMLChar) { 607 ch.SetComputedStyle(Style()); 608 } 609 } 610 611 ////////////////// 612 613 namespace mozilla { 614 615 class nsDisplayNotation final : public nsPaintedDisplayItem { 616 public: 617 nsDisplayNotation(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, 618 const nsRect& aRect, nscoord aThickness, 619 nsMencloseNotation aType) 620 : nsPaintedDisplayItem(aBuilder, aFrame), 621 mRect(aRect), 622 mThickness(aThickness), 623 mType(aType) { 624 MOZ_COUNT_CTOR(nsDisplayNotation); 625 } 626 627 MOZ_COUNTED_DTOR_FINAL(nsDisplayNotation) 628 629 void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override; 630 NS_DISPLAY_DECL_NAME("MathMLMencloseNotation", TYPE_MATHML_MENCLOSE_NOTATION) 631 632 private: 633 nsRect mRect; 634 nscoord mThickness; 635 nsMencloseNotation mType; 636 }; 637 638 void nsDisplayNotation::Paint(nsDisplayListBuilder* aBuilder, 639 gfxContext* aCtx) { 640 DrawTarget& aDrawTarget = *aCtx->GetDrawTarget(); 641 nsPresContext* presContext = mFrame->PresContext(); 642 643 Float strokeWidth = presContext->AppUnitsToGfxUnits(mThickness); 644 645 Rect rect = NSRectToRect(mRect + ToReferenceFrame(), 646 presContext->AppUnitsPerDevPixel()); 647 rect.Deflate(strokeWidth / 2.f); 648 649 ColorPattern color(ToDeviceColor( 650 mFrame->GetVisitedDependentColor(&nsStyleText::mWebkitTextFillColor))); 651 652 StrokeOptions strokeOptions(strokeWidth); 653 654 switch (mType) { 655 case NOTATION_CIRCLE: { 656 RefPtr<Path> ellipse = 657 MakePathForEllipse(aDrawTarget, rect.Center(), rect.Size()); 658 aDrawTarget.Stroke(ellipse, color, strokeOptions); 659 return; 660 } 661 case NOTATION_ROUNDEDBOX: { 662 Float radius = 3 * strokeWidth; 663 RectCornerRadii radii(radius, radius); 664 RefPtr<Path> roundedRect = 665 MakePathForRoundedRect(aDrawTarget, rect, radii, true); 666 aDrawTarget.Stroke(roundedRect, color, strokeOptions); 667 return; 668 } 669 case NOTATION_UPDIAGONALSTRIKE: { 670 aDrawTarget.StrokeLine(rect.BottomLeft(), rect.TopRight(), color, 671 strokeOptions); 672 return; 673 } 674 case NOTATION_DOWNDIAGONALSTRIKE: { 675 aDrawTarget.StrokeLine(rect.TopLeft(), rect.BottomRight(), color, 676 strokeOptions); 677 return; 678 } 679 case NOTATION_UPDIAGONALARROW: { 680 // Compute some parameters to draw the updiagonalarrow. The values below 681 // are taken from MathJax's HTML-CSS output. 682 Float W = rect.Width(); 683 gfxFloat H = rect.Height(); 684 Float l = sqrt(W * W + H * H); 685 Float f = Float(kArrowHeadSize) * strokeWidth / l; 686 Float w = W * f; 687 gfxFloat h = H * f; 688 689 // Draw the arrow shaft 690 aDrawTarget.StrokeLine(rect.BottomLeft(), 691 rect.TopRight() + Point(-.7 * w, .7 * h), color, 692 strokeOptions); 693 694 // Draw the arrow head 695 RefPtr<PathBuilder> builder = aDrawTarget.CreatePathBuilder(); 696 builder->MoveTo(rect.TopRight()); 697 builder->LineTo( 698 rect.TopRight() + 699 Point(-w - .4 * h, std::max(-strokeWidth / 2.0, h - .4 * w))); 700 builder->LineTo(rect.TopRight() + Point(-.7 * w, .7 * h)); 701 builder->LineTo( 702 rect.TopRight() + 703 Point(std::min(strokeWidth / 2.0, -w + .4 * h), h + .4 * w)); 704 builder->Close(); 705 RefPtr<Path> path = builder->Finish(); 706 aDrawTarget.Fill(path, color); 707 return; 708 } 709 case NOTATION_PHASORANGLE: { 710 // Compute some parameters to draw the angled line, 711 // that uses a slope of 2 (angle = tan^-1(2)). 712 // H = w * tan(angle) = w * 2 713 Float w = Float(kPhasorangleWidth) * strokeWidth; 714 Float H = 2 * w; 715 716 // Draw the angled line 717 aDrawTarget.StrokeLine(rect.BottomLeft(), 718 rect.BottomLeft() + Point(w, -H), color, 719 strokeOptions); 720 return; 721 } 722 default: 723 MOZ_ASSERT_UNREACHABLE( 724 "This notation can not be drawn using " 725 "nsDisplayNotation"); 726 } 727 } 728 729 } // namespace mozilla 730 731 void nsMathMLmencloseFrame::DisplayNotation(nsDisplayListBuilder* aBuilder, 732 nsIFrame* aFrame, 733 const nsRect& aRect, 734 const nsDisplayListSet& aLists, 735 nscoord aThickness, 736 nsMencloseNotation aType) { 737 if (!aFrame->StyleVisibility()->IsVisible() || aRect.IsEmpty() || 738 aThickness <= 0) { 739 return; 740 } 741 742 const uint16_t index = aType; 743 aLists.Content()->AppendNewToTopWithIndex<nsDisplayNotation>( 744 aBuilder, aFrame, index, aRect, aThickness, aType); 745 }