1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6
7 #include "nsMathMLmfracFrame.h"
8
9 #include "gfxUtils.h"
10 #include "mozilla/gfx/2D.h"
11 #include "mozilla/RefPtr.h"
12 #include "nsLayoutUtils.h"
13 #include "nsPresContext.h"
14 #include "nsRenderingContext.h"
15 #include "nsDisplayList.h"
16 #include "gfxContext.h"
17 #include "nsMathMLElement.h"
18 #include <algorithm>
19 #include "gfxMathTable.h"
20
21 using namespace mozilla;
22 using namespace mozilla::gfx;
23
24 //
25 // <mfrac> -- form a fraction from two subexpressions - implementation
26 //
27
28 // various fraction line thicknesses (multiplicative values of the default rule thickness)
29
30 #define THIN_FRACTION_LINE 0.5f
31 #define THIN_FRACTION_LINE_MINIMUM_PIXELS 1 // minimum of 1 pixel
32
33 #define THICK_FRACTION_LINE 2.0f
34 #define THICK_FRACTION_LINE_MINIMUM_PIXELS 2 // minimum of 2 pixels
35
36 nsIFrame*
NS_NewMathMLmfracFrame(nsIPresShell * aPresShell,nsStyleContext * aContext)37 NS_NewMathMLmfracFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
38 {
39 return new (aPresShell) nsMathMLmfracFrame(aContext);
40 }
41
NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmfracFrame)42 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmfracFrame)
43
44 nsMathMLmfracFrame::~nsMathMLmfracFrame()
45 {
46 }
47
48 eMathMLFrameType
GetMathMLFrameType()49 nsMathMLmfracFrame::GetMathMLFrameType()
50 {
51 // frac is "inner" in TeXBook, Appendix G, rule 15e. See also page 170.
52 return eMathMLFrameType_Inner;
53 }
54
55 uint8_t
ScriptIncrement(nsIFrame * aFrame)56 nsMathMLmfracFrame::ScriptIncrement(nsIFrame* aFrame)
57 {
58 if (!StyleFont()->mMathDisplay &&
59 aFrame && (mFrames.FirstChild() == aFrame ||
60 mFrames.LastChild() == aFrame)) {
61 return 1;
62 }
63 return 0;
64 }
65
66 NS_IMETHODIMP
TransmitAutomaticData()67 nsMathMLmfracFrame::TransmitAutomaticData()
68 {
69 // The TeXbook (Ch 17. p.141) says the numerator inherits the compression
70 // while the denominator is compressed
71 UpdatePresentationDataFromChildAt(1, 1,
72 NS_MATHML_COMPRESSED,
73 NS_MATHML_COMPRESSED);
74
75 // If displaystyle is false, then scriptlevel is incremented, so notify the
76 // children of this.
77 if (!StyleFont()->mMathDisplay) {
78 PropagateFrameFlagFor(mFrames.FirstChild(),
79 NS_FRAME_MATHML_SCRIPT_DESCENDANT);
80 PropagateFrameFlagFor(mFrames.LastChild(),
81 NS_FRAME_MATHML_SCRIPT_DESCENDANT);
82 }
83
84 // if our numerator is an embellished operator, let its state bubble to us
85 GetEmbellishDataFrom(mFrames.FirstChild(), mEmbellishData);
86 if (NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData.flags)) {
87 // even when embellished, we need to record that <mfrac> won't fire
88 // Stretch() on its embellished child
89 mEmbellishData.direction = NS_STRETCH_DIRECTION_UNSUPPORTED;
90 }
91
92 return NS_OK;
93 }
94
95 nscoord
CalcLineThickness(nsPresContext * aPresContext,nsStyleContext * aStyleContext,nsString & aThicknessAttribute,nscoord onePixel,nscoord aDefaultRuleThickness,float aFontSizeInflation)96 nsMathMLmfracFrame::CalcLineThickness(nsPresContext* aPresContext,
97 nsStyleContext* aStyleContext,
98 nsString& aThicknessAttribute,
99 nscoord onePixel,
100 nscoord aDefaultRuleThickness,
101 float aFontSizeInflation)
102 {
103 nscoord defaultThickness = aDefaultRuleThickness;
104 nscoord lineThickness = aDefaultRuleThickness;
105 nscoord minimumThickness = onePixel;
106
107 // linethickness
108 //
109 // "Specifies the thickness of the horizontal 'fraction bar', or 'rule'. The
110 // default value is 'medium', 'thin' is thinner, but visible, 'thick' is
111 // thicker; the exact thickness of these is left up to the rendering agent."
112 //
113 // values: length | "thin" | "medium" | "thick"
114 // default: medium
115 //
116 if (!aThicknessAttribute.IsEmpty()) {
117 if (aThicknessAttribute.EqualsLiteral("thin")) {
118 lineThickness = NSToCoordFloor(defaultThickness * THIN_FRACTION_LINE);
119 minimumThickness = onePixel * THIN_FRACTION_LINE_MINIMUM_PIXELS;
120 // should visually decrease by at least one pixel, if default is not a pixel
121 if (defaultThickness > onePixel && lineThickness > defaultThickness - onePixel)
122 lineThickness = defaultThickness - onePixel;
123 }
124 else if (aThicknessAttribute.EqualsLiteral("medium")) {
125 // medium is default
126 }
127 else if (aThicknessAttribute.EqualsLiteral("thick")) {
128 lineThickness = NSToCoordCeil(defaultThickness * THICK_FRACTION_LINE);
129 minimumThickness = onePixel * THICK_FRACTION_LINE_MINIMUM_PIXELS;
130 // should visually increase by at least one pixel
131 if (lineThickness < defaultThickness + onePixel)
132 lineThickness = defaultThickness + onePixel;
133 }
134 else {
135 // length value
136 lineThickness = defaultThickness;
137 ParseNumericValue(aThicknessAttribute, &lineThickness,
138 nsMathMLElement::PARSE_ALLOW_UNITLESS,
139 aPresContext, aStyleContext, aFontSizeInflation);
140 }
141 }
142
143 // use minimum if the lineThickness is a non-zero value less than minimun
144 if (lineThickness && lineThickness < minimumThickness)
145 lineThickness = minimumThickness;
146
147 return lineThickness;
148 }
149
150 void
BuildDisplayList(nsDisplayListBuilder * aBuilder,const nsRect & aDirtyRect,const nsDisplayListSet & aLists)151 nsMathMLmfracFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
152 const nsRect& aDirtyRect,
153 const nsDisplayListSet& aLists)
154 {
155 /////////////
156 // paint the numerator and denominator
157 nsMathMLContainerFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
158
159 /////////////
160 // paint the fraction line
161 if (mIsBevelled) {
162 DisplaySlash(aBuilder, this, mLineRect, mLineThickness, aLists);
163 } else {
164 DisplayBar(aBuilder, this, mLineRect, aLists);
165 }
166 }
167
168
169 nsresult
AttributeChanged(int32_t aNameSpaceID,nsIAtom * aAttribute,int32_t aModType)170 nsMathMLmfracFrame::AttributeChanged(int32_t aNameSpaceID,
171 nsIAtom* aAttribute,
172 int32_t aModType)
173 {
174 if (nsGkAtoms::linethickness_ == aAttribute) {
175 InvalidateFrame();
176 }
177 return
178 nsMathMLContainerFrame::AttributeChanged(aNameSpaceID, aAttribute,
179 aModType);
180 }
181
182 /* virtual */ nsresult
MeasureForWidth(DrawTarget * aDrawTarget,ReflowOutput & aDesiredSize)183 nsMathMLmfracFrame::MeasureForWidth(DrawTarget* aDrawTarget,
184 ReflowOutput& aDesiredSize)
185 {
186 return PlaceInternal(aDrawTarget, false, aDesiredSize, true);
187 }
188
189 nscoord
FixInterFrameSpacing(ReflowOutput & aDesiredSize)190 nsMathMLmfracFrame::FixInterFrameSpacing(ReflowOutput& aDesiredSize)
191 {
192 nscoord gap = nsMathMLContainerFrame::FixInterFrameSpacing(aDesiredSize);
193 if (!gap) return 0;
194
195 mLineRect.MoveBy(gap, 0);
196 return gap;
197 }
198
199 /* virtual */ nsresult
Place(DrawTarget * aDrawTarget,bool aPlaceOrigin,ReflowOutput & aDesiredSize)200 nsMathMLmfracFrame::Place(DrawTarget* aDrawTarget,
201 bool aPlaceOrigin,
202 ReflowOutput& aDesiredSize)
203 {
204 return PlaceInternal(aDrawTarget, aPlaceOrigin, aDesiredSize, false);
205 }
206
207 nsresult
PlaceInternal(DrawTarget * aDrawTarget,bool aPlaceOrigin,ReflowOutput & aDesiredSize,bool aWidthOnly)208 nsMathMLmfracFrame::PlaceInternal(DrawTarget* aDrawTarget,
209 bool aPlaceOrigin,
210 ReflowOutput& aDesiredSize,
211 bool aWidthOnly)
212 {
213 ////////////////////////////////////
214 // Get the children's desired sizes
215 nsBoundingMetrics bmNum, bmDen;
216 ReflowOutput sizeNum(aDesiredSize.GetWritingMode());
217 ReflowOutput sizeDen(aDesiredSize.GetWritingMode());
218 nsIFrame* frameDen = nullptr;
219 nsIFrame* frameNum = mFrames.FirstChild();
220 if (frameNum)
221 frameDen = frameNum->GetNextSibling();
222 if (!frameNum || !frameDen || frameDen->GetNextSibling()) {
223 // report an error, encourage people to get their markups in order
224 if (aPlaceOrigin) {
225 ReportChildCountError();
226 }
227 return ReflowError(aDrawTarget, aDesiredSize);
228 }
229 GetReflowAndBoundingMetricsFor(frameNum, sizeNum, bmNum);
230 GetReflowAndBoundingMetricsFor(frameDen, sizeDen, bmDen);
231
232 nsPresContext* presContext = PresContext();
233 nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
234
235 float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
236 RefPtr<nsFontMetrics> fm =
237 nsLayoutUtils::GetFontMetricsForFrame(this, fontSizeInflation);
238
239 nscoord defaultRuleThickness, axisHeight;
240 nscoord oneDevPixel = fm->AppUnitsPerDevPixel();
241 gfxFont* mathFont = fm->GetThebesFontGroup()->GetFirstMathFont();
242 if (mathFont) {
243 defaultRuleThickness = mathFont->MathTable()->
244 Constant(gfxMathTable::FractionRuleThickness, oneDevPixel);
245 } else {
246 GetRuleThickness(aDrawTarget, fm, defaultRuleThickness);
247 }
248 GetAxisHeight(aDrawTarget, fm, axisHeight);
249
250 bool outermostEmbellished = false;
251 if (mEmbellishData.coreFrame) {
252 nsEmbellishData parentData;
253 GetEmbellishDataFrom(GetParent(), parentData);
254 outermostEmbellished = parentData.coreFrame != mEmbellishData.coreFrame;
255 }
256
257 // see if the linethickness attribute is there
258 nsAutoString value;
259 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::linethickness_, value);
260 mLineThickness = CalcLineThickness(presContext, mStyleContext, value,
261 onePixel, defaultRuleThickness,
262 fontSizeInflation);
263
264 // bevelled attribute
265 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::bevelled_, value);
266 mIsBevelled = value.EqualsLiteral("true");
267
268 bool displayStyle = StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK;
269
270 if (!mIsBevelled) {
271 mLineRect.height = mLineThickness;
272
273 // by default, leave at least one-pixel padding at either end, and add
274 // lspace & rspace that may come from <mo> if we are an outermost
275 // embellished container (we fetch values from the core since they may use
276 // units that depend on style data, and style changes could have occurred
277 // in the core since our last visit there)
278 nscoord leftSpace = onePixel;
279 nscoord rightSpace = onePixel;
280 if (outermostEmbellished) {
281 nsEmbellishData coreData;
282 GetEmbellishDataFrom(mEmbellishData.coreFrame, coreData);
283 leftSpace += StyleVisibility()->mDirection ?
284 coreData.trailingSpace : coreData.leadingSpace;
285 rightSpace += StyleVisibility()->mDirection ?
286 coreData.leadingSpace : coreData.trailingSpace;
287 }
288
289 nscoord actualRuleThickness = mLineThickness;
290
291 //////////////////
292 // Get shifts
293 nscoord numShift = 0;
294 nscoord denShift = 0;
295
296 // Rule 15b, App. G, TeXbook
297 nscoord numShift1, numShift2, numShift3;
298 nscoord denShift1, denShift2;
299
300 GetNumeratorShifts(fm, numShift1, numShift2, numShift3);
301 GetDenominatorShifts(fm, denShift1, denShift2);
302
303 if (0 == actualRuleThickness) {
304 numShift = displayStyle ? numShift1 : numShift3;
305 denShift = displayStyle ? denShift1 : denShift2;
306 if (mathFont) {
307 numShift = mathFont->
308 MathTable()->Constant(displayStyle ?
309 gfxMathTable::StackTopDisplayStyleShiftUp :
310 gfxMathTable::StackTopShiftUp,
311 oneDevPixel);
312 denShift = mathFont->
313 MathTable()->Constant(displayStyle ?
314 gfxMathTable::StackBottomDisplayStyleShiftDown :
315 gfxMathTable::StackBottomShiftDown,
316 oneDevPixel);
317 }
318 } else {
319 numShift = displayStyle ? numShift1 : numShift2;
320 denShift = displayStyle ? denShift1 : denShift2;
321 if (mathFont) {
322 numShift = mathFont->MathTable()->
323 Constant(displayStyle ?
324 gfxMathTable::FractionNumeratorDisplayStyleShiftUp :
325 gfxMathTable::FractionNumeratorShiftUp,
326 oneDevPixel);
327 denShift = mathFont->MathTable()->
328 Constant(displayStyle ?
329 gfxMathTable::FractionDenominatorDisplayStyleShiftDown :
330 gfxMathTable::FractionDenominatorShiftDown,
331 oneDevPixel);
332 }
333 }
334
335 if (0 == actualRuleThickness) {
336 // Rule 15c, App. G, TeXbook
337
338 // min clearance between numerator and denominator
339 nscoord minClearance = displayStyle ?
340 7 * defaultRuleThickness : 3 * defaultRuleThickness;
341 if (mathFont) {
342 minClearance = mathFont->MathTable()->
343 Constant(displayStyle ?
344 gfxMathTable::StackDisplayStyleGapMin :
345 gfxMathTable::StackGapMin,
346 oneDevPixel);
347 }
348 // Factor in axis height
349 // http://www.mathml-association.org/MathMLinHTML5/S3.html#SS3.SSS2
350 numShift += axisHeight;
351 denShift += axisHeight;
352
353 nscoord actualClearance =
354 (numShift - bmNum.descent) - (bmDen.ascent - denShift);
355 // actualClearance should be >= minClearance
356 if (actualClearance < minClearance) {
357 nscoord halfGap = (minClearance - actualClearance)/2;
358 numShift += halfGap;
359 denShift += halfGap;
360 }
361 }
362 else {
363 // Rule 15d, App. G, TeXbook
364
365 // min clearance between numerator or denominator and middle of bar
366
367 // TeX has a different interpretation of the thickness.
368 // Try $a \above10pt b$ to see. Here is what TeX does:
369 // minClearance = displayStyle ?
370 // 3 * actualRuleThickness : actualRuleThickness;
371
372 // we slightly depart from TeX here. We use the defaultRuleThickness instead
373 // of the value coming from the linethickness attribute, i.e., we recover what
374 // TeX does if the user hasn't set linethickness. But when the linethickness
375 // is set, we avoid the wide gap problem.
376 nscoord minClearanceNum = displayStyle ?
377 3 * defaultRuleThickness : defaultRuleThickness + onePixel;
378 nscoord minClearanceDen = minClearanceNum;
379 if (mathFont) {
380 minClearanceNum = mathFont->
381 MathTable()->Constant(displayStyle ?
382 gfxMathTable::FractionNumDisplayStyleGapMin :
383 gfxMathTable::FractionNumeratorGapMin,
384 oneDevPixel);
385 minClearanceDen = mathFont->
386 MathTable()->Constant(displayStyle ?
387 gfxMathTable::FractionDenomDisplayStyleGapMin :
388 gfxMathTable::FractionDenominatorGapMin,
389 oneDevPixel);
390 }
391
392 // adjust numShift to maintain minClearanceNum if needed
393 nscoord actualClearanceNum =
394 (numShift - bmNum.descent) - (axisHeight + actualRuleThickness/2);
395 if (actualClearanceNum < minClearanceNum) {
396 numShift += (minClearanceNum - actualClearanceNum);
397 }
398 // adjust denShift to maintain minClearanceDen if needed
399 nscoord actualClearanceDen =
400 (axisHeight - actualRuleThickness/2) - (bmDen.ascent - denShift);
401 if (actualClearanceDen < minClearanceDen) {
402 denShift += (minClearanceDen - actualClearanceDen);
403 }
404 }
405
406 //////////////////
407 // Place Children
408
409 // XXX Need revisiting the width. TeX uses the exact width
410 // e.g. in $$\huge\frac{\displaystyle\int}{i}$$
411 nscoord width = std::max(bmNum.width, bmDen.width);
412 nscoord dxNum = leftSpace + (width - sizeNum.Width())/2;
413 nscoord dxDen = leftSpace + (width - sizeDen.Width())/2;
414 width += leftSpace + rightSpace;
415
416 // see if the numalign attribute is there
417 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::numalign_, value);
418 if (value.EqualsLiteral("left"))
419 dxNum = leftSpace;
420 else if (value.EqualsLiteral("right"))
421 dxNum = width - rightSpace - sizeNum.Width();
422
423 // see if the denomalign attribute is there
424 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::denomalign_, value);
425 if (value.EqualsLiteral("left"))
426 dxDen = leftSpace;
427 else if (value.EqualsLiteral("right"))
428 dxDen = width - rightSpace - sizeDen.Width();
429
430 mBoundingMetrics.rightBearing =
431 std::max(dxNum + bmNum.rightBearing, dxDen + bmDen.rightBearing);
432 if (mBoundingMetrics.rightBearing < width - rightSpace)
433 mBoundingMetrics.rightBearing = width - rightSpace;
434 mBoundingMetrics.leftBearing =
435 std::min(dxNum + bmNum.leftBearing, dxDen + bmDen.leftBearing);
436 if (mBoundingMetrics.leftBearing > leftSpace)
437 mBoundingMetrics.leftBearing = leftSpace;
438 mBoundingMetrics.ascent = bmNum.ascent + numShift;
439 mBoundingMetrics.descent = bmDen.descent + denShift;
440 mBoundingMetrics.width = width;
441
442 aDesiredSize.SetBlockStartAscent(sizeNum.BlockStartAscent() + numShift);
443 aDesiredSize.Height() = aDesiredSize.BlockStartAscent() +
444 sizeDen.Height() - sizeDen.BlockStartAscent() + denShift;
445 aDesiredSize.Width() = mBoundingMetrics.width;
446 aDesiredSize.mBoundingMetrics = mBoundingMetrics;
447
448 mReference.x = 0;
449 mReference.y = aDesiredSize.BlockStartAscent();
450
451 if (aPlaceOrigin) {
452 nscoord dy;
453 // place numerator
454 dy = 0;
455 FinishReflowChild(frameNum, presContext, sizeNum, nullptr, dxNum, dy, 0);
456 // place denominator
457 dy = aDesiredSize.Height() - sizeDen.Height();
458 FinishReflowChild(frameDen, presContext, sizeDen, nullptr, dxDen, dy, 0);
459 // place the fraction bar - dy is top of bar
460 dy = aDesiredSize.BlockStartAscent() - (axisHeight + actualRuleThickness/2);
461 mLineRect.SetRect(leftSpace, dy, width - (leftSpace + rightSpace),
462 actualRuleThickness);
463 }
464 } else {
465 nscoord numShift = 0.0;
466 nscoord denShift = 0.0;
467 nscoord padding = 3 * defaultRuleThickness;
468 nscoord slashRatio = 3;
469
470 // Define the constant used in the expression of the maximum width
471 nscoord em = fm->EmHeight();
472 nscoord slashMaxWidthConstant = 2 * em;
473
474 // For large line thicknesses the minimum slash height is limited to the
475 // largest expected height of a fraction
476 nscoord slashMinHeight = slashRatio *
477 std::min(2 * mLineThickness, slashMaxWidthConstant);
478
479 nscoord leadingSpace = padding;
480 nscoord trailingSpace = padding;
481 if (outermostEmbellished) {
482 nsEmbellishData coreData;
483 GetEmbellishDataFrom(mEmbellishData.coreFrame, coreData);
484 leadingSpace += coreData.leadingSpace;
485 trailingSpace += coreData.trailingSpace;
486 }
487 nscoord delta;
488
489 // ___________
490 // | | /
491 // {|-NUMERATOR-| /
492 // {|___________| S
493 // { L
494 // numShift{ A
495 // ------------------------------------------------------- baseline
496 // S _____________ } denShift
497 // H | |}
498 // / |-DENOMINATOR-|}
499 // / |_____________|
500 //
501
502 // first, ensure that the top of the numerator is at least as high as the
503 // top of the denominator (and the reverse for the bottoms)
504 delta = std::max(bmDen.ascent - bmNum.ascent,
505 bmNum.descent - bmDen.descent) / 2;
506 if (delta > 0) {
507 numShift += delta;
508 denShift += delta;
509 }
510
511 if (StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK) {
512 delta = std::min(bmDen.ascent + bmDen.descent,
513 bmNum.ascent + bmNum.descent) / 2;
514 numShift += delta;
515 denShift += delta;
516 } else {
517 nscoord xHeight = fm->XHeight();
518 numShift += xHeight / 2;
519 denShift += xHeight / 4;
520 }
521
522 // Set the ascent/descent of our BoundingMetrics.
523 mBoundingMetrics.ascent = bmNum.ascent + numShift;
524 mBoundingMetrics.descent = bmDen.descent + denShift;
525
526 // At this point the height of the slash is
527 // mBoundingMetrics.ascent + mBoundingMetrics.descent
528 // Ensure that it is greater than slashMinHeight
529 delta = (slashMinHeight -
530 (mBoundingMetrics.ascent + mBoundingMetrics.descent)) / 2;
531 if (delta > 0) {
532 mBoundingMetrics.ascent += delta;
533 mBoundingMetrics.descent += delta;
534 }
535
536 // Set the width of the slash
537 if (aWidthOnly) {
538 mLineRect.width = mLineThickness + slashMaxWidthConstant;
539 } else {
540 mLineRect.width = mLineThickness +
541 std::min(slashMaxWidthConstant,
542 (mBoundingMetrics.ascent + mBoundingMetrics.descent) /
543 slashRatio);
544 }
545
546 // Set horizontal bounding metrics
547 if (StyleVisibility()->mDirection) {
548 mBoundingMetrics.leftBearing = trailingSpace + bmDen.leftBearing;
549 mBoundingMetrics.rightBearing = trailingSpace + bmDen.width + mLineRect.width + bmNum.rightBearing;
550 } else {
551 mBoundingMetrics.leftBearing = leadingSpace + bmNum.leftBearing;
552 mBoundingMetrics.rightBearing = leadingSpace + bmNum.width + mLineRect.width + bmDen.rightBearing;
553 }
554 mBoundingMetrics.width =
555 leadingSpace + bmNum.width + mLineRect.width + bmDen.width +
556 trailingSpace;
557
558 // Set aDesiredSize
559 aDesiredSize.SetBlockStartAscent(mBoundingMetrics.ascent + padding);
560 aDesiredSize.Height() =
561 mBoundingMetrics.ascent + mBoundingMetrics.descent + 2 * padding;
562 aDesiredSize.Width() = mBoundingMetrics.width;
563 aDesiredSize.mBoundingMetrics = mBoundingMetrics;
564
565 mReference.x = 0;
566 mReference.y = aDesiredSize.BlockStartAscent();
567
568 if (aPlaceOrigin) {
569 nscoord dx, dy;
570
571 // place numerator
572 dx = MirrorIfRTL(aDesiredSize.Width(), sizeNum.Width(),
573 leadingSpace);
574 dy = aDesiredSize.BlockStartAscent() - numShift - sizeNum.BlockStartAscent();
575 FinishReflowChild(frameNum, presContext, sizeNum, nullptr, dx, dy, 0);
576
577 // place the fraction bar
578 dx = MirrorIfRTL(aDesiredSize.Width(), mLineRect.width,
579 leadingSpace + bmNum.width);
580 dy = aDesiredSize.BlockStartAscent() - mBoundingMetrics.ascent;
581 mLineRect.SetRect(dx, dy,
582 mLineRect.width, aDesiredSize.Height() - 2 * padding);
583
584 // place denominator
585 dx = MirrorIfRTL(aDesiredSize.Width(), sizeDen.Width(),
586 leadingSpace + bmNum.width + mLineRect.width);
587 dy = aDesiredSize.BlockStartAscent() + denShift - sizeDen.BlockStartAscent();
588 FinishReflowChild(frameDen, presContext, sizeDen, nullptr, dx, dy, 0);
589 }
590
591 }
592
593 return NS_OK;
594 }
595
596 class nsDisplayMathMLSlash : public nsDisplayItem {
597 public:
nsDisplayMathMLSlash(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,const nsRect & aRect,nscoord aThickness,bool aRTL)598 nsDisplayMathMLSlash(nsDisplayListBuilder* aBuilder,
599 nsIFrame* aFrame, const nsRect& aRect,
600 nscoord aThickness, bool aRTL)
601 : nsDisplayItem(aBuilder, aFrame), mRect(aRect), mThickness(aThickness),
602 mRTL(aRTL) {
603 MOZ_COUNT_CTOR(nsDisplayMathMLSlash);
604 }
605 #ifdef NS_BUILD_REFCNT_LOGGING
~nsDisplayMathMLSlash()606 virtual ~nsDisplayMathMLSlash() {
607 MOZ_COUNT_DTOR(nsDisplayMathMLSlash);
608 }
609 #endif
610
611 virtual void Paint(nsDisplayListBuilder* aBuilder,
612 nsRenderingContext* aCtx) override;
613 NS_DISPLAY_DECL_NAME("MathMLSlash", TYPE_MATHML_SLASH)
614
615 private:
616 nsRect mRect;
617 nscoord mThickness;
618 bool mRTL;
619 };
620
Paint(nsDisplayListBuilder * aBuilder,nsRenderingContext * aCtx)621 void nsDisplayMathMLSlash::Paint(nsDisplayListBuilder* aBuilder,
622 nsRenderingContext* aCtx)
623 {
624 DrawTarget& aDrawTarget = *aCtx->GetDrawTarget();
625
626 // get the gfxRect
627 nsPresContext* presContext = mFrame->PresContext();
628 Rect rect = NSRectToRect(mRect + ToReferenceFrame(),
629 presContext->AppUnitsPerDevPixel());
630
631 ColorPattern color(ToDeviceColor(
632 mFrame->GetVisitedDependentColor(eCSSProperty__webkit_text_fill_color)));
633
634 // draw the slash as a parallelogram
635 Point delta = Point(presContext->AppUnitsToGfxUnits(mThickness), 0);
636 RefPtr<PathBuilder> builder = aDrawTarget.CreatePathBuilder();
637 if (mRTL) {
638 builder->MoveTo(rect.TopLeft());
639 builder->LineTo(rect.TopLeft() + delta);
640 builder->LineTo(rect.BottomRight());
641 builder->LineTo(rect.BottomRight() - delta);
642 } else {
643 builder->MoveTo(rect.BottomLeft());
644 builder->LineTo(rect.BottomLeft() + delta);
645 builder->LineTo(rect.TopRight());
646 builder->LineTo(rect.TopRight() - delta);
647 }
648 RefPtr<Path> path = builder->Finish();
649 aDrawTarget.Fill(path, color);
650 }
651
652 void
DisplaySlash(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,const nsRect & aRect,nscoord aThickness,const nsDisplayListSet & aLists)653 nsMathMLmfracFrame::DisplaySlash(nsDisplayListBuilder* aBuilder,
654 nsIFrame* aFrame, const nsRect& aRect,
655 nscoord aThickness,
656 const nsDisplayListSet& aLists) {
657 if (!aFrame->StyleVisibility()->IsVisible() || aRect.IsEmpty())
658 return;
659
660 aLists.Content()->AppendNewToTop(new (aBuilder)
661 nsDisplayMathMLSlash(aBuilder, aFrame, aRect, aThickness,
662 StyleVisibility()->mDirection));
663 }
664