Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/layout/mathml/nsMathMLmrootFrame.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
#include "nsMathMLmrootFrame.h"
8
#include "nsPresContext.h"
9
#include <algorithm>
10
#include "gfxContext.h"
11
#include "gfxMathTable.h"
12
13
using namespace mozilla;
14
15
//
16
// <mroot> -- form a radical - implementation
17
//
18
19
// additional ComputedStyle to be used by our MathMLChar.
20
0
#define NS_SQR_CHAR_STYLE_CONTEXT_INDEX   0
21
22
static const char16_t kSqrChar = char16_t(0x221A);
23
24
nsIFrame*
25
NS_NewMathMLmrootFrame(nsIPresShell* aPresShell, ComputedStyle* aStyle)
26
0
{
27
0
  return new (aPresShell) nsMathMLmrootFrame(aStyle);
28
0
}
29
30
NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmrootFrame)
31
32
nsMathMLmrootFrame::nsMathMLmrootFrame(ComputedStyle* aStyle) :
33
  nsMathMLContainerFrame(aStyle, kClassID),
34
  mSqrChar(),
35
  mBarRect()
36
0
{
37
0
}
38
39
nsMathMLmrootFrame::~nsMathMLmrootFrame()
40
0
{
41
0
}
42
43
void
44
nsMathMLmrootFrame::Init(nsIContent*       aContent,
45
                         nsContainerFrame* aParent,
46
                         nsIFrame*         aPrevInFlow)
47
0
{
48
0
  nsMathMLContainerFrame::Init(aContent, aParent, aPrevInFlow);
49
0
50
0
  nsPresContext *presContext = PresContext();
51
0
52
0
  // No need to track the ComputedStyle given to our MathML char.
53
0
  // The Style System will use Get/SetAdditionalComputedStyle() to keep it
54
0
  // up-to-date if dynamic changes arise.
55
0
  nsAutoString sqrChar; sqrChar.Assign(kSqrChar);
56
0
  mSqrChar.SetData(sqrChar);
57
0
  ResolveMathMLCharStyle(presContext, mContent, mComputedStyle, &mSqrChar);
58
0
}
59
60
NS_IMETHODIMP
61
nsMathMLmrootFrame::TransmitAutomaticData()
62
0
{
63
0
  // 1. The REC says:
64
0
  //    The <mroot> element increments scriptlevel by 2, and sets displaystyle to
65
0
  //    "false", within index, but leaves both attributes unchanged within base.
66
0
  // 2. The TeXbook (Ch 17. p.141) says \sqrt is compressed
67
0
  UpdatePresentationDataFromChildAt(1, 1,
68
0
                                    NS_MATHML_COMPRESSED,
69
0
                                    NS_MATHML_COMPRESSED);
70
0
  UpdatePresentationDataFromChildAt(0, 0,
71
0
     NS_MATHML_COMPRESSED, NS_MATHML_COMPRESSED);
72
0
73
0
  PropagateFrameFlagFor(mFrames.LastChild(),
74
0
                        NS_FRAME_MATHML_SCRIPT_DESCENDANT);
75
0
76
0
  return NS_OK;
77
0
}
78
79
void
80
nsMathMLmrootFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
81
                                     const nsDisplayListSet& aLists)
82
0
{
83
0
  /////////////
84
0
  // paint the content we are square-rooting
85
0
  nsMathMLContainerFrame::BuildDisplayList(aBuilder, aLists);
86
0
87
0
  /////////////
88
0
  // paint the sqrt symbol
89
0
  if (!NS_MATHML_HAS_ERROR(mPresentationData.flags)) {
90
0
    mSqrChar.Display(aBuilder, this, aLists, 0);
91
0
92
0
    DisplayBar(aBuilder, this, mBarRect, aLists);
93
0
94
#if defined(DEBUG) && defined(SHOW_BOUNDING_BOX)
95
    // for visual debug
96
    nsRect rect;
97
    mSqrChar.GetRect(rect);
98
    nsBoundingMetrics bm;
99
    mSqrChar.GetBoundingMetrics(bm);
100
    DisplayBoundingMetrics(aBuilder, this, rect.TopLeft(), bm, aLists);
101
#endif
102
  }
103
0
}
104
105
void
106
nsMathMLmrootFrame::GetRadicalXOffsets(nscoord aIndexWidth, nscoord aSqrWidth,
107
                                       nsFontMetrics* aFontMetrics,
108
                                       nscoord* aIndexOffset,
109
                                       nscoord* aSqrOffset)
