Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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"
7 #include "nsPresContext.h"
8 #include "nsRenderingContext.h"
9 #include "nsWhitespaceTokenizer.h"
11 #include "nsDisplayList.h"
12 #include "gfxContext.h"
13 #include "nsMathMLChar.h"
14 #include <algorithm>
16 //
17 // <menclose> -- enclose content with a stretching symbol such
18 // as a long division sign. - implementation
20 // longdiv:
21 // Unicode 5.1 assigns U+27CC to LONG DIVISION, but a right parenthesis
22 // renders better with current font support.
23 static const char16_t kLongDivChar = ')';
25 // radical: 'SQUARE ROOT'
26 static const char16_t kRadicalChar = 0x221A;
28 // updiagonalstrike
29 static const uint8_t kArrowHeadSize = 10;
31 nsIFrame*
32 NS_NewMathMLmencloseFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
33 {
34 return new (aPresShell) nsMathMLmencloseFrame(aContext);
35 }
37 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmencloseFrame)
39 nsMathMLmencloseFrame::nsMathMLmencloseFrame(nsStyleContext* aContext) :
40 nsMathMLContainerFrame(aContext), mNotationsToDraw(0),
41 mLongDivCharIndex(-1), mRadicalCharIndex(-1), mContentWidth(0)
42 {
43 }
45 nsMathMLmencloseFrame::~nsMathMLmencloseFrame()
46 {
47 }
49 nsresult nsMathMLmencloseFrame::AllocateMathMLChar(nsMencloseNotation mask)
50 {
51 // Is the char already allocated?
52 if ((mask == NOTATION_LONGDIV && mLongDivCharIndex >= 0) ||
53 (mask == NOTATION_RADICAL && mRadicalCharIndex >= 0))
54 return NS_OK;
56 // No need to track the style context given to our MathML chars.
57 // The Style System will use Get/SetAdditionalStyleContext() to keep it
58 // up-to-date if dynamic changes arise.
59 uint32_t i = mMathMLChar.Length();
60 nsAutoString Char;
62 if (!mMathMLChar.AppendElement())
63 return NS_ERROR_OUT_OF_MEMORY;
65 if (mask == NOTATION_LONGDIV) {
66 Char.Assign(kLongDivChar);
67 mLongDivCharIndex = i;
68 } else if (mask == NOTATION_RADICAL) {
69 Char.Assign(kRadicalChar);
70 mRadicalCharIndex = i;
71 }
73 nsPresContext *presContext = PresContext();
74 mMathMLChar[i].SetData(presContext, Char);
75 ResolveMathMLCharStyle(presContext, mContent, mStyleContext, &mMathMLChar[i]);
77 return NS_OK;
78 }
80 /*
81 * Add a notation to draw, if the argument is the name of a known notation.
82 * @param aNotation string name of a notation
83 */
84 nsresult nsMathMLmencloseFrame::AddNotation(const nsAString& aNotation)
85 {
86 nsresult rv;
88 if (aNotation.EqualsLiteral("longdiv")) {
89 rv = AllocateMathMLChar(NOTATION_LONGDIV);
90 NS_ENSURE_SUCCESS(rv, rv);
91 mNotationsToDraw |= NOTATION_LONGDIV;
92 } else if (aNotation.EqualsLiteral("actuarial")) {
93 mNotationsToDraw |= (NOTATION_RIGHT | NOTATION_TOP);
94 } else if (aNotation.EqualsLiteral("radical")) {
95 rv = AllocateMathMLChar(NOTATION_RADICAL);
96 NS_ENSURE_SUCCESS(rv, rv);
97 mNotationsToDraw |= NOTATION_RADICAL;
98 } else if (aNotation.EqualsLiteral("box")) {
99 mNotationsToDraw |= (NOTATION_LEFT | NOTATION_RIGHT |
100 NOTATION_TOP | NOTATION_BOTTOM);
101 } else if (aNotation.EqualsLiteral("roundedbox")) {
102 mNotationsToDraw |= NOTATION_ROUNDEDBOX;
103 } else if (aNotation.EqualsLiteral("circle")) {
104 mNotationsToDraw |= NOTATION_CIRCLE;
105 } else if (aNotation.EqualsLiteral("left")) {
106 mNotationsToDraw |= NOTATION_LEFT;
107 } else if (aNotation.EqualsLiteral("right")) {
108 mNotationsToDraw |= NOTATION_RIGHT;
109 } else if (aNotation.EqualsLiteral("top")) {
110 mNotationsToDraw |= NOTATION_TOP;
111 } else if (aNotation.EqualsLiteral("bottom")) {
112 mNotationsToDraw |= NOTATION_BOTTOM;
113 } else if (aNotation.EqualsLiteral("updiagonalstrike")) {
114 mNotationsToDraw |= NOTATION_UPDIAGONALSTRIKE;
115 } else if (aNotation.EqualsLiteral("updiagonalarrow")) {
116 mNotationsToDraw |= NOTATION_UPDIAGONALARROW;
117 } else if (aNotation.EqualsLiteral("downdiagonalstrike")) {
118 mNotationsToDraw |= NOTATION_DOWNDIAGONALSTRIKE;
119 } else if (aNotation.EqualsLiteral("verticalstrike")) {
120 mNotationsToDraw |= NOTATION_VERTICALSTRIKE;
121 } else if (aNotation.EqualsLiteral("horizontalstrike")) {
122 mNotationsToDraw |= NOTATION_HORIZONTALSTRIKE;
123 } else if (aNotation.EqualsLiteral("madruwb")) {
124 mNotationsToDraw |= (NOTATION_RIGHT | NOTATION_BOTTOM);
125 }
127 return NS_OK;
128 }
130 /*
131 * Initialize the list of notations to draw
132 */
133 void nsMathMLmencloseFrame::InitNotations()
134 {
135 mNotationsToDraw = 0;
136 mLongDivCharIndex = mRadicalCharIndex = -1;
137 mMathMLChar.Clear();
139 nsAutoString value;
141 if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::notation_, value)) {
142 // parse the notation attribute
143 nsWhitespaceTokenizer tokenizer(value);
145 while (tokenizer.hasMoreTokens())
146 AddNotation(tokenizer.nextToken());
148 if (IsToDraw(NOTATION_UPDIAGONALARROW)) {
149 // For <menclose notation="updiagonalstrike updiagonalarrow">, if
150 // the two notations are drawn then the strike line may cause the point of
151 // the arrow to be too wide. Hence we will only draw the updiagonalarrow
152 // and the arrow shaft may be thought to be the updiagonalstrike.
153 mNotationsToDraw &= ~NOTATION_UPDIAGONALSTRIKE;
154 }
155 } else {
156 // default: longdiv
157 if (NS_FAILED(AllocateMathMLChar(NOTATION_LONGDIV)))
158 return;
159 mNotationsToDraw = NOTATION_LONGDIV;
160 }
161 }
163 NS_IMETHODIMP
164 nsMathMLmencloseFrame::InheritAutomaticData(nsIFrame* aParent)
165 {
166 // let the base class get the default from our parent
167 nsMathMLContainerFrame::InheritAutomaticData(aParent);
169 mPresentationData.flags |= NS_MATHML_STRETCH_ALL_CHILDREN_VERTICALLY;
171 InitNotations();
173 return NS_OK;
174 }
176 NS_IMETHODIMP
177 nsMathMLmencloseFrame::TransmitAutomaticData()
178 {
179 if (IsToDraw(NOTATION_RADICAL)) {
180 // The TeXBook (Ch 17. p.141) says that \sqrt is cramped
181 UpdatePresentationDataFromChildAt(0, -1,
182 NS_MATHML_COMPRESSED,
183 NS_MATHML_COMPRESSED);
184 }
186 return NS_OK;
187 }
189 void
190 nsMathMLmencloseFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
191 const nsRect& aDirtyRect,
192 const nsDisplayListSet& aLists)
193 {
194 /////////////
195 // paint the menclosed content
196 nsMathMLContainerFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
198 if (NS_MATHML_HAS_ERROR(mPresentationData.flags))
199 return;
201 nsRect mencloseRect = nsIFrame::GetRect();
202 mencloseRect.x = mencloseRect.y = 0;
204 if (IsToDraw(NOTATION_RADICAL)) {
205 mMathMLChar[mRadicalCharIndex].Display(aBuilder, this, aLists, 0);
207 nsRect rect;
208 mMathMLChar[mRadicalCharIndex].GetRect(rect);
209 rect.MoveBy(StyleVisibility()->mDirection ? -mContentWidth : rect.width, 0);
210 rect.SizeTo(mContentWidth, mRuleThickness);
211 DisplayBar(aBuilder, this, rect, aLists);
212 }
214 if (IsToDraw(NOTATION_LONGDIV)) {
215 mMathMLChar[mLongDivCharIndex].Display(aBuilder, this, aLists, 1);
217 nsRect rect;
218 mMathMLChar[mLongDivCharIndex].GetRect(rect);
219 rect.SizeTo(rect.width + mContentWidth, mRuleThickness);
220 DisplayBar(aBuilder, this, rect, aLists);
221 }
223 if (IsToDraw(NOTATION_TOP)) {
224 nsRect rect(0, 0, mencloseRect.width, mRuleThickness);
225 DisplayBar(aBuilder, this, rect, aLists);
226 }
228 if (IsToDraw(NOTATION_BOTTOM)) {
229 nsRect rect(0, mencloseRect.height - mRuleThickness,
230 mencloseRect.width, mRuleThickness);
231 DisplayBar(aBuilder, this, rect, aLists);
232 }
234 if (IsToDraw(NOTATION_LEFT)) {
235 nsRect rect(0, 0, mRuleThickness, mencloseRect.height);
236 DisplayBar(aBuilder, this, rect, aLists);
237 }
239 if (IsToDraw(NOTATION_RIGHT)) {
240 nsRect rect(mencloseRect.width - mRuleThickness, 0,
241 mRuleThickness, mencloseRect.height);
242 DisplayBar(aBuilder, this, rect, aLists);
243 }
245 if (IsToDraw(NOTATION_ROUNDEDBOX)) {
246 DisplayNotation(aBuilder, this, mencloseRect, aLists,
247 mRuleThickness, NOTATION_ROUNDEDBOX);
248 }
250 if (IsToDraw(NOTATION_CIRCLE)) {
251 DisplayNotation(aBuilder, this, mencloseRect, aLists,
252 mRuleThickness, NOTATION_CIRCLE);
253 }
255 if (IsToDraw(NOTATION_UPDIAGONALSTRIKE)) {
256 DisplayNotation(aBuilder, this, mencloseRect, aLists,
257 mRuleThickness, NOTATION_UPDIAGONALSTRIKE);
258 }
260 if (IsToDraw(NOTATION_UPDIAGONALARROW)) {
261 DisplayNotation(aBuilder, this, mencloseRect, aLists,
262 mRuleThickness, NOTATION_UPDIAGONALARROW);
263 }
265 if (IsToDraw(NOTATION_DOWNDIAGONALSTRIKE)) {
266 DisplayNotation(aBuilder, this, mencloseRect, aLists,
267 mRuleThickness, NOTATION_DOWNDIAGONALSTRIKE);
268 }
270 if (IsToDraw(NOTATION_HORIZONTALSTRIKE)) {
271 nsRect rect(0, mencloseRect.height / 2 - mRuleThickness / 2,
272 mencloseRect.width, mRuleThickness);
273 DisplayBar(aBuilder, this, rect, aLists);
274 }
276 if (IsToDraw(NOTATION_VERTICALSTRIKE)) {
277 nsRect rect(mencloseRect.width / 2 - mRuleThickness / 2, 0,
278 mRuleThickness, mencloseRect.height);
279 DisplayBar(aBuilder, this, rect, aLists);
280 }
281 }
283 /* virtual */ nsresult
284 nsMathMLmencloseFrame::MeasureForWidth(nsRenderingContext& aRenderingContext,
285 nsHTMLReflowMetrics& aDesiredSize)
286 {
287 return PlaceInternal(aRenderingContext, false, aDesiredSize, true);
288 }
290 /* virtual */ nsresult
291 nsMathMLmencloseFrame::Place(nsRenderingContext& aRenderingContext,
292 bool aPlaceOrigin,
293 nsHTMLReflowMetrics& aDesiredSize)
294 {
295 return PlaceInternal(aRenderingContext, aPlaceOrigin, aDesiredSize, false);
296 }
298 /* virtual */ nsresult
299 nsMathMLmencloseFrame::PlaceInternal(nsRenderingContext& aRenderingContext,
300 bool aPlaceOrigin,
301 nsHTMLReflowMetrics& aDesiredSize,
302 bool aWidthOnly)
303 {
304 ///////////////
305 // Measure the size of our content using the base class to format like an
306 // inferred mrow.
307 nsHTMLReflowMetrics baseSize(aDesiredSize.GetWritingMode());
308 nsresult rv =
309 nsMathMLContainerFrame::Place(aRenderingContext, false, baseSize);
311 if (NS_MATHML_HAS_ERROR(mPresentationData.flags) || NS_FAILED(rv)) {
312 DidReflowChildren(GetFirstPrincipalChild());
313 return rv;
314 }
316 nsBoundingMetrics bmBase = baseSize.mBoundingMetrics;
317 nscoord dx_left = 0, dx_right = 0;
318 nsBoundingMetrics bmLongdivChar, bmRadicalChar;
319 nscoord radicalAscent = 0, radicalDescent = 0;
320 nscoord longdivAscent = 0, longdivDescent = 0;
321 nscoord psi = 0;
323 ///////////////
324 // Thickness of bars and font metrics
325 nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
327 nscoord mEmHeight;
328 nsRefPtr<nsFontMetrics> fm;
329 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
330 aRenderingContext.SetFont(fm);
331 GetRuleThickness(aRenderingContext, fm, mRuleThickness);
332 GetEmHeight(fm, mEmHeight);
334 char16_t one = '1';
335 nsBoundingMetrics bmOne = aRenderingContext.GetBoundingMetrics(&one, 1);
337 ///////////////
338 // General rules: the menclose element takes the size of the enclosed content.
339 // We add a padding when needed.
341 // determine padding & psi
342 nscoord padding = 3 * mRuleThickness;
343 nscoord delta = padding % onePixel;
344 if (delta)
345 padding += onePixel - delta; // round up
347 if (IsToDraw(NOTATION_LONGDIV) || IsToDraw(NOTATION_RADICAL)) {
348 nscoord phi;
349 // Rule 11, App. G, TeXbook
350 // psi = clearance between rule and content
351 if (StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK)
352 phi = fm->XHeight();
353 else
354 phi = mRuleThickness;
355 psi = mRuleThickness + phi / 4;
357 delta = psi % onePixel;
358 if (delta)
359 psi += onePixel - delta; // round up
360 }
362 if (mRuleThickness < onePixel)
363 mRuleThickness = onePixel;
365 // Set horizontal parameters
366 if (IsToDraw(NOTATION_ROUNDEDBOX) ||
367 IsToDraw(NOTATION_TOP) ||
368 IsToDraw(NOTATION_LEFT) ||
369 IsToDraw(NOTATION_BOTTOM) ||
370 IsToDraw(NOTATION_CIRCLE))
371 dx_left = padding;
373 if (IsToDraw(NOTATION_ROUNDEDBOX) ||
374 IsToDraw(NOTATION_TOP) ||
375 IsToDraw(NOTATION_RIGHT) ||
376 IsToDraw(NOTATION_BOTTOM) ||
377 IsToDraw(NOTATION_CIRCLE))
378 dx_right = padding;
380 // Set vertical parameters
381 if (IsToDraw(NOTATION_RIGHT) ||
382 IsToDraw(NOTATION_LEFT) ||
383 IsToDraw(NOTATION_UPDIAGONALSTRIKE) ||
384 IsToDraw(NOTATION_UPDIAGONALARROW) ||
385 IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) ||
386 IsToDraw(NOTATION_VERTICALSTRIKE) ||
387 IsToDraw(NOTATION_CIRCLE) ||
388 IsToDraw(NOTATION_ROUNDEDBOX) ||
389 IsToDraw(NOTATION_RADICAL) ||
390 IsToDraw(NOTATION_LONGDIV)) {
391 // set a minimal value for the base height
392 bmBase.ascent = std::max(bmOne.ascent, bmBase.ascent);
393 bmBase.descent = std::max(0, bmBase.descent);
394 }
396 mBoundingMetrics.ascent = bmBase.ascent;
397 mBoundingMetrics.descent = bmBase.descent;
399 if (IsToDraw(NOTATION_ROUNDEDBOX) ||
400 IsToDraw(NOTATION_TOP) ||
401 IsToDraw(NOTATION_LEFT) ||
402 IsToDraw(NOTATION_RIGHT) ||
403 IsToDraw(NOTATION_CIRCLE))
404 mBoundingMetrics.ascent += padding;
406 if (IsToDraw(NOTATION_ROUNDEDBOX) ||
407 IsToDraw(NOTATION_LEFT) ||
408 IsToDraw(NOTATION_RIGHT) ||
409 IsToDraw(NOTATION_BOTTOM) ||
410 IsToDraw(NOTATION_CIRCLE))
411 mBoundingMetrics.descent += padding;
413 ///////////////
414 // updiagonal arrow notation. We need enough space at the top right corner to
415 // draw the arrow head.
416 if (IsToDraw(NOTATION_UPDIAGONALARROW)) {
417 // This is an estimate, see nsDisplayNotation::Paint for the exact head size
418 nscoord arrowHeadSize = kArrowHeadSize * mRuleThickness;
420 // We want that the arrow shaft strikes the menclose content and that the
421 // arrow head does not overlap with that content. Hence we add some space
422 // on the right. We don't add space on the top but only ensure that the
423 // ascent is large enough.
424 dx_right = std::max(dx_right, arrowHeadSize);
425 mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent, arrowHeadSize);
426 }
428 ///////////////
429 // circle notation: we don't want the ellipse to overlap the enclosed
430 // content. Hence, we need to increase the size of the bounding box by a
431 // factor of at least sqrt(2).
432 if (IsToDraw(NOTATION_CIRCLE)) {
433 double ratio = (sqrt(2.0) - 1.0) / 2.0;
434 nscoord padding2;
436 // Update horizontal parameters
437 padding2 = ratio * bmBase.width;
439 dx_left = std::max(dx_left, padding2);
440 dx_right = std::max(dx_right, padding2);
442 // Update vertical parameters
443 padding2 = ratio * (bmBase.ascent + bmBase.descent);
445 mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent,
446 bmBase.ascent + padding2);
447 mBoundingMetrics.descent = std::max(mBoundingMetrics.descent,
448 bmBase.descent + padding2);
449 }
451 ///////////////
452 // longdiv notation:
453 if (IsToDraw(NOTATION_LONGDIV)) {
454 if (aWidthOnly) {
455 nscoord longdiv_width = mMathMLChar[mLongDivCharIndex].
456 GetMaxWidth(PresContext(), aRenderingContext);
458 // Update horizontal parameters
459 dx_left = std::max(dx_left, longdiv_width);
460 } else {
461 // Stretch the parenthesis to the appropriate height if it is not
462 // big enough.
463 nsBoundingMetrics contSize = bmBase;
464 contSize.ascent = mRuleThickness;
465 contSize.descent = bmBase.ascent + bmBase.descent + psi;
467 // height(longdiv) should be >= height(base) + psi + mRuleThickness
468 mMathMLChar[mLongDivCharIndex].Stretch(PresContext(), aRenderingContext,
469 NS_STRETCH_DIRECTION_VERTICAL,
470 contSize, bmLongdivChar,
471 NS_STRETCH_LARGER, false);
472 mMathMLChar[mLongDivCharIndex].GetBoundingMetrics(bmLongdivChar);
474 // Update horizontal parameters
475 dx_left = std::max(dx_left, bmLongdivChar.width);
477 // Update vertical parameters
478 longdivAscent = bmBase.ascent + psi + mRuleThickness;
479 longdivDescent = std::max(bmBase.descent,
480 (bmLongdivChar.ascent + bmLongdivChar.descent -
481 longdivAscent));
483 mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent,
484 longdivAscent);
485 mBoundingMetrics.descent = std::max(mBoundingMetrics.descent,
486 longdivDescent);
487 }
488 }
490 ///////////////
491 // radical notation:
492 if (IsToDraw(NOTATION_RADICAL)) {
493 nscoord *dx_leading = StyleVisibility()->mDirection ? &dx_right : &dx_left;
495 if (aWidthOnly) {
496 nscoord radical_width = mMathMLChar[mRadicalCharIndex].
497 GetMaxWidth(PresContext(), aRenderingContext);
499 // Update horizontal parameters
500 *dx_leading = std::max(*dx_leading, radical_width);
501 } else {
502 // Stretch the radical symbol to the appropriate height if it is not
503 // big enough.
504 nsBoundingMetrics contSize = bmBase;
505 contSize.ascent = mRuleThickness;
506 contSize.descent = bmBase.ascent + bmBase.descent + psi;
508 // height(radical) should be >= height(base) + psi + mRuleThickness
509 mMathMLChar[mRadicalCharIndex].Stretch(PresContext(), aRenderingContext,
510 NS_STRETCH_DIRECTION_VERTICAL,
511 contSize, bmRadicalChar,
512 NS_STRETCH_LARGER,
513 StyleVisibility()->mDirection);
514 mMathMLChar[mRadicalCharIndex].GetBoundingMetrics(bmRadicalChar);
516 // Update horizontal parameters
517 *dx_leading = std::max(*dx_leading, bmRadicalChar.width);
519 // Update vertical parameters
520 radicalAscent = bmBase.ascent + psi + mRuleThickness;
521 radicalDescent = std::max(bmBase.descent,
522 (bmRadicalChar.ascent + bmRadicalChar.descent -
523 radicalAscent));
525 mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent,
526 radicalAscent);
527 mBoundingMetrics.descent = std::max(mBoundingMetrics.descent,
528 radicalDescent);
529 }
530 }
532 ///////////////
533 //
534 if (IsToDraw(NOTATION_CIRCLE) ||
535 IsToDraw(NOTATION_ROUNDEDBOX) ||
536 (IsToDraw(NOTATION_LEFT) && IsToDraw(NOTATION_RIGHT))) {
537 // center the menclose around the content (horizontally)
538 dx_left = dx_right = std::max(dx_left, dx_right);
539 }
541 ///////////////
542 // The maximum size is now computed: set the remaining parameters
543 mBoundingMetrics.width = dx_left + bmBase.width + dx_right;
545 mBoundingMetrics.leftBearing = std::min(0, dx_left + bmBase.leftBearing);
546 mBoundingMetrics.rightBearing =
547 std::max(mBoundingMetrics.width, dx_left + bmBase.rightBearing);
549 aDesiredSize.Width() = mBoundingMetrics.width;
551 aDesiredSize.SetTopAscent(std::max(mBoundingMetrics.ascent, baseSize.TopAscent()));
552 aDesiredSize.Height() = aDesiredSize.TopAscent() +
553 std::max(mBoundingMetrics.descent, baseSize.Height() - baseSize.TopAscent());
555 if (IsToDraw(NOTATION_LONGDIV) || IsToDraw(NOTATION_RADICAL)) {
556 // get the leading to be left at the top of the resulting frame
557 // this seems more reliable than using fm->GetLeading() on suspicious
558 // fonts
559 nscoord leading = nscoord(0.2f * mEmHeight);
560 nscoord desiredSizeAscent = aDesiredSize.TopAscent();
561 nscoord desiredSizeDescent = aDesiredSize.Height() - aDesiredSize.TopAscent();
563 if (IsToDraw(NOTATION_LONGDIV)) {
564 desiredSizeAscent = std::max(desiredSizeAscent,
565 longdivAscent + leading);
566 desiredSizeDescent = std::max(desiredSizeDescent,
567 longdivDescent + mRuleThickness);
568 }
570 if (IsToDraw(NOTATION_RADICAL)) {
571 desiredSizeAscent = std::max(desiredSizeAscent,
572 radicalAscent + leading);
573 desiredSizeDescent = std::max(desiredSizeDescent,
574 radicalDescent + mRuleThickness);
575 }
577 aDesiredSize.SetTopAscent(desiredSizeAscent);
578 aDesiredSize.Height() = desiredSizeAscent + desiredSizeDescent;
579 }
581 if (IsToDraw(NOTATION_CIRCLE) ||
582 IsToDraw(NOTATION_ROUNDEDBOX) ||
583 (IsToDraw(NOTATION_TOP) && IsToDraw(NOTATION_BOTTOM))) {
584 // center the menclose around the content (vertically)
585 nscoord dy = std::max(aDesiredSize.TopAscent() - bmBase.ascent,
586 aDesiredSize.Height() - aDesiredSize.TopAscent() -
587 bmBase.descent);
589 aDesiredSize.SetTopAscent(bmBase.ascent + dy);
590 aDesiredSize.Height() = aDesiredSize.TopAscent() + bmBase.descent + dy;
591 }
593 // Update mBoundingMetrics ascent/descent
594 if (IsToDraw(NOTATION_TOP) ||
595 IsToDraw(NOTATION_RIGHT) ||
596 IsToDraw(NOTATION_LEFT) ||
597 IsToDraw(NOTATION_UPDIAGONALSTRIKE) ||
598 IsToDraw(NOTATION_UPDIAGONALARROW) ||
599 IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) ||
600 IsToDraw(NOTATION_VERTICALSTRIKE) ||
601 IsToDraw(NOTATION_CIRCLE) ||
602 IsToDraw(NOTATION_ROUNDEDBOX))
603 mBoundingMetrics.ascent = aDesiredSize.TopAscent();
605 if (IsToDraw(NOTATION_BOTTOM) ||
606 IsToDraw(NOTATION_RIGHT) ||
607 IsToDraw(NOTATION_LEFT) ||
608 IsToDraw(NOTATION_UPDIAGONALSTRIKE) ||
609 IsToDraw(NOTATION_UPDIAGONALARROW) ||
610 IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) ||
611 IsToDraw(NOTATION_VERTICALSTRIKE) ||
612 IsToDraw(NOTATION_CIRCLE) ||
613 IsToDraw(NOTATION_ROUNDEDBOX))
614 mBoundingMetrics.descent = aDesiredSize.Height() - aDesiredSize.TopAscent();
616 aDesiredSize.mBoundingMetrics = mBoundingMetrics;
618 mReference.x = 0;
619 mReference.y = aDesiredSize.TopAscent();
621 if (aPlaceOrigin) {
622 //////////////////
623 // Set position and size of MathMLChars
624 if (IsToDraw(NOTATION_LONGDIV))
625 mMathMLChar[mLongDivCharIndex].SetRect(nsRect(dx_left -
626 bmLongdivChar.width,
627 aDesiredSize.TopAscent() -
628 longdivAscent,
629 bmLongdivChar.width,
630 bmLongdivChar.ascent +
631 bmLongdivChar.descent));
633 if (IsToDraw(NOTATION_RADICAL)) {
634 nscoord dx = (StyleVisibility()->mDirection ?
635 dx_left + bmBase.width : dx_left - bmRadicalChar.width);
637 mMathMLChar[mRadicalCharIndex].SetRect(nsRect(dx,
638 aDesiredSize.TopAscent() -
639 radicalAscent,
640 bmRadicalChar.width,
641 bmRadicalChar.ascent +
642 bmRadicalChar.descent));
643 }
645 mContentWidth = bmBase.width;
647 //////////////////
648 // Finish reflowing child frames
649 PositionRowChildFrames(dx_left, aDesiredSize.TopAscent());
650 }
652 return NS_OK;
653 }
655 nscoord
656 nsMathMLmencloseFrame::FixInterFrameSpacing(nsHTMLReflowMetrics& aDesiredSize)
657 {
658 nscoord gap = nsMathMLContainerFrame::FixInterFrameSpacing(aDesiredSize);
659 if (!gap)
660 return 0;
662 // Move the MathML characters
663 nsRect rect;
664 for (uint32_t i = 0; i < mMathMLChar.Length(); i++) {
665 mMathMLChar[i].GetRect(rect);
666 rect.MoveBy(gap, 0);
667 mMathMLChar[i].SetRect(rect);
668 }
670 return gap;
671 }
673 nsresult
674 nsMathMLmencloseFrame::AttributeChanged(int32_t aNameSpaceID,
675 nsIAtom* aAttribute,
676 int32_t aModType)
677 {
678 if (aAttribute == nsGkAtoms::notation_) {
679 InitNotations();
680 }
682 return nsMathMLContainerFrame::
683 AttributeChanged(aNameSpaceID, aAttribute, aModType);
684 }
686 //////////////////
687 // the Style System will use these to pass the proper style context to our
688 // MathMLChar
689 nsStyleContext*
690 nsMathMLmencloseFrame::GetAdditionalStyleContext(int32_t aIndex) const
691 {
692 int32_t len = mMathMLChar.Length();
693 if (aIndex >= 0 && aIndex < len)
694 return mMathMLChar[aIndex].GetStyleContext();
695 else
696 return nullptr;
697 }
699 void
700 nsMathMLmencloseFrame::SetAdditionalStyleContext(int32_t aIndex,
701 nsStyleContext* aStyleContext)
702 {
703 int32_t len = mMathMLChar.Length();
704 if (aIndex >= 0 && aIndex < len)
705 mMathMLChar[aIndex].SetStyleContext(aStyleContext);
706 }
708 class nsDisplayNotation : public nsDisplayItem
709 {
710 public:
711 nsDisplayNotation(nsDisplayListBuilder* aBuilder,
712 nsIFrame* aFrame, const nsRect& aRect,
713 nscoord aThickness, nsMencloseNotation aType)
714 : nsDisplayItem(aBuilder, aFrame), mRect(aRect),
715 mThickness(aThickness), mType(aType) {
716 MOZ_COUNT_CTOR(nsDisplayNotation);
717 }
718 #ifdef NS_BUILD_REFCNT_LOGGING
719 virtual ~nsDisplayNotation() {
720 MOZ_COUNT_DTOR(nsDisplayNotation);
721 }
722 #endif
724 virtual void Paint(nsDisplayListBuilder* aBuilder,
725 nsRenderingContext* aCtx) MOZ_OVERRIDE;
726 NS_DISPLAY_DECL_NAME("MathMLMencloseNotation", TYPE_MATHML_MENCLOSE_NOTATION)
728 private:
729 nsRect mRect;
730 nscoord mThickness;
731 nsMencloseNotation mType;
732 };
734 void nsDisplayNotation::Paint(nsDisplayListBuilder* aBuilder,
735 nsRenderingContext* aCtx)
736 {
737 // get the gfxRect
738 nsPresContext* presContext = mFrame->PresContext();
739 gfxRect rect = presContext->AppUnitsToGfxUnits(mRect + ToReferenceFrame());
741 // paint the frame with the current text color
742 aCtx->SetColor(mFrame->GetVisitedDependentColor(eCSSProperty_color));
744 // change line width to mThickness
745 gfxContext *gfxCtx = aCtx->ThebesContext();
746 gfxFloat e = presContext->AppUnitsToGfxUnits(mThickness);
747 gfxCtx->Save();
748 gfxCtx->SetLineWidth(e);
750 rect.Deflate(e / 2.0);
752 switch(mType)
753 {
754 case NOTATION_CIRCLE:
755 gfxCtx->NewPath();
756 gfxCtx->Ellipse(rect.Center(), rect.Size());
757 gfxCtx->Stroke();
758 break;
760 case NOTATION_ROUNDEDBOX:
761 gfxCtx->NewPath();
762 gfxCtx->RoundedRectangle(rect, gfxCornerSizes(3 * e), true);
763 gfxCtx->Stroke();
764 break;
766 case NOTATION_UPDIAGONALSTRIKE:
767 gfxCtx->NewPath();
768 gfxCtx->Line(rect.BottomLeft(), rect.TopRight());
769 gfxCtx->Stroke();
770 break;
772 case NOTATION_DOWNDIAGONALSTRIKE:
773 gfxCtx->NewPath();
774 gfxCtx->Line(rect.TopLeft(), rect.BottomRight());
775 gfxCtx->Stroke();
776 break;
778 case NOTATION_UPDIAGONALARROW: {
779 // Compute some parameters to draw the updiagonalarrow. The values below
780 // are taken from MathJax's HTML-CSS output.
781 gfxFloat W = rect.Width(); gfxFloat H = rect.Height();
782 gfxFloat l = sqrt(W*W + H*H);
783 gfxFloat f = gfxFloat(kArrowHeadSize) * e / l;
784 gfxFloat w = W * f; gfxFloat h = H * f;
786 // Draw the arrow shaft
787 gfxCtx->NewPath();
788 gfxCtx->Line(rect.BottomLeft(), rect.TopRight() + gfxPoint(-.7*w, .7*h));
789 gfxCtx->Stroke();
791 // Draw the arrow head
792 gfxCtx->NewPath();
793 gfxPoint p[] = {
794 rect.TopRight(),
795 rect.TopRight() + gfxPoint(-w -.4*h, std::max(-e / 2.0, h - .4*w)),
796 rect.TopRight() + gfxPoint(-.7*w, .7*h),
797 rect.TopRight() + gfxPoint(std::min(e / 2.0, -w + .4*h), h + .4*w),
798 rect.TopRight()
799 };
800 gfxCtx->Polygon(p, MOZ_ARRAY_LENGTH(p));
801 gfxCtx->Fill();
802 }
803 break;
805 default:
806 NS_NOTREACHED("This notation can not be drawn using nsDisplayNotation");
807 break;
808 }
810 gfxCtx->Restore();
811 }
813 void
814 nsMathMLmencloseFrame::DisplayNotation(nsDisplayListBuilder* aBuilder,
815 nsIFrame* aFrame, const nsRect& aRect,
816 const nsDisplayListSet& aLists,
817 nscoord aThickness,
818 nsMencloseNotation aType)
819 {
820 if (!aFrame->StyleVisibility()->IsVisible() || aRect.IsEmpty() ||
821 aThickness <= 0)
822 return;
824 aLists.Content()->AppendNewToTop(new (aBuilder)
825 nsDisplayNotation(aBuilder, aFrame, aRect, aThickness, aType));
826 }