Bumping manifests a=b2g-bump
[gecko.git] / layout / mathml / nsMathMLmencloseFrame.cpp
blob5b50ec1a73e9171a117c7fc6e27f7be3d5bd3775
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/. */
6 #include "nsMathMLmencloseFrame.h"
8 #include "gfx2DGlue.h"
9 #include "gfxUtils.h"
10 #include "mozilla/gfx/2D.h"
11 #include "mozilla/gfx/PathHelpers.h"
12 #include "nsPresContext.h"
13 #include "nsRenderingContext.h"
14 #include "nsWhitespaceTokenizer.h"
16 #include "nsDisplayList.h"
17 #include "gfxContext.h"
18 #include "nsMathMLChar.h"
19 #include <algorithm>
21 using namespace mozilla;
22 using namespace mozilla::gfx;
25 // <menclose> -- enclose content with a stretching symbol such
26 // as a long division sign. - implementation
28 // longdiv:
29 // Unicode 5.1 assigns U+27CC to LONG DIVISION, but a right parenthesis
30 // renders better with current font support.
31 static const char16_t kLongDivChar = ')';
33 // radical: 'SQUARE ROOT'
34 static const char16_t kRadicalChar = 0x221A;
36 // updiagonalstrike
37 static const uint8_t kArrowHeadSize = 10;
39 // phasorangle
40 static const uint8_t kPhasorangleWidth = 8;
42 nsIFrame*
43 NS_NewMathMLmencloseFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
45 return new (aPresShell) nsMathMLmencloseFrame(aContext);
48 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmencloseFrame)
50 nsMathMLmencloseFrame::nsMathMLmencloseFrame(nsStyleContext* aContext) :
51 nsMathMLContainerFrame(aContext), mNotationsToDraw(0),
52 mLongDivCharIndex(-1), mRadicalCharIndex(-1), mContentWidth(0)
56 nsMathMLmencloseFrame::~nsMathMLmencloseFrame()
60 nsresult nsMathMLmencloseFrame::AllocateMathMLChar(nsMencloseNotation mask)
62 // Is the char already allocated?
63 if ((mask == NOTATION_LONGDIV && mLongDivCharIndex >= 0) ||
64 (mask == NOTATION_RADICAL && mRadicalCharIndex >= 0))
65 return NS_OK;
67 // No need to track the style context given to our MathML chars.
68 // The Style System will use Get/SetAdditionalStyleContext() to keep it
69 // up-to-date if dynamic changes arise.
70 uint32_t i = mMathMLChar.Length();
71 nsAutoString Char;
73 if (!mMathMLChar.AppendElement())
74 return NS_ERROR_OUT_OF_MEMORY;
76 if (mask == NOTATION_LONGDIV) {
77 Char.Assign(kLongDivChar);
78 mLongDivCharIndex = i;
79 } else if (mask == NOTATION_RADICAL) {
80 Char.Assign(kRadicalChar);
81 mRadicalCharIndex = i;
84 nsPresContext *presContext = PresContext();
85 mMathMLChar[i].SetData(presContext, Char);
86 ResolveMathMLCharStyle(presContext, mContent, mStyleContext, &mMathMLChar[i]);
88 return NS_OK;
92 * Add a notation to draw, if the argument is the name of a known notation.
93 * @param aNotation string name of a notation
95 nsresult nsMathMLmencloseFrame::AddNotation(const nsAString& aNotation)
97 nsresult rv;
99 if (aNotation.EqualsLiteral("longdiv")) {
100 rv = AllocateMathMLChar(NOTATION_LONGDIV);
101 NS_ENSURE_SUCCESS(rv, rv);
102 mNotationsToDraw |= NOTATION_LONGDIV;
103 } else if (aNotation.EqualsLiteral("actuarial")) {
104 mNotationsToDraw |= (NOTATION_RIGHT | NOTATION_TOP);
105 } else if (aNotation.EqualsLiteral("radical")) {
106 rv = AllocateMathMLChar(NOTATION_RADICAL);
107 NS_ENSURE_SUCCESS(rv, rv);
108 mNotationsToDraw |= NOTATION_RADICAL;
109 } else if (aNotation.EqualsLiteral("box")) {
110 mNotationsToDraw |= (NOTATION_LEFT | NOTATION_RIGHT |
111 NOTATION_TOP | NOTATION_BOTTOM);
112 } else if (aNotation.EqualsLiteral("roundedbox")) {
113 mNotationsToDraw |= NOTATION_ROUNDEDBOX;
114 } else if (aNotation.EqualsLiteral("circle")) {
115 mNotationsToDraw |= NOTATION_CIRCLE;
116 } else if (aNotation.EqualsLiteral("left")) {
117 mNotationsToDraw |= NOTATION_LEFT;
118 } else if (aNotation.EqualsLiteral("right")) {
119 mNotationsToDraw |= NOTATION_RIGHT;
120 } else if (aNotation.EqualsLiteral("top")) {
121 mNotationsToDraw |= NOTATION_TOP;
122 } else if (aNotation.EqualsLiteral("bottom")) {
123 mNotationsToDraw |= NOTATION_BOTTOM;
124 } else if (aNotation.EqualsLiteral("updiagonalstrike")) {
125 mNotationsToDraw |= NOTATION_UPDIAGONALSTRIKE;
126 } else if (aNotation.EqualsLiteral("updiagonalarrow")) {
127 mNotationsToDraw |= NOTATION_UPDIAGONALARROW;
128 } else if (aNotation.EqualsLiteral("downdiagonalstrike")) {
129 mNotationsToDraw |= NOTATION_DOWNDIAGONALSTRIKE;
130 } else if (aNotation.EqualsLiteral("verticalstrike")) {
131 mNotationsToDraw |= NOTATION_VERTICALSTRIKE;
132 } else if (aNotation.EqualsLiteral("horizontalstrike")) {
133 mNotationsToDraw |= NOTATION_HORIZONTALSTRIKE;
134 } else if (aNotation.EqualsLiteral("madruwb")) {
135 mNotationsToDraw |= (NOTATION_RIGHT | NOTATION_BOTTOM);
136 } else if (aNotation.EqualsLiteral("phasorangle")) {
137 mNotationsToDraw |= (NOTATION_BOTTOM | NOTATION_PHASORANGLE);
140 return NS_OK;
144 * Initialize the list of notations to draw
146 void nsMathMLmencloseFrame::InitNotations()
148 mNotationsToDraw = 0;
149 mLongDivCharIndex = mRadicalCharIndex = -1;
150 mMathMLChar.Clear();
152 nsAutoString value;
154 if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::notation_, value)) {
155 // parse the notation attribute
156 nsWhitespaceTokenizer tokenizer(value);
158 while (tokenizer.hasMoreTokens())
159 AddNotation(tokenizer.nextToken());
161 if (IsToDraw(NOTATION_UPDIAGONALARROW)) {
162 // For <menclose notation="updiagonalstrike updiagonalarrow">, if
163 // the two notations are drawn then the strike line may cause the point of
164 // the arrow to be too wide. Hence we will only draw the updiagonalarrow
165 // and the arrow shaft may be thought to be the updiagonalstrike.
166 mNotationsToDraw &= ~NOTATION_UPDIAGONALSTRIKE;
168 } else {
169 // default: longdiv
170 if (NS_FAILED(AllocateMathMLChar(NOTATION_LONGDIV)))
171 return;
172 mNotationsToDraw = NOTATION_LONGDIV;
176 NS_IMETHODIMP
177 nsMathMLmencloseFrame::InheritAutomaticData(nsIFrame* aParent)
179 // let the base class get the default from our parent
180 nsMathMLContainerFrame::InheritAutomaticData(aParent);
182 mPresentationData.flags |= NS_MATHML_STRETCH_ALL_CHILDREN_VERTICALLY;
184 InitNotations();
186 return NS_OK;
189 NS_IMETHODIMP
190 nsMathMLmencloseFrame::TransmitAutomaticData()
192 if (IsToDraw(NOTATION_RADICAL)) {
193 // The TeXBook (Ch 17. p.141) says that \sqrt is cramped
194 UpdatePresentationDataFromChildAt(0, -1,
195 NS_MATHML_COMPRESSED,
196 NS_MATHML_COMPRESSED);
199 return NS_OK;
202 void
203 nsMathMLmencloseFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
204 const nsRect& aDirtyRect,
205 const nsDisplayListSet& aLists)
207 /////////////
208 // paint the menclosed content
209 nsMathMLContainerFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
211 if (NS_MATHML_HAS_ERROR(mPresentationData.flags))
212 return;
214 nsRect mencloseRect = nsIFrame::GetRect();
215 mencloseRect.x = mencloseRect.y = 0;
217 if (IsToDraw(NOTATION_RADICAL)) {
218 mMathMLChar[mRadicalCharIndex].Display(aBuilder, this, aLists, 0);
220 nsRect rect;
221 mMathMLChar[mRadicalCharIndex].GetRect(rect);
222 rect.MoveBy(StyleVisibility()->mDirection ? -mContentWidth : rect.width, 0);
223 rect.SizeTo(mContentWidth, mRadicalRuleThickness);
224 DisplayBar(aBuilder, this, rect, aLists);
227 if (IsToDraw(NOTATION_PHASORANGLE)) {
228 DisplayNotation(aBuilder, this, mencloseRect, aLists,
229 mRuleThickness, NOTATION_PHASORANGLE);
232 if (IsToDraw(NOTATION_LONGDIV)) {
233 mMathMLChar[mLongDivCharIndex].Display(aBuilder, this, aLists, 1);
235 nsRect rect;
236 mMathMLChar[mLongDivCharIndex].GetRect(rect);
237 rect.SizeTo(rect.width + mContentWidth, mRuleThickness);
238 DisplayBar(aBuilder, this, rect, aLists);
241 if (IsToDraw(NOTATION_TOP)) {
242 nsRect rect(0, 0, mencloseRect.width, mRuleThickness);
243 DisplayBar(aBuilder, this, rect, aLists);
246 if (IsToDraw(NOTATION_BOTTOM)) {
247 nsRect rect(0, mencloseRect.height - mRuleThickness,
248 mencloseRect.width, mRuleThickness);
249 DisplayBar(aBuilder, this, rect, aLists);
252 if (IsToDraw(NOTATION_LEFT)) {
253 nsRect rect(0, 0, mRuleThickness, mencloseRect.height);
254 DisplayBar(aBuilder, this, rect, aLists);
257 if (IsToDraw(NOTATION_RIGHT)) {
258 nsRect rect(mencloseRect.width - mRuleThickness, 0,
259 mRuleThickness, mencloseRect.height);
260 DisplayBar(aBuilder, this, rect, aLists);
263 if (IsToDraw(NOTATION_ROUNDEDBOX)) {
264 DisplayNotation(aBuilder, this, mencloseRect, aLists,
265 mRuleThickness, NOTATION_ROUNDEDBOX);
268 if (IsToDraw(NOTATION_CIRCLE)) {
269 DisplayNotation(aBuilder, this, mencloseRect, aLists,
270 mRuleThickness, NOTATION_CIRCLE);
273 if (IsToDraw(NOTATION_UPDIAGONALSTRIKE)) {
274 DisplayNotation(aBuilder, this, mencloseRect, aLists,
275 mRuleThickness, NOTATION_UPDIAGONALSTRIKE);
278 if (IsToDraw(NOTATION_UPDIAGONALARROW)) {
279 DisplayNotation(aBuilder, this, mencloseRect, aLists,
280 mRuleThickness, NOTATION_UPDIAGONALARROW);
283 if (IsToDraw(NOTATION_DOWNDIAGONALSTRIKE)) {
284 DisplayNotation(aBuilder, this, mencloseRect, aLists,
285 mRuleThickness, NOTATION_DOWNDIAGONALSTRIKE);
288 if (IsToDraw(NOTATION_HORIZONTALSTRIKE)) {
289 nsRect rect(0, mencloseRect.height / 2 - mRuleThickness / 2,
290 mencloseRect.width, mRuleThickness);
291 DisplayBar(aBuilder, this, rect, aLists);
294 if (IsToDraw(NOTATION_VERTICALSTRIKE)) {
295 nsRect rect(mencloseRect.width / 2 - mRuleThickness / 2, 0,
296 mRuleThickness, mencloseRect.height);
297 DisplayBar(aBuilder, this, rect, aLists);
301 /* virtual */ nsresult
302 nsMathMLmencloseFrame::MeasureForWidth(nsRenderingContext& aRenderingContext,
303 nsHTMLReflowMetrics& aDesiredSize)
305 return PlaceInternal(aRenderingContext, false, aDesiredSize, true);
308 /* virtual */ nsresult
309 nsMathMLmencloseFrame::Place(nsRenderingContext& aRenderingContext,
310 bool aPlaceOrigin,
311 nsHTMLReflowMetrics& aDesiredSize)
313 return PlaceInternal(aRenderingContext, aPlaceOrigin, aDesiredSize, false);
316 /* virtual */ nsresult
317 nsMathMLmencloseFrame::PlaceInternal(nsRenderingContext& aRenderingContext,
318 bool aPlaceOrigin,
319 nsHTMLReflowMetrics& aDesiredSize,
320 bool aWidthOnly)
322 ///////////////
323 // Measure the size of our content using the base class to format like an
324 // inferred mrow.
325 nsHTMLReflowMetrics baseSize(aDesiredSize.GetWritingMode());
326 nsresult rv =
327 nsMathMLContainerFrame::Place(aRenderingContext, false, baseSize);
329 if (NS_MATHML_HAS_ERROR(mPresentationData.flags) || NS_FAILED(rv)) {
330 DidReflowChildren(GetFirstPrincipalChild());
331 return rv;
334 nsBoundingMetrics bmBase = baseSize.mBoundingMetrics;
335 nscoord dx_left = 0, dx_right = 0;
336 nsBoundingMetrics bmLongdivChar, bmRadicalChar;
337 nscoord radicalAscent = 0, radicalDescent = 0;
338 nscoord longdivAscent = 0, longdivDescent = 0;
339 nscoord psi = 0;
340 nscoord leading = 0;
342 ///////////////
343 // Thickness of bars and font metrics
344 nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
346 float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
347 nsRefPtr<nsFontMetrics> fm;
348 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm),
349 fontSizeInflation);
350 GetRuleThickness(aRenderingContext, fm, mRuleThickness);
351 if (mRuleThickness < onePixel) {
352 mRuleThickness = onePixel;
355 char16_t one = '1';
356 nsBoundingMetrics bmOne =
357 nsLayoutUtils::AppUnitBoundsOfString(&one, 1, *fm, aRenderingContext);
359 ///////////////
360 // General rules: the menclose element takes the size of the enclosed content.
361 // We add a padding when needed.
363 // determine padding & psi
364 nscoord padding = 3 * mRuleThickness;
365 nscoord delta = padding % onePixel;
366 if (delta)
367 padding += onePixel - delta; // round up
369 if (IsToDraw(NOTATION_LONGDIV) || IsToDraw(NOTATION_RADICAL)) {
370 GetRadicalParameters(fm, StyleFont()->mMathDisplay ==
371 NS_MATHML_DISPLAYSTYLE_BLOCK,
372 mRadicalRuleThickness, leading, psi);
374 // make sure that the rule appears on on screen
375 if (mRadicalRuleThickness < onePixel) {
376 mRadicalRuleThickness = onePixel;
379 // adjust clearance psi to get an exact number of pixels -- this
380 // gives a nicer & uniform look on stacked radicals (bug 130282)
381 delta = psi % onePixel;
382 if (delta) {
383 psi += onePixel - delta; // round up
387 // Set horizontal parameters
388 if (IsToDraw(NOTATION_ROUNDEDBOX) ||
389 IsToDraw(NOTATION_TOP) ||
390 IsToDraw(NOTATION_LEFT) ||
391 IsToDraw(NOTATION_BOTTOM) ||
392 IsToDraw(NOTATION_CIRCLE))
393 dx_left = padding;
395 if (IsToDraw(NOTATION_ROUNDEDBOX) ||
396 IsToDraw(NOTATION_TOP) ||
397 IsToDraw(NOTATION_RIGHT) ||
398 IsToDraw(NOTATION_BOTTOM) ||
399 IsToDraw(NOTATION_CIRCLE))
400 dx_right = padding;
402 // Set vertical parameters
403 if (IsToDraw(NOTATION_RIGHT) ||
404 IsToDraw(NOTATION_LEFT) ||
405 IsToDraw(NOTATION_UPDIAGONALSTRIKE) ||
406 IsToDraw(NOTATION_UPDIAGONALARROW) ||
407 IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) ||
408 IsToDraw(NOTATION_VERTICALSTRIKE) ||
409 IsToDraw(NOTATION_CIRCLE) ||
410 IsToDraw(NOTATION_ROUNDEDBOX) ||
411 IsToDraw(NOTATION_RADICAL) ||
412 IsToDraw(NOTATION_LONGDIV) ||
413 IsToDraw(NOTATION_PHASORANGLE)) {
414 // set a minimal value for the base height
415 bmBase.ascent = std::max(bmOne.ascent, bmBase.ascent);
416 bmBase.descent = std::max(0, bmBase.descent);
419 mBoundingMetrics.ascent = bmBase.ascent;
420 mBoundingMetrics.descent = bmBase.descent;
422 if (IsToDraw(NOTATION_ROUNDEDBOX) ||
423 IsToDraw(NOTATION_TOP) ||
424 IsToDraw(NOTATION_LEFT) ||
425 IsToDraw(NOTATION_RIGHT) ||
426 IsToDraw(NOTATION_CIRCLE))
427 mBoundingMetrics.ascent += padding;
429 if (IsToDraw(NOTATION_ROUNDEDBOX) ||
430 IsToDraw(NOTATION_LEFT) ||
431 IsToDraw(NOTATION_RIGHT) ||
432 IsToDraw(NOTATION_BOTTOM) ||
433 IsToDraw(NOTATION_CIRCLE))
434 mBoundingMetrics.descent += padding;
436 ///////////////
437 // phasorangle notation
438 if (IsToDraw(NOTATION_PHASORANGLE)) {
439 nscoord phasorangleWidth = kPhasorangleWidth * mRuleThickness;
440 // Update horizontal parameters
441 dx_left = std::max(dx_left, phasorangleWidth);
444 ///////////////
445 // updiagonal arrow notation. We need enough space at the top right corner to
446 // draw the arrow head.
447 if (IsToDraw(NOTATION_UPDIAGONALARROW)) {
448 // This is an estimate, see nsDisplayNotation::Paint for the exact head size
449 nscoord arrowHeadSize = kArrowHeadSize * mRuleThickness;
451 // We want that the arrow shaft strikes the menclose content and that the
452 // arrow head does not overlap with that content. Hence we add some space
453 // on the right. We don't add space on the top but only ensure that the
454 // ascent is large enough.
455 dx_right = std::max(dx_right, arrowHeadSize);
456 mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent, arrowHeadSize);
459 ///////////////
460 // circle notation: we don't want the ellipse to overlap the enclosed
461 // content. Hence, we need to increase the size of the bounding box by a
462 // factor of at least sqrt(2).
463 if (IsToDraw(NOTATION_CIRCLE)) {
464 double ratio = (sqrt(2.0) - 1.0) / 2.0;
465 nscoord padding2;
467 // Update horizontal parameters
468 padding2 = ratio * bmBase.width;
470 dx_left = std::max(dx_left, padding2);
471 dx_right = std::max(dx_right, padding2);
473 // Update vertical parameters
474 padding2 = ratio * (bmBase.ascent + bmBase.descent);
476 mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent,
477 bmBase.ascent + padding2);
478 mBoundingMetrics.descent = std::max(mBoundingMetrics.descent,
479 bmBase.descent + padding2);
482 ///////////////
483 // longdiv notation:
484 if (IsToDraw(NOTATION_LONGDIV)) {
485 if (aWidthOnly) {
486 nscoord longdiv_width = mMathMLChar[mLongDivCharIndex].
487 GetMaxWidth(PresContext(), aRenderingContext, fontSizeInflation);
489 // Update horizontal parameters
490 dx_left = std::max(dx_left, longdiv_width);
491 } else {
492 // Stretch the parenthesis to the appropriate height if it is not
493 // big enough.
494 nsBoundingMetrics contSize = bmBase;
495 contSize.ascent = mRuleThickness;
496 contSize.descent = bmBase.ascent + bmBase.descent + psi;
498 // height(longdiv) should be >= height(base) + psi + mRuleThickness
499 mMathMLChar[mLongDivCharIndex].Stretch(PresContext(), aRenderingContext,
500 fontSizeInflation,
501 NS_STRETCH_DIRECTION_VERTICAL,
502 contSize, bmLongdivChar,
503 NS_STRETCH_LARGER, false);
504 mMathMLChar[mLongDivCharIndex].GetBoundingMetrics(bmLongdivChar);
506 // Update horizontal parameters
507 dx_left = std::max(dx_left, bmLongdivChar.width);
509 // Update vertical parameters
510 longdivAscent = bmBase.ascent + psi + mRuleThickness;
511 longdivDescent = std::max(bmBase.descent,
512 (bmLongdivChar.ascent + bmLongdivChar.descent -
513 longdivAscent));
515 mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent,
516 longdivAscent);
517 mBoundingMetrics.descent = std::max(mBoundingMetrics.descent,
518 longdivDescent);
522 ///////////////
523 // radical notation:
524 if (IsToDraw(NOTATION_RADICAL)) {
525 nscoord *dx_leading = StyleVisibility()->mDirection ? &dx_right : &dx_left;
527 if (aWidthOnly) {
528 nscoord radical_width = mMathMLChar[mRadicalCharIndex].
529 GetMaxWidth(PresContext(), aRenderingContext, fontSizeInflation);
531 // Update horizontal parameters
532 *dx_leading = std::max(*dx_leading, radical_width);
533 } else {
534 // Stretch the radical symbol to the appropriate height if it is not
535 // big enough.
536 nsBoundingMetrics contSize = bmBase;
537 contSize.ascent = mRadicalRuleThickness;
538 contSize.descent = bmBase.ascent + bmBase.descent + psi;
540 // height(radical) should be >= height(base) + psi + mRadicalRuleThickness
541 mMathMLChar[mRadicalCharIndex].Stretch(PresContext(), aRenderingContext,
542 fontSizeInflation,
543 NS_STRETCH_DIRECTION_VERTICAL,
544 contSize, bmRadicalChar,
545 NS_STRETCH_LARGER,
546 StyleVisibility()->mDirection);
547 mMathMLChar[mRadicalCharIndex].GetBoundingMetrics(bmRadicalChar);
549 // Update horizontal parameters
550 *dx_leading = std::max(*dx_leading, bmRadicalChar.width);
552 // Update vertical parameters
553 radicalAscent = bmBase.ascent + psi + mRadicalRuleThickness;
554 radicalDescent = std::max(bmBase.descent,
555 (bmRadicalChar.ascent + bmRadicalChar.descent -
556 radicalAscent));
558 mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent,
559 radicalAscent);
560 mBoundingMetrics.descent = std::max(mBoundingMetrics.descent,
561 radicalDescent);
565 ///////////////
567 if (IsToDraw(NOTATION_CIRCLE) ||
568 IsToDraw(NOTATION_ROUNDEDBOX) ||
569 (IsToDraw(NOTATION_LEFT) && IsToDraw(NOTATION_RIGHT))) {
570 // center the menclose around the content (horizontally)
571 dx_left = dx_right = std::max(dx_left, dx_right);
574 ///////////////
575 // The maximum size is now computed: set the remaining parameters
576 mBoundingMetrics.width = dx_left + bmBase.width + dx_right;
578 mBoundingMetrics.leftBearing = std::min(0, dx_left + bmBase.leftBearing);
579 mBoundingMetrics.rightBearing =
580 std::max(mBoundingMetrics.width, dx_left + bmBase.rightBearing);
582 aDesiredSize.Width() = mBoundingMetrics.width;
584 aDesiredSize.SetBlockStartAscent(std::max(mBoundingMetrics.ascent,
585 baseSize.BlockStartAscent()));
586 aDesiredSize.Height() = aDesiredSize.BlockStartAscent() +
587 std::max(mBoundingMetrics.descent,
588 baseSize.Height() - baseSize.BlockStartAscent());
590 if (IsToDraw(NOTATION_LONGDIV) || IsToDraw(NOTATION_RADICAL)) {
591 nscoord desiredSizeAscent = aDesiredSize.BlockStartAscent();
592 nscoord desiredSizeDescent = aDesiredSize.Height() -
593 aDesiredSize.BlockStartAscent();
595 if (IsToDraw(NOTATION_LONGDIV)) {
596 desiredSizeAscent = std::max(desiredSizeAscent,
597 longdivAscent + leading);
598 desiredSizeDescent = std::max(desiredSizeDescent,
599 longdivDescent + mRuleThickness);
602 if (IsToDraw(NOTATION_RADICAL)) {
603 desiredSizeAscent = std::max(desiredSizeAscent,
604 radicalAscent + leading);
605 desiredSizeDescent = std::max(desiredSizeDescent,
606 radicalDescent + mRadicalRuleThickness);
609 aDesiredSize.SetBlockStartAscent(desiredSizeAscent);
610 aDesiredSize.Height() = desiredSizeAscent + desiredSizeDescent;
613 if (IsToDraw(NOTATION_CIRCLE) ||
614 IsToDraw(NOTATION_ROUNDEDBOX) ||
615 (IsToDraw(NOTATION_TOP) && IsToDraw(NOTATION_BOTTOM))) {
616 // center the menclose around the content (vertically)
617 nscoord dy = std::max(aDesiredSize.BlockStartAscent() - bmBase.ascent,
618 aDesiredSize.Height() -
619 aDesiredSize.BlockStartAscent() - bmBase.descent);
621 aDesiredSize.SetBlockStartAscent(bmBase.ascent + dy);
622 aDesiredSize.Height() = aDesiredSize.BlockStartAscent() + bmBase.descent + dy;
625 // Update mBoundingMetrics ascent/descent
626 if (IsToDraw(NOTATION_TOP) ||
627 IsToDraw(NOTATION_RIGHT) ||
628 IsToDraw(NOTATION_LEFT) ||
629 IsToDraw(NOTATION_UPDIAGONALSTRIKE) ||
630 IsToDraw(NOTATION_UPDIAGONALARROW) ||
631 IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) ||
632 IsToDraw(NOTATION_VERTICALSTRIKE) ||
633 IsToDraw(NOTATION_CIRCLE) ||
634 IsToDraw(NOTATION_ROUNDEDBOX))
635 mBoundingMetrics.ascent = aDesiredSize.BlockStartAscent();
637 if (IsToDraw(NOTATION_BOTTOM) ||
638 IsToDraw(NOTATION_RIGHT) ||
639 IsToDraw(NOTATION_LEFT) ||
640 IsToDraw(NOTATION_UPDIAGONALSTRIKE) ||
641 IsToDraw(NOTATION_UPDIAGONALARROW) ||
642 IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) ||
643 IsToDraw(NOTATION_VERTICALSTRIKE) ||
644 IsToDraw(NOTATION_CIRCLE) ||
645 IsToDraw(NOTATION_ROUNDEDBOX))
646 mBoundingMetrics.descent = aDesiredSize.Height() - aDesiredSize.BlockStartAscent();
648 // phasorangle notation:
649 // move up from the bottom by the angled line height
650 if (IsToDraw(NOTATION_PHASORANGLE))
651 mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent, 2 * kPhasorangleWidth * mRuleThickness - mBoundingMetrics.descent);
653 aDesiredSize.mBoundingMetrics = mBoundingMetrics;
655 mReference.x = 0;
656 mReference.y = aDesiredSize.BlockStartAscent();
658 if (aPlaceOrigin) {
659 //////////////////
660 // Set position and size of MathMLChars
661 if (IsToDraw(NOTATION_LONGDIV))
662 mMathMLChar[mLongDivCharIndex].SetRect(nsRect(dx_left -
663 bmLongdivChar.width,
664 aDesiredSize.BlockStartAscent() -
665 longdivAscent,
666 bmLongdivChar.width,
667 bmLongdivChar.ascent +
668 bmLongdivChar.descent));
670 if (IsToDraw(NOTATION_RADICAL)) {
671 nscoord dx = (StyleVisibility()->mDirection ?
672 dx_left + bmBase.width : dx_left - bmRadicalChar.width);
674 mMathMLChar[mRadicalCharIndex].SetRect(nsRect(dx,
675 aDesiredSize.BlockStartAscent() -
676 radicalAscent,
677 bmRadicalChar.width,
678 bmRadicalChar.ascent +
679 bmRadicalChar.descent));
682 mContentWidth = bmBase.width;
684 //////////////////
685 // Finish reflowing child frames
686 PositionRowChildFrames(dx_left, aDesiredSize.BlockStartAscent());
689 return NS_OK;
692 nscoord
693 nsMathMLmencloseFrame::FixInterFrameSpacing(nsHTMLReflowMetrics& aDesiredSize)
695 nscoord gap = nsMathMLContainerFrame::FixInterFrameSpacing(aDesiredSize);
696 if (!gap)
697 return 0;
699 // Move the MathML characters
700 nsRect rect;
701 for (uint32_t i = 0; i < mMathMLChar.Length(); i++) {
702 mMathMLChar[i].GetRect(rect);
703 rect.MoveBy(gap, 0);
704 mMathMLChar[i].SetRect(rect);
707 return gap;
710 nsresult
711 nsMathMLmencloseFrame::AttributeChanged(int32_t aNameSpaceID,
712 nsIAtom* aAttribute,
713 int32_t aModType)
715 if (aAttribute == nsGkAtoms::notation_) {
716 InitNotations();
719 return nsMathMLContainerFrame::
720 AttributeChanged(aNameSpaceID, aAttribute, aModType);
723 //////////////////
724 // the Style System will use these to pass the proper style context to our
725 // MathMLChar
726 nsStyleContext*
727 nsMathMLmencloseFrame::GetAdditionalStyleContext(int32_t aIndex) const
729 int32_t len = mMathMLChar.Length();
730 if (aIndex >= 0 && aIndex < len)
731 return mMathMLChar[aIndex].GetStyleContext();
732 else
733 return nullptr;
736 void
737 nsMathMLmencloseFrame::SetAdditionalStyleContext(int32_t aIndex,
738 nsStyleContext* aStyleContext)
740 int32_t len = mMathMLChar.Length();
741 if (aIndex >= 0 && aIndex < len)
742 mMathMLChar[aIndex].SetStyleContext(aStyleContext);
745 class nsDisplayNotation : public nsDisplayItem
747 public:
748 nsDisplayNotation(nsDisplayListBuilder* aBuilder,
749 nsIFrame* aFrame, const nsRect& aRect,
750 nscoord aThickness, nsMencloseNotation aType)
751 : nsDisplayItem(aBuilder, aFrame), mRect(aRect),
752 mThickness(aThickness), mType(aType) {
753 MOZ_COUNT_CTOR(nsDisplayNotation);
755 #ifdef NS_BUILD_REFCNT_LOGGING
756 virtual ~nsDisplayNotation() {
757 MOZ_COUNT_DTOR(nsDisplayNotation);
759 #endif
761 virtual void Paint(nsDisplayListBuilder* aBuilder,
762 nsRenderingContext* aCtx) MOZ_OVERRIDE;
763 NS_DISPLAY_DECL_NAME("MathMLMencloseNotation", TYPE_MATHML_MENCLOSE_NOTATION)
765 private:
766 nsRect mRect;
767 nscoord mThickness;
768 nsMencloseNotation mType;
771 void nsDisplayNotation::Paint(nsDisplayListBuilder* aBuilder,
772 nsRenderingContext* aCtx)
774 DrawTarget& aDrawTarget = *aCtx->GetDrawTarget();
775 nsPresContext* presContext = mFrame->PresContext();
777 Float strokeWidth = presContext->AppUnitsToGfxUnits(mThickness);
779 Rect rect = NSRectToRect(mRect + ToReferenceFrame(),
780 presContext->AppUnitsPerDevPixel());
781 rect.Deflate(strokeWidth / 2.f);
783 ColorPattern color(ToDeviceColor(
784 mFrame->GetVisitedDependentColor(eCSSProperty_color)));
786 StrokeOptions strokeOptions(strokeWidth);
788 switch(mType)
790 case NOTATION_CIRCLE: {
791 RefPtr<Path> ellipse =
792 MakePathForEllipse(aDrawTarget, rect.Center(), rect.Size());
793 aDrawTarget.Stroke(ellipse, color, strokeOptions);
794 return;
796 case NOTATION_ROUNDEDBOX: {
797 Float radius = 3 * strokeWidth;
798 RectCornerRadii radii(radius, radius);
799 RefPtr<Path> roundedRect =
800 MakePathForRoundedRect(aDrawTarget, rect, radii, true);
801 aDrawTarget.Stroke(roundedRect, color, strokeOptions);
802 return;
804 case NOTATION_UPDIAGONALSTRIKE: {
805 aDrawTarget.StrokeLine(rect.BottomLeft(), rect.TopRight(),
806 color, strokeOptions);
807 return;
809 case NOTATION_DOWNDIAGONALSTRIKE: {
810 aDrawTarget.StrokeLine(rect.TopLeft(), rect.BottomRight(),
811 color, strokeOptions);
812 return;
814 case NOTATION_UPDIAGONALARROW: {
815 // Compute some parameters to draw the updiagonalarrow. The values below
816 // are taken from MathJax's HTML-CSS output.
817 Float W = rect.Width(); gfxFloat H = rect.Height();
818 Float l = sqrt(W*W + H*H);
819 Float f = Float(kArrowHeadSize) * strokeWidth / l;
820 Float w = W * f; gfxFloat h = H * f;
822 // Draw the arrow shaft
823 aDrawTarget.StrokeLine(rect.BottomLeft(),
824 rect.TopRight() + Point(-.7*w, .7*h),
825 color, strokeOptions);
827 // Draw the arrow head
828 RefPtr<PathBuilder> builder = aDrawTarget.CreatePathBuilder();
829 builder->MoveTo(rect.TopRight());
830 builder->LineTo(rect.TopRight() + Point(-w -.4*h, std::max(-strokeWidth / 2.0, h - .4*w)));
831 builder->LineTo(rect.TopRight() + Point(-.7*w, .7*h));
832 builder->LineTo(rect.TopRight() + Point(std::min(strokeWidth / 2.0, -w + .4*h), h + .4*w));
833 builder->Close();
834 RefPtr<Path> path = builder->Finish();
835 aDrawTarget.Fill(path, color);
836 return;
838 case NOTATION_PHASORANGLE: {
839 // Compute some parameters to draw the angled line,
840 // that uses a slope of 2 (angle = tan^-1(2)).
841 // H = w * tan(angle) = w * 2
842 Float w = Float(kPhasorangleWidth) * strokeWidth;
843 Float H = 2 * w;
845 // Draw the angled line
846 aDrawTarget.StrokeLine(rect.BottomLeft(),
847 rect.BottomLeft() + Point(w, -H),
848 color, strokeOptions);
849 return;
851 default:
852 NS_NOTREACHED("This notation can not be drawn using nsDisplayNotation");
856 void
857 nsMathMLmencloseFrame::DisplayNotation(nsDisplayListBuilder* aBuilder,
858 nsIFrame* aFrame, const nsRect& aRect,
859 const nsDisplayListSet& aLists,
860 nscoord aThickness,
861 nsMencloseNotation aType)
863 if (!aFrame->StyleVisibility()->IsVisible() || aRect.IsEmpty() ||
864 aThickness <= 0)
865 return;
867 aLists.Content()->AppendNewToTop(new (aBuilder)
868 nsDisplayNotation(aBuilder, aFrame, aRect, aThickness, aType));