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