110
0
{
111
0
  // The index is tucked in closer to the radical while making sure
112
0
  // that the kern does not make the index and radical collide
113
0
  nscoord dxIndex, dxSqr;
114
0
  nscoord xHeight = aFontMetrics->XHeight();
115
0
  nscoord indexRadicalKern = NSToCoordRound(1.35f * xHeight);
116
0
  nscoord oneDevPixel = aFontMetrics->AppUnitsPerDevPixel();
117
0
  gfxFont* mathFont = aFontMetrics->GetThebesFontGroup()->GetFirstMathFont();
118
0
  if (mathFont) {
119
0
    indexRadicalKern =
120
0
      mathFont->MathTable()->Constant(gfxMathTable::RadicalKernAfterDegree,
121
0
                                      oneDevPixel);
122
0
    indexRadicalKern = -indexRadicalKern;
123
0
  }
124
0
  if (indexRadicalKern > aIndexWidth) {
125
0
    dxIndex = indexRadicalKern - aIndexWidth;
126
0
    dxSqr = 0;
127
0
  }
128
0
  else {
129
0
    dxIndex = 0;
130
0
    dxSqr = aIndexWidth - indexRadicalKern;
131
0
  }
132
0
133
0
  if (mathFont) {
134
0
    // add some kern before the radical index
135
0
    nscoord indexRadicalKernBefore = 0;
136
0
    indexRadicalKernBefore =
137
0
      mathFont->MathTable()->Constant(gfxMathTable::RadicalKernBeforeDegree,
138
0
                                      oneDevPixel);
139
0
    dxIndex += indexRadicalKernBefore;
140
0
    dxSqr += indexRadicalKernBefore;
141
0
  } else {
142
0
    // avoid collision by leaving a minimum space between index and radical
143
0
    nscoord minimumClearance = aSqrWidth / 2;
144
0
    if (dxIndex + aIndexWidth + minimumClearance > dxSqr + aSqrWidth) {
145
0
      if (aIndexWidth + minimumClearance < aSqrWidth) {
146
0
        dxIndex = aSqrWidth - (aIndexWidth + minimumClearance);
147
0
        dxSqr = 0;
148
0
      }
149
0
      else {
150
0
        dxIndex = 0;
151
0
        dxSqr = (aIndexWidth + minimumClearance) - aSqrWidth;
152
0
      }
153
0
    }
154
0
  }
155
0
156
0
  if (aIndexOffset)
157
0
    *aIndexOffset = dxIndex;
158
0
  if (aSqrOffset)
159
0
    *aSqrOffset = dxSqr;
160
0
}
161
162
void
163
nsMathMLmrootFrame::Reflow(nsPresContext*          aPresContext,
164
                           ReflowOutput&     aDesiredSize,
165
                           const ReflowInput& aReflowInput,
166
                           nsReflowStatus&          aStatus)
