Coverage Report

Created: 2018-09-25 14:53

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