167
0
{
168
0
  MarkInReflow();
169
0
  MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
170
0
171
0
  nsReflowStatus childStatus;
172
0
  mPresentationData.flags &= ~NS_MATHML_ERROR;
173
0
  aDesiredSize.ClearSize();
174
0
  aDesiredSize.SetBlockStartAscent(0);
175
0
176
0
  nsBoundingMetrics bmSqr, bmBase, bmIndex;
177
0
  DrawTarget* drawTarget = aReflowInput.mRenderingContext->GetDrawTarget();
178
0
179
0
  //////////////////
180
0
  // Reflow Children
181
0
182
0
  int32_t count = 0;
183
0
  nsIFrame* baseFrame = nullptr;
184
0
  nsIFrame* indexFrame = nullptr;
185
0
  ReflowOutput baseSize(aReflowInput);
186
0
  ReflowOutput indexSize(aReflowInput);
187
0
  nsIFrame* childFrame = mFrames.FirstChild();
188
0
  while (childFrame) {
189
0
    // ask our children to compute their bounding metrics
190
0
    ReflowOutput childDesiredSize(aReflowInput);
191
0
    WritingMode wm = childFrame->GetWritingMode();
192
0
    LogicalSize availSize = aReflowInput.ComputedSize(wm);
193
0
    availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
194
0
    ReflowInput childReflowInput(aPresContext, aReflowInput,
195
0
                                       childFrame, availSize);
196
0
    ReflowChild(childFrame, aPresContext,
197
0
                     childDesiredSize, childReflowInput, childStatus);
198
0
    //NS_ASSERTION(childStatus.IsComplete(), "bad status");
199
0
    if (0 == count) {
200
0
      // base
201
0
      baseFrame = childFrame;
202
0
      baseSize = childDesiredSize;
203
0
      bmBase = childDesiredSize.mBoundingMetrics;
204
0
    }
205
0
    else if (1 == count) {
206
0
      // index
207
0
      indexFrame = childFrame;
208
0
      indexSize = childDesiredSize;
209
0
      bmIndex = childDesiredSize.mBoundingMetrics;
210
0
    }
211
0
    count++;
212
0
    childFrame = childFrame->GetNextSibling();
213
0
  }
214
0
  if (2 != count) {
215
0
    // report an error, encourage people to get their markups in order
216
0
    ReportChildCountError();
217
0
    ReflowError(drawTarget, aDesiredSize);
218
0
    NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
219
0
    // Call DidReflow() for the child frames we successfully did reflow.
220
0
    DidReflowChildren(mFrames.FirstChild(), childFrame);
221
0
    return;
222
0
  }
223
0
224
0
  ////////////
225
0
  // Prepare the radical symbol and the overline bar
226
0
227
0
  float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
228
0
  RefPtr<nsFontMetrics> fm =
229
0
    nsLayoutUtils::GetFontMetricsForFrame(this, fontSizeInflation);
230
0
231
0
  nscoord ruleThickness, leading, psi;
232
0
  GetRadicalParameters(fm, StyleFont()->mMathDisplay ==
233
0
                       NS_MATHML_DISPLAYSTYLE_BLOCK,
234
0
                       ruleThickness, leading, psi);
235
0
236
0
  // built-in: adjust clearance psi to emulate \mathstrut using '1' (TexBook, p.131)
237
0
  char16_t one = '1';
238
0
  nsBoundingMetrics bmOne =
239
0
    nsLayoutUtils::AppUnitBoundsOfString(&one, 1, *fm, drawTarget);
240
0
  if (bmOne.ascent > bmBase.ascent)
241
0
    psi += bmOne.ascent - bmBase.ascent;
242
0
243
0
  // make sure that the rule appears on on screen
244
0
  nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
245
0
  if (ruleThickness < onePixel) {
246
0
    ruleThickness = onePixel;
247
0
  }
248
0
249
0
  // adjust clearance psi to get an exact number of pixels -- this
250
0
  // gives a nicer & uniform look on stacked radicals (bug 130282)
251
0
  nscoord delta = psi % onePixel;
252
0
  if (delta)
253
0
    psi += onePixel - delta; // round up
254
0
255
0
  // Stretch the radical symbol to the appropriate height if it is not big enough.
256
0
  nsBoundingMetrics contSize = bmBase;
257
0
  contSize.descent = bmBase.ascent + bmBase.descent + psi;
258
0
  contSize.ascent = ruleThickness;
259
0
260
0
  // height(radical) should be >= height(base) + psi + ruleThickness
261
0
  nsBoundingMetrics radicalSize;
262
0
  mSqrChar.Stretch(this, drawTarget,
263
0
                   fontSizeInflation,
264
0
                   NS_STRETCH_DIRECTION_VERTICAL,
265
0
                   contSize, radicalSize,
266
0
                   NS_STRETCH_LARGER,
267
0
                   StyleVisibility()->mDirection);
268
0
  // radicalSize have changed at this point, and should match with
269
0
  // the bounding metrics of the char
270
0
  mSqrChar.GetBoundingMetrics(bmSqr);
271
0
272
0
  // Update the desired size for the container (like msqrt, index is not yet included)
273
0
  // the baseline will be that of the base.
274
0
  mBoundingMetrics.ascent = bmBase.ascent + psi + ruleThickness;
275
0
  mBoundingMetrics.descent =
276
0
    std::max(bmBase.descent,
277
0
           (bmSqr.ascent + bmSqr.descent - mBoundingMetrics.ascent));
278
0
  mBoundingMetrics.width = bmSqr.width + bmBase.width;
279
0
  mBoundingMetrics.leftBearing = bmSqr.leftBearing;
280
0
  mBoundingMetrics.rightBearing = bmSqr.width +
281
0
    std::max(bmBase.width, bmBase.rightBearing); // take also care of the rule
282
0
283
0
  aDesiredSize.SetBlockStartAscent(mBoundingMetrics.ascent + leading);
284
0
  aDesiredSize.Height() = aDesiredSize.BlockStartAscent() +
285
0
    std::max(baseSize.Height() - baseSize.BlockStartAscent(),
286
0
             mBoundingMetrics.descent + ruleThickness);
287
0
  aDesiredSize.Width() = mBoundingMetrics.width;
288
0
289
0
  /////////////
290
0
  // Re-adjust the desired size to include the index.
291
0
292
0
  // the index is raised by some fraction of the height
293
0
  // of the radical, see \mroot macro in App. B, TexBook
294
0
  float raiseIndexPercent = 0.6f;
295
0
  gfxFont* mathFont = fm->GetThebesFontGroup()->GetFirstMathFont();
296
0
  if (mathFont) {
297
0
    raiseIndexPercent = mathFont->MathTable()->
298
0
      Constant(gfxMathTable::RadicalDegreeBottomRaisePercent);
299
0
  }
300
0
  nscoord raiseIndexDelta = NSToCoordRound(raiseIndexPercent *
301
0
                                           (bmSqr.ascent + bmSqr.descent));
302
0
  nscoord indexRaisedAscent = mBoundingMetrics.ascent // top of radical
303
0
    - (bmSqr.ascent + bmSqr.descent) // to bottom of radical
304
0
    + raiseIndexDelta + bmIndex.ascent + bmIndex.descent; // to top of raised index
305
0
306
0
  nscoord indexClearance = 0;
307
0
  if (mBoundingMetrics.ascent < indexRaisedAscent) {
308
0
    indexClearance =
309
0
      indexRaisedAscent - mBoundingMetrics.ascent; // excess gap introduced by a tall index
310
0
    mBoundingMetrics.ascent = indexRaisedAscent;
311
0
    nscoord descent = aDesiredSize.Height() - aDesiredSize.BlockStartAscent();
312
0
    aDesiredSize.SetBlockStartAscent(mBoundingMetrics.ascent + leading);
313
0
    aDesiredSize.Height() = aDesiredSize.BlockStartAscent() + descent;
314
0
  }
315
0
316
0
  nscoord dxIndex, dxSqr;
317
0
  GetRadicalXOffsets(bmIndex.width, bmSqr.width, fm, &dxIndex, &dxSqr);
318
0
319
0
  mBoundingMetrics.width = dxSqr + bmSqr.width + bmBase.width;
320
0
  mBoundingMetrics.leftBearing =
321
0
    std::min(dxIndex + bmIndex.leftBearing, dxSqr + bmSqr.leftBearing);
322
0
  mBoundingMetrics.rightBearing = dxSqr + bmSqr.width +
323
0
    std::max(bmBase.width, bmBase.rightBearing);
324
0
325
0
  aDesiredSize.Width() = mBoundingMetrics.width;
326
0
  aDesiredSize.mBoundingMetrics = mBoundingMetrics;
327
0
  GatherAndStoreOverflow(&aDesiredSize);
328
0
329
0
  // place the index
330
0
  nscoord dx = dxIndex;
331
0
  nscoord dy = aDesiredSize.BlockStartAscent() -
332
0
    (indexRaisedAscent + indexSize.BlockStartAscent() - bmIndex.ascent);
333
0
  FinishReflowChild(indexFrame, aPresContext, indexSize, nullptr,
334
0
                    MirrorIfRTL(aDesiredSize.Width(), indexSize.Width(), dx),
335
0
                    dy, 0);
336
0
337
0
  // place the radical symbol and the radical bar
338
0
  dx = dxSqr;
339
0
  dy = indexClearance + leading; // leave a leading at the top
340
0
  mSqrChar.SetRect(nsRect(MirrorIfRTL(aDesiredSize.Width(), bmSqr.width, dx),
341
0
                          dy, bmSqr.width, bmSqr.ascent + bmSqr.descent));
342
0
  dx += bmSqr.width;
343
0
  mBarRect.SetRect(MirrorIfRTL(aDesiredSize.Width(), bmBase.width, dx),
344
0
                   dy, bmBase.width, ruleThickness);
345
0
346
0
  // place the base
347
0
  dy = aDesiredSize.BlockStartAscent() - baseSize.BlockStartAscent();
348
0
  FinishReflowChild(baseFrame, aPresContext, baseSize, nullptr,
349
0
                    MirrorIfRTL(aDesiredSize.Width(), baseSize.Width(), dx),
350
0
                    dy, 0);
351
0
352
0
  mReference.x = 0;
353
0
  mReference.y = aDesiredSize.BlockStartAscent();
354
0
355
0
  NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
356
0
}
357
358
/* virtual */ void
359
nsMathMLmrootFrame::GetIntrinsicISizeMetrics(gfxContext* aRenderingContext, ReflowOutput& aDesiredSize)
360
0
{
361
0
  nsIFrame* baseFrame = mFrames.FirstChild();
362
0
  nsIFrame* indexFrame = nullptr;
363
0
  if (baseFrame)
364
0
    indexFrame = baseFrame->GetNextSibling();
365
0
  if (!indexFrame || indexFrame->GetNextSibling()) {
366
0
    ReflowError(aRenderingContext->GetDrawTarget(), aDesiredSize);
367
0
    return;
368
0
  }
369
0
370
0
  float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
371
0
  nscoord baseWidth =
372
0
    nsLayoutUtils::IntrinsicForContainer(aRenderingContext, baseFrame,
373
0
                                         nsLayoutUtils::PREF_ISIZE);
374
0
  nscoord indexWidth =
375
0
    nsLayoutUtils::IntrinsicForContainer(aRenderingContext, indexFrame,
376
0
                                         nsLayoutUtils::PREF_ISIZE);
377
0
  nscoord sqrWidth = mSqrChar.GetMaxWidth(this,
378
0
                                          aRenderingContext->GetDrawTarget(),
379
0
                                          fontSizeInflation);
380
0
381
0
  nscoord dxSqr;
382
0
  RefPtr<nsFontMetrics> fm =
383
0
    nsLayoutUtils::GetFontMetricsForFrame(this, fontSizeInflation);
384
0
  GetRadicalXOffsets(indexWidth, sqrWidth, fm, nullptr, &dxSqr);
385
0
386
0
  nscoord width = dxSqr + sqrWidth + baseWidth;
387
0
388
0
  aDesiredSize.Width() = width;
389
0
  aDesiredSize.mBoundingMetrics.width = width;
390
0
  aDesiredSize.mBoundingMetrics.leftBearing = 0;
391
0
  aDesiredSize.mBoundingMetrics.rightBearing = width;
392
0
}
393
394
// ----------------------
395
// the Style System will use these to pass the proper ComputedStyle to our
396
// MathMLChar
397
ComputedStyle*
398
nsMathMLmrootFrame::GetAdditionalComputedStyle(int32_t aIndex) const
399
0
{
400
0
  switch (aIndex) {
401
0
  case NS_SQR_CHAR_STYLE_CONTEXT_INDEX:
402
0
    return mSqrChar.GetComputedStyle();
403
0
  default:
404
0
    return nullptr;
405
0
  }
406
0
}
407
408
void
409
nsMathMLmrootFrame::SetAdditionalComputedStyle(int32_t          aIndex,
410
                                              ComputedStyle*  aComputedStyle)
411
0
{
412
0
  switch (aIndex) {
413
0
  case NS_SQR_CHAR_STYLE_CONTEXT_INDEX:
414
0
    mSqrChar.SetComputedStyle(aComputedStyle);
415
0
    break;
416
0
  }
417
0
}