Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/layout/mathml/nsMathMLmencloseFrame.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 "nsMathMLmencloseFrame.h"
8
9
#include "gfx2DGlue.h"
10
#include "gfxUtils.h"
11
#include "mozilla/gfx/2D.h"
12
#include "mozilla/gfx/PathHelpers.h"
13
#include "nsPresContext.h"
14
#include "nsWhitespaceTokenizer.h"
15
16
#include "nsDisplayList.h"
17
#include "gfxContext.h"
18
#include "nsMathMLChar.h"
19
#include <algorithm>
20
21
using namespace mozilla;
22
using namespace mozilla::gfx;
23
24
//
25
// <menclose> -- enclose content with a stretching symbol such
26
// as a long division sign. - implementation
27
28
// longdiv:
29
// Unicode 5.1 assigns U+27CC to LONG DIVISION, but a right parenthesis
30
// renders better with current font support.
31
static const char16_t kLongDivChar = ')';
32
33
// radical: 'SQUARE ROOT'
34
static const char16_t kRadicalChar = 0x221A;
35
36
// updiagonalstrike
37
static const uint8_t kArrowHeadSize = 10;
38
39
// phasorangle
40
static const uint8_t kPhasorangleWidth = 8;
41
42
nsIFrame*
43
NS_NewMathMLmencloseFrame(nsIPresShell* aPresShell, ComputedStyle* aStyle)
44
0
{
45
0
  return new (aPresShell) nsMathMLmencloseFrame(aStyle);
46
0
}
47
48
NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmencloseFrame)
49
50
nsMathMLmencloseFrame::nsMathMLmencloseFrame(ComputedStyle* aStyle, ClassID aID) :
51
  nsMathMLContainerFrame(aStyle, aID),
52
  mRuleThickness(0), mRadicalRuleThickness(0),
53
  mLongDivCharIndex(-1), mRadicalCharIndex(-1), mContentWidth(0)
54
0
{
55
0
}
56
57
nsMathMLmencloseFrame::~nsMathMLmencloseFrame()
58
0
{
59
0
}
60
61
nsresult nsMathMLmencloseFrame::AllocateMathMLChar(nsMencloseNotation mask)
62
0
{
63
0
  // Is the char already allocated?
64
0
  if ((mask == NOTATION_LONGDIV && mLongDivCharIndex >= 0) ||
65
0
      (mask == NOTATION_RADICAL && mRadicalCharIndex >= 0))
66
0
    return NS_OK;
67
0
68
0
  // No need to track the ComputedStyle given to our MathML chars.
69
0
  // The Style System will use Get/SetAdditionalComputedStyle() to keep it
70
0
  // up-to-date if dynamic changes arise.
71
0
  uint32_t i = mMathMLChar.Length();
72
0
  nsAutoString Char;
73
0
74
0
  if (!mMathMLChar.AppendElement())
75
0
    return NS_ERROR_OUT_OF_MEMORY;
76
0
77
0
  if (mask == NOTATION_LONGDIV) {
78
0
    Char.Assign(kLongDivChar);
79
0
    mLongDivCharIndex = i;
80
0
  } else if (mask == NOTATION_RADICAL) {
81
0
    Char.Assign(kRadicalChar);
82
0
    mRadicalCharIndex = i;
83
0
  }
84
0
85
0
  nsPresContext *presContext = PresContext();
86
0
  mMathMLChar[i].SetData(Char);
87
0
  ResolveMathMLCharStyle(presContext, mContent, mComputedStyle, &mMathMLChar[i]);
88
0
89
0
  return NS_OK;
90
0
}
91
92
/*
93
 * Add a notation to draw, if the argument is the name of a known notation.
94
 * @param aNotation string name of a notation
95
 */
96
nsresult nsMathMLmencloseFrame::AddNotation(const nsAString& aNotation)
97
0
{
98
0
  nsresult rv;
99
0
100
0
  if (aNotation.EqualsLiteral("longdiv")) {
101
0
    rv = AllocateMathMLChar(NOTATION_LONGDIV);
102
0
    NS_ENSURE_SUCCESS(rv, rv);
103
0
    mNotationsToDraw += NOTATION_LONGDIV;
104
0
  } else if (aNotation.EqualsLiteral("actuarial")) {
105
0
    mNotationsToDraw += NOTATION_RIGHT;
106
0
    mNotationsToDraw += NOTATION_TOP;
107
0
  } else if (aNotation.EqualsLiteral("radical")) {
108
0
    rv = AllocateMathMLChar(NOTATION_RADICAL);
109
0
    NS_ENSURE_SUCCESS(rv, rv);
110
0
    mNotationsToDraw += NOTATION_RADICAL;
111
0
  } else if (aNotation.EqualsLiteral("box")) {
112
0
    mNotationsToDraw += NOTATION_LEFT;
113
0
    mNotationsToDraw += NOTATION_RIGHT;
114
0
    mNotationsToDraw += NOTATION_TOP;
115
0
    mNotationsToDraw += NOTATION_BOTTOM;
116
0
  } else if (aNotation.EqualsLiteral("roundedbox")) {
117
0
    mNotationsToDraw += NOTATION_ROUNDEDBOX;
118
0
  } else if (aNotation.EqualsLiteral("circle")) {
119
0
    mNotationsToDraw += NOTATION_CIRCLE;
120
0
  } else if (aNotation.EqualsLiteral("left")) {
121
0
    mNotationsToDraw += NOTATION_LEFT;
122
0
  } else if (aNotation.EqualsLiteral("right")) {
123
0
    mNotationsToDraw += NOTATION_RIGHT;
124
0
  } else if (aNotation.EqualsLiteral("top")) {
125
0
    mNotationsToDraw += NOTATION_TOP;
126
0
  } else if (aNotation.EqualsLiteral("bottom")) {
127
0
    mNotationsToDraw += NOTATION_BOTTOM;
128
0
  } else if (aNotation.EqualsLiteral("updiagonalstrike")) {
129
0
    mNotationsToDraw += NOTATION_UPDIAGONALSTRIKE;
130
0
  } else if (aNotation.EqualsLiteral("updiagonalarrow")) {
131
0
    mNotationsToDraw += NOTATION_UPDIAGONALARROW;
132
0
  } else if (aNotation.EqualsLiteral("downdiagonalstrike")) {
133
0
    mNotationsToDraw += NOTATION_DOWNDIAGONALSTRIKE;
134
0
  } else if (aNotation.EqualsLiteral("verticalstrike")) {
135
0
    mNotationsToDraw += NOTATION_VERTICALSTRIKE;
136
0
  } else if (aNotation.EqualsLiteral("horizontalstrike")) {
137
0
    mNotationsToDraw += NOTATION_HORIZONTALSTRIKE;
138
0
  } else if (aNotation.EqualsLiteral("madruwb")) {
139
0
    mNotationsToDraw += NOTATION_RIGHT;
140
0
    mNotationsToDraw += NOTATION_BOTTOM;
141
0
  } else if (aNotation.EqualsLiteral("phasorangle")) {
142
0
    mNotationsToDraw += NOTATION_BOTTOM;
143
0
    mNotationsToDraw += NOTATION_PHASORANGLE;
144
0
  }
145
0
146
0
  return NS_OK;
147
0
}
148
149
/*
150
 * Initialize the list of notations to draw
151
 */
152
void nsMathMLmencloseFrame::InitNotations()
153
0
{
154
0
  MarkNeedsDisplayItemRebuild();
155
0
  mNotationsToDraw.clear();
156
0
  mLongDivCharIndex = mRadicalCharIndex = -1;
157
0
  mMathMLChar.Clear();
158
0
159
0
  nsAutoString value;
160
0
161
0
  if (mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::notation_, value)) {
162
0
    // parse the notation attribute
163
0
    nsWhitespaceTokenizer tokenizer(value);
164
0
165
0
    while (tokenizer.hasMoreTokens())
166
0
      AddNotation(tokenizer.nextToken());
167
0
168
0
    if (IsToDraw(NOTATION_UPDIAGONALARROW)) {
169
0
      // For <menclose notation="updiagonalstrike updiagonalarrow">, if
170
0
      // the two notations are drawn then the strike line may cause the point of
171
0
      // the arrow to be too wide. Hence we will only draw the updiagonalarrow
172
0
      // and the arrow shaft may be thought to be the updiagonalstrike.
173
0
      mNotationsToDraw -= NOTATION_UPDIAGONALSTRIKE;
174
0
    }
175
0
  } else {
176
0
    // default: longdiv
177
0
    if (NS_FAILED(AllocateMathMLChar(NOTATION_LONGDIV)))
178
0
      return;
179
0
    mNotationsToDraw += NOTATION_LONGDIV;
180
0
  }
181
0
}
182
183
NS_IMETHODIMP
184
nsMathMLmencloseFrame::InheritAutomaticData(nsIFrame* aParent)
185
0
{
186
0
  // let the base class get the default from our parent
187
0
  nsMathMLContainerFrame::InheritAutomaticData(aParent);
188
0
189
0
  mPresentationData.flags |= NS_MATHML_STRETCH_ALL_CHILDREN_VERTICALLY;
190
0
191
0
  InitNotations();
192
0
193
0
  return NS_OK;
194
0
}
195
196
NS_IMETHODIMP
197
nsMathMLmencloseFrame::TransmitAutomaticData()
198
0
{
199
0
  if (IsToDraw(NOTATION_RADICAL)) {
200
0
    // The TeXBook (Ch 17. p.141) says that \sqrt is cramped
201
0
    UpdatePresentationDataFromChildAt(0, -1,
202
0
                                      NS_MATHML_COMPRESSED,
203
0
                                      NS_MATHML_COMPRESSED);
204
0
  }
205
0
206
0
  return NS_OK;
207
0
}
208
209
void
210
nsMathMLmencloseFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
211
                                        const nsDisplayListSet& aLists)
212
0
{
213
0
  /////////////
214
0
  // paint the menclosed content
215
0
  nsMathMLContainerFrame::BuildDisplayList(aBuilder, aLists);
216
0
217
0
  if (NS_MATHML_HAS_ERROR(mPresentationData.flags))
218
0
    return;
219
0
220
0
  nsRect mencloseRect = nsIFrame::GetRect();
221
0
  mencloseRect.x = mencloseRect.y = 0;
222
0
223
0
  if (IsToDraw(NOTATION_RADICAL)) {
224
0
    mMathMLChar[mRadicalCharIndex].Display(aBuilder, this, aLists, 0);
225
0
226
0
    nsRect rect;
227
0
    mMathMLChar[mRadicalCharIndex].GetRect(rect);
228
0
    rect.MoveBy(StyleVisibility()->mDirection ? -mContentWidth : rect.width, 0);
229
0
    rect.SizeTo(mContentWidth, mRadicalRuleThickness);
230
0
    DisplayBar(aBuilder, this, rect, aLists, NOTATION_RADICAL);
231
0
  }
232
0
233
0
  if (IsToDraw(NOTATION_PHASORANGLE)) {
234
0
    DisplayNotation(aBuilder, this, mencloseRect, aLists,
235
0
                mRuleThickness, NOTATION_PHASORANGLE);
236
0
  }
237
0
238
0
  if (IsToDraw(NOTATION_LONGDIV)) {
239
0
    mMathMLChar[mLongDivCharIndex].Display(aBuilder, this, aLists, 1);
240
0
241
0
    nsRect rect;
242
0
    mMathMLChar[mLongDivCharIndex].GetRect(rect);
243
0
    rect.SizeTo(rect.width + mContentWidth, mRuleThickness);
244
0
    DisplayBar(aBuilder, this, rect, aLists, NOTATION_LONGDIV);
245
0
  }
246
0
247
0
  if (IsToDraw(NOTATION_TOP)) {
248
0
    nsRect rect(0, 0, mencloseRect.width, mRuleThickness);
249
0
    DisplayBar(aBuilder, this, rect, aLists, NOTATION_TOP);
250
0
  }
251
0
252
0
  if (IsToDraw(NOTATION_BOTTOM)) {
253
0
    nsRect rect(0, mencloseRect.height - mRuleThickness,
254
0
                mencloseRect.width, mRuleThickness);
255
0
    DisplayBar(aBuilder, this, rect, aLists, NOTATION_BOTTOM);
256
0
  }
257
0
258
0
  if (IsToDraw(NOTATION_LEFT)) {
259
0
    nsRect rect(0, 0, mRuleThickness, mencloseRect.height);
260
0
    DisplayBar(aBuilder, this, rect, aLists, NOTATION_LEFT);
261
0
  }
262
0
263
0
  if (IsToDraw(NOTATION_RIGHT)) {
264
0
    nsRect rect(mencloseRect.width - mRuleThickness, 0,
265
0
                mRuleThickness, mencloseRect.height);
266
0
    DisplayBar(aBuilder, this, rect, aLists, NOTATION_RIGHT);
267
0
  }
268
0
269
0
  if (IsToDraw(NOTATION_ROUNDEDBOX)) {
270
0
    DisplayNotation(aBuilder, this, mencloseRect, aLists,
271
0
                    mRuleThickness, NOTATION_ROUNDEDBOX);
272
0
  }
273
0
274
0
  if (IsToDraw(NOTATION_CIRCLE)) {
275
0
    DisplayNotation(aBuilder, this, mencloseRect, aLists,
276
0
                    mRuleThickness, NOTATION_CIRCLE);
277
0
  }
278
0
279
0
  if (IsToDraw(NOTATION_UPDIAGONALSTRIKE)) {
280
0
    DisplayNotation(aBuilder, this, mencloseRect, aLists,
281
0
                    mRuleThickness, NOTATION_UPDIAGONALSTRIKE);
282
0
  }
283
0
284
0
  if (IsToDraw(NOTATION_UPDIAGONALARROW)) {
285
0
    DisplayNotation(aBuilder, this, mencloseRect, aLists,
286
0
                    mRuleThickness, NOTATION_UPDIAGONALARROW);
287
0
  }
288
0
289
0
  if (IsToDraw(NOTATION_DOWNDIAGONALSTRIKE)) {
290
0
    DisplayNotation(aBuilder, this, mencloseRect, aLists,
291
0
                    mRuleThickness, NOTATION_DOWNDIAGONALSTRIKE);
292
0
  }
293
0
294
0
  if (IsToDraw(NOTATION_HORIZONTALSTRIKE)) {
295
0
    nsRect rect(0, mencloseRect.height / 2 - mRuleThickness / 2,
296
0
                mencloseRect.width, mRuleThickness);
297
0
    DisplayBar(aBuilder, this, rect, aLists, NOTATION_HORIZONTALSTRIKE);
298
0
  }
299
0
300
0
  if (IsToDraw(NOTATION_VERTICALSTRIKE)) {
301
0
    nsRect rect(mencloseRect.width / 2 - mRuleThickness / 2, 0,
302
0
                mRuleThickness, mencloseRect.height);
303
0
    DisplayBar(aBuilder, this, rect, aLists, NOTATION_VERTICALSTRIKE);
304
0
  }
305
0
}
306
307
/* virtual */ nsresult
308
nsMathMLmencloseFrame::MeasureForWidth(DrawTarget* aDrawTarget,
309
                                       ReflowOutput& aDesiredSize)
310
0
{
311
0
  return PlaceInternal(aDrawTarget, false, aDesiredSize, true);
312
0
}
313
314
/* virtual */ nsresult
315
nsMathMLmencloseFrame::Place(DrawTarget*          aDrawTarget,
316
                             bool                 aPlaceOrigin,
317
                             ReflowOutput& aDesiredSize)
318
0
{
319
0
  return PlaceInternal(aDrawTarget, aPlaceOrigin, aDesiredSize, false);
320
0
}
321
322
/* virtual */ nsresult
323
nsMathMLmencloseFrame::PlaceInternal(DrawTarget*          aDrawTarget,
324
                                     bool                 aPlaceOrigin,
325
                                     ReflowOutput& aDesiredSize,
326
                                     bool                 aWidthOnly)
327
0
{
328
0
  ///////////////
329
0
  // Measure the size of our content using the base class to format like an
330
0
  // inferred mrow.
331
0
  ReflowOutput baseSize(aDesiredSize.GetWritingMode());
332
0
  nsresult rv =
333
0
    nsMathMLContainerFrame::Place(aDrawTarget, false, baseSize);
334
0
335
0
  if (NS_MATHML_HAS_ERROR(mPresentationData.flags) || NS_FAILED(rv)) {
336
0
      DidReflowChildren(PrincipalChildList().FirstChild());
337
0
      return rv;
338
0
    }
339
0
340
0
  nsBoundingMetrics bmBase = baseSize.mBoundingMetrics;
341
0
  nscoord dx_left = 0, dx_right = 0;
342
0
  nsBoundingMetrics bmLongdivChar, bmRadicalChar;
343
0
  nscoord radicalAscent = 0, radicalDescent = 0;
344
0
  nscoord longdivAscent = 0, longdivDescent = 0;
345
0
  nscoord psi = 0;
346
0
  nscoord leading = 0;
347
0
348
0
  ///////////////
349
0
  // Thickness of bars and font metrics
350
0
  nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
351
0
352
0
  float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
353
0
  RefPtr<nsFontMetrics> fm =
354
0
    nsLayoutUtils::GetFontMetricsForFrame(this, fontSizeInflation);
355
0
  GetRuleThickness(aDrawTarget, fm, mRuleThickness);
356
0
  if (mRuleThickness < onePixel) {
357
0
    mRuleThickness = onePixel;
358
0
  }
359
0
360
0
  char16_t one = '1';
361
0
  nsBoundingMetrics bmOne =
362
0
    nsLayoutUtils::AppUnitBoundsOfString(&one, 1, *fm, aDrawTarget);
363
0
364
0
  ///////////////
365
0
  // General rules: the menclose element takes the size of the enclosed content.
366
0
  // We add a padding when needed.
367
0
368
0
  // determine padding & psi
369
0
  nscoord padding = 3 * mRuleThickness;
370
0
  nscoord delta = padding % onePixel;
371
0
  if (delta)
372
0
    padding += onePixel - delta; // round up
373
0
374
0
  if (IsToDraw(NOTATION_LONGDIV) || IsToDraw(NOTATION_RADICAL)) {
375
0
    GetRadicalParameters(fm, StyleFont()->mMathDisplay ==
376
0
                         NS_MATHML_DISPLAYSTYLE_BLOCK,
377
0
                         mRadicalRuleThickness, leading, psi);
378
0
379
0
    // make sure that the rule appears on on screen
380
0
    if (mRadicalRuleThickness < onePixel) {
381
0
      mRadicalRuleThickness = onePixel;
382
0
    }
383
0
384
0
    // adjust clearance psi to get an exact number of pixels -- this
385
0
    // gives a nicer & uniform look on stacked radicals (bug 130282)
386
0
    delta = psi % onePixel;
387
0
    if (delta) {
388
0
      psi += onePixel - delta; // round up
389
0
    }
390
0
  }
391
0
392
0
  // Set horizontal parameters
393
0
  if (IsToDraw(NOTATION_ROUNDEDBOX) ||
394
0
      IsToDraw(NOTATION_TOP) ||
395
0
      IsToDraw(NOTATION_LEFT) ||
396
0
      IsToDraw(NOTATION_BOTTOM) ||
397
0
      IsToDraw(NOTATION_CIRCLE))
398
0
    dx_left = padding;
399
0
400
0
  if (IsToDraw(NOTATION_ROUNDEDBOX) ||
401
0
      IsToDraw(NOTATION_TOP) ||
402
0
      IsToDraw(NOTATION_RIGHT) ||
403
0
      IsToDraw(NOTATION_BOTTOM) ||
404
0
      IsToDraw(NOTATION_CIRCLE))
405
0
    dx_right = padding;
406
0
407
0
  // Set vertical parameters
408
0
  if (IsToDraw(NOTATION_RIGHT) ||
409
0
      IsToDraw(NOTATION_LEFT) ||
410
0
      IsToDraw(NOTATION_UPDIAGONALSTRIKE) ||
411
0
      IsToDraw(NOTATION_UPDIAGONALARROW) ||
412
0
      IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) ||
413
0
      IsToDraw(NOTATION_VERTICALSTRIKE) ||
414
0
      IsToDraw(NOTATION_CIRCLE) ||
415
0
      IsToDraw(NOTATION_ROUNDEDBOX) ||
416
0
      IsToDraw(NOTATION_RADICAL) ||
417
0
      IsToDraw(NOTATION_LONGDIV) ||
418
0
      IsToDraw(NOTATION_PHASORANGLE)) {
419
0
      // set a minimal value for the base height
420
0
      bmBase.ascent = std::max(bmOne.ascent, bmBase.ascent);
421
0
      bmBase.descent = std::max(0, bmBase.descent);
422
0
  }
423
0
424
0
  mBoundingMetrics.ascent = bmBase.ascent;
425
0
  mBoundingMetrics.descent = bmBase.descent;
426
0
427
0
  if (IsToDraw(NOTATION_ROUNDEDBOX) ||
428
0
      IsToDraw(NOTATION_TOP) ||
429
0
      IsToDraw(NOTATION_LEFT) ||
430
0
      IsToDraw(NOTATION_RIGHT) ||
431
0
      IsToDraw(NOTATION_CIRCLE))
432
0
    mBoundingMetrics.ascent += padding;
433
0
434
0
  if (IsToDraw(NOTATION_ROUNDEDBOX) ||
435
0
      IsToDraw(NOTATION_LEFT) ||
436
0
      IsToDraw(NOTATION_RIGHT) ||
437
0
      IsToDraw(NOTATION_BOTTOM) ||
438
0
      IsToDraw(NOTATION_CIRCLE))
439
0
    mBoundingMetrics.descent += padding;
440
0
441
0
   ///////////////
442
0
   // phasorangle notation
443
0
  if (IsToDraw(NOTATION_PHASORANGLE)) {
444
0
    nscoord phasorangleWidth = kPhasorangleWidth * mRuleThickness;
445
0
    // Update horizontal parameters
446
0
    dx_left = std::max(dx_left, phasorangleWidth);
447
0
  }
448
0
449
0
  ///////////////
450
0
  // updiagonal arrow notation. We need enough space at the top right corner to
451
0
  // draw the arrow head.
452
0
  if (IsToDraw(NOTATION_UPDIAGONALARROW)) {
453
0
    // This is an estimate, see nsDisplayNotation::Paint for the exact head size
454
0
    nscoord arrowHeadSize = kArrowHeadSize * mRuleThickness;
455
0
456
0
    // We want that the arrow shaft strikes the menclose content and that the
457
0
    // arrow head does not overlap with that content. Hence we add some space
458
0
    // on the right. We don't add space on the top but only ensure that the
459
0
    // ascent is large enough.
460
0
    dx_right = std::max(dx_right, arrowHeadSize);
461
0
    mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent, arrowHeadSize);
462
0
  }
463
0
464
0
  ///////////////
465
0
  // circle notation: we don't want the ellipse to overlap the enclosed
466
0
  // content. Hence, we need to increase the size of the bounding box by a
467
0
  // factor of at least sqrt(2).
468
0
  if (IsToDraw(NOTATION_CIRCLE)) {
469
0
    double ratio = (sqrt(2.0) - 1.0) / 2.0;
470
0
    nscoord padding2;
471
0
472
0
    // Update horizontal parameters
473
0
    padding2 = ratio * bmBase.width;
474
0
475
0
    dx_left = std::max(dx_left, padding2);
476
0
    dx_right = std::max(dx_right, padding2);
477
0
478
0
    // Update vertical parameters
479
0
    padding2 = ratio * (bmBase.ascent + bmBase.descent);
480
0
481
0
    mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent,
482
0
                                     bmBase.ascent + padding2);
483
0
    mBoundingMetrics.descent = std::max(mBoundingMetrics.descent,
484
0
                                      bmBase.descent + padding2);
485
0
  }
486
0
487
0
  ///////////////
488
0
  // longdiv notation:
489
0
  if (IsToDraw(NOTATION_LONGDIV)) {
490
0
    if (aWidthOnly) {
491
0
        nscoord longdiv_width = mMathMLChar[mLongDivCharIndex].
492
0
          GetMaxWidth(this, aDrawTarget, fontSizeInflation);
493
0
494
0
        // Update horizontal parameters
495
0
        dx_left = std::max(dx_left, longdiv_width);
496
0
    } else {
497
0
      // Stretch the parenthesis to the appropriate height if it is not
498
0
      // big enough.
499
0
      nsBoundingMetrics contSize = bmBase;
500
0
      contSize.ascent = mRuleThickness;
501
0
      contSize.descent = bmBase.ascent + bmBase.descent + psi;
502
0
503
0
      // height(longdiv) should be >= height(base) + psi + mRuleThickness
504
0
      mMathMLChar[mLongDivCharIndex].Stretch(this, aDrawTarget,
505
0
                                             fontSizeInflation,
506
0
                                             NS_STRETCH_DIRECTION_VERTICAL,
507
0
                                             contSize, bmLongdivChar,
508
0
                                             NS_STRETCH_LARGER, false);
509
0
      mMathMLChar[mLongDivCharIndex].GetBoundingMetrics(bmLongdivChar);
510
0
511
0
      // Update horizontal parameters
512
0
      dx_left = std::max(dx_left, bmLongdivChar.width);
513
0
514
0
      // Update vertical parameters
515
0
      longdivAscent = bmBase.ascent + psi + mRuleThickness;
516
0
      longdivDescent = std::max(bmBase.descent,
517
0
                              (bmLongdivChar.ascent + bmLongdivChar.descent -
518
0
                               longdivAscent));
519
0
520
0
      mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent,
521
0
                                       longdivAscent);
522
0
      mBoundingMetrics.descent = std::max(mBoundingMetrics.descent,
523
0
                                        longdivDescent);
524
0
    }
525
0
  }
526
0
527
0
  ///////////////
528
0
  // radical notation:
529
0
  if (IsToDraw(NOTATION_RADICAL)) {
530
0
    nscoord *dx_leading = StyleVisibility()->mDirection ? &dx_right : &dx_left;
531
0
532
0
    if (aWidthOnly) {
533
0
      nscoord radical_width = mMathMLChar[mRadicalCharIndex].
534
0
        GetMaxWidth(this, aDrawTarget, fontSizeInflation);
535
0
536
0
      // Update horizontal parameters
537
0
      *dx_leading = std::max(*dx_leading, radical_width);
538
0
    } else {
539
0
      // Stretch the radical symbol to the appropriate height if it is not
540
0
      // big enough.
541
0
      nsBoundingMetrics contSize = bmBase;
542
0
      contSize.ascent = mRadicalRuleThickness;
543
0
      contSize.descent = bmBase.ascent + bmBase.descent + psi;
544
0
545
0
      // height(radical) should be >= height(base) + psi + mRadicalRuleThickness
546
0
      mMathMLChar[mRadicalCharIndex].Stretch(this, aDrawTarget,
547
0
                                             fontSizeInflation,
548
0
                                             NS_STRETCH_DIRECTION_VERTICAL,
549
0
                                             contSize, bmRadicalChar,
550
0
                                             NS_STRETCH_LARGER,
551
0
                                             StyleVisibility()->mDirection);
552
0
      mMathMLChar[mRadicalCharIndex].GetBoundingMetrics(bmRadicalChar);
553
0
554
0
      // Update horizontal parameters
555
0
      *dx_leading = std::max(*dx_leading, bmRadicalChar.width);
556
0
557
0
      // Update vertical parameters
558
0
      radicalAscent = bmBase.ascent + psi + mRadicalRuleThickness;
559
0
      radicalDescent = std::max(bmBase.descent,
560
0
                              (bmRadicalChar.ascent + bmRadicalChar.descent -
561
0
                               radicalAscent));
562
0
563
0
      mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent,
564
0
                                       radicalAscent);
565
0
      mBoundingMetrics.descent = std::max(mBoundingMetrics.descent,
566
0
                                        radicalDescent);
567
0
    }
568
0
  }
569
0
570
0
  ///////////////
571
0
  //
572
0
  if (IsToDraw(NOTATION_CIRCLE) ||
573
0
      IsToDraw(NOTATION_ROUNDEDBOX) ||
574
0
      (IsToDraw(NOTATION_LEFT) && IsToDraw(NOTATION_RIGHT))) {
575
0
    // center the menclose around the content (horizontally)
576
0
    dx_left = dx_right = std::max(dx_left, dx_right);
577
0
  }
578
0
579
0
  ///////////////
580
0
  // The maximum size is now computed: set the remaining parameters
581
0
  mBoundingMetrics.width = dx_left + bmBase.width + dx_right;
582
0
583
0
  mBoundingMetrics.leftBearing = std::min(0, dx_left + bmBase.leftBearing);
584
0
  mBoundingMetrics.rightBearing =
585
0
    std::max(mBoundingMetrics.width, dx_left + bmBase.rightBearing);
586
0
587
0
  aDesiredSize.Width() = mBoundingMetrics.width;
588
0
589
0
  aDesiredSize.SetBlockStartAscent(std::max(mBoundingMetrics.ascent,
590
0
                                            baseSize.BlockStartAscent()));
591
0
  aDesiredSize.Height() = aDesiredSize.BlockStartAscent() +
592
0
    std::max(mBoundingMetrics.descent,
593
0
             baseSize.Height() - baseSize.BlockStartAscent());
594
0
595
0
  if (IsToDraw(NOTATION_LONGDIV) || IsToDraw(NOTATION_RADICAL)) {
596
0
    nscoord desiredSizeAscent = aDesiredSize.BlockStartAscent();
597
0
    nscoord desiredSizeDescent = aDesiredSize.Height() -
598
0
                                 aDesiredSize.BlockStartAscent();
599
0
600
0
    if (IsToDraw(NOTATION_LONGDIV)) {
601
0
      desiredSizeAscent = std::max(desiredSizeAscent,
602
0
                                 longdivAscent + leading);
603
0
      desiredSizeDescent = std::max(desiredSizeDescent,
604
0
                                  longdivDescent + mRuleThickness);
605
0
    }
606
0
607
0
    if (IsToDraw(NOTATION_RADICAL)) {
608
0
      desiredSizeAscent = std::max(desiredSizeAscent,
609
0
                                 radicalAscent + leading);
610
0
      desiredSizeDescent = std::max(desiredSizeDescent,
611
0
                                    radicalDescent + mRadicalRuleThickness);
612
0
    }
613
0
614
0
    aDesiredSize.SetBlockStartAscent(desiredSizeAscent);
615
0
    aDesiredSize.Height() = desiredSizeAscent + desiredSizeDescent;
616
0
  }
617
0
618
0
  if (IsToDraw(NOTATION_CIRCLE) ||
619
0
      IsToDraw(NOTATION_ROUNDEDBOX) ||
620
0
      (IsToDraw(NOTATION_TOP) && IsToDraw(NOTATION_BOTTOM))) {
621
0
    // center the menclose around the content (vertically)
622
0
    nscoord dy = std::max(aDesiredSize.BlockStartAscent() - bmBase.ascent,
623
0
                          aDesiredSize.Height() -
624
0
                          aDesiredSize.BlockStartAscent() - bmBase.descent);
625
0
626
0
    aDesiredSize.SetBlockStartAscent(bmBase.ascent + dy);
627
0
    aDesiredSize.Height() = aDesiredSize.BlockStartAscent() + bmBase.descent + dy;
628
0
  }
629
0
630
0
  // Update mBoundingMetrics ascent/descent
631
0
  if (IsToDraw(NOTATION_TOP) ||
632
0
      IsToDraw(NOTATION_RIGHT) ||
633
0
      IsToDraw(NOTATION_LEFT) ||
634
0
      IsToDraw(NOTATION_UPDIAGONALSTRIKE) ||
635
0
      IsToDraw(NOTATION_UPDIAGONALARROW) ||
636
0
      IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) ||
637
0
      IsToDraw(NOTATION_VERTICALSTRIKE) ||
638
0
      IsToDraw(NOTATION_CIRCLE) ||
639
0
      IsToDraw(NOTATION_ROUNDEDBOX))
640
0
    mBoundingMetrics.ascent = aDesiredSize.BlockStartAscent();
641
0
642
0
  if (IsToDraw(NOTATION_BOTTOM) ||
643
0
      IsToDraw(NOTATION_RIGHT) ||
644
0
      IsToDraw(NOTATION_LEFT) ||
645
0
      IsToDraw(NOTATION_UPDIAGONALSTRIKE) ||
646
0
      IsToDraw(NOTATION_UPDIAGONALARROW) ||
647
0
      IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) ||
648
0
      IsToDraw(NOTATION_VERTICALSTRIKE) ||
649
0
      IsToDraw(NOTATION_CIRCLE) ||
650
0
      IsToDraw(NOTATION_ROUNDEDBOX))
651
0
    mBoundingMetrics.descent = aDesiredSize.Height() - aDesiredSize.BlockStartAscent();
652
0
653
0
  // phasorangle notation:
654
0
  // move up from the bottom by the angled line height
655
0
  if (IsToDraw(NOTATION_PHASORANGLE))
656
0
    mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent, 2 * kPhasorangleWidth * mRuleThickness - mBoundingMetrics.descent);
657
0
658
0
  aDesiredSize.mBoundingMetrics = mBoundingMetrics;
659
0
660
0
  mReference.x = 0;
661
0
  mReference.y = aDesiredSize.BlockStartAscent();
662
0
663
0
  if (aPlaceOrigin) {
664
0
    //////////////////
665
0
    // Set position and size of MathMLChars
666
0
    if (IsToDraw(NOTATION_LONGDIV))
667
0
      mMathMLChar[mLongDivCharIndex].SetRect(nsRect(dx_left -
668
0
                                                    bmLongdivChar.width,
669
0
                                                    aDesiredSize.BlockStartAscent() -
670
0
                                                    longdivAscent,
671
0
                                                    bmLongdivChar.width,
672
0
                                                    bmLongdivChar.ascent +
673
0
                                                    bmLongdivChar.descent));
674
0
675
0
    if (IsToDraw(NOTATION_RADICAL)) {
676
0
      nscoord dx = (StyleVisibility()->mDirection ?
677
0
                    dx_left + bmBase.width : dx_left - bmRadicalChar.width);
678
0
679
0
      mMathMLChar[mRadicalCharIndex].SetRect(nsRect(dx,
680
0
                                                    aDesiredSize.BlockStartAscent() -
681
0
                                                    radicalAscent,
682
0
                                                    bmRadicalChar.width,
683
0
                                                    bmRadicalChar.ascent +
684
0
                                                    bmRadicalChar.descent));
685
0
    }
686
0
687
0
    mContentWidth = bmBase.width;
688
0
689
0
    //////////////////
690
0
    // Finish reflowing child frames
691
0
    PositionRowChildFrames(dx_left, aDesiredSize.BlockStartAscent());
692
0
  }
693
0
694
0
  return NS_OK;
695
0
}
696
697
nscoord
698
nsMathMLmencloseFrame::FixInterFrameSpacing(ReflowOutput& aDesiredSize)
699
0
{
700
0
  nscoord gap = nsMathMLContainerFrame::FixInterFrameSpacing(aDesiredSize);
701
0
  if (!gap)
702
0
    return 0;
703
0
704
0
  // Move the MathML characters
705
0
  nsRect rect;
706
0
  for (uint32_t i = 0; i < mMathMLChar.Length(); i++) {
707
0
    mMathMLChar[i].GetRect(rect);
708
0
    rect.MoveBy(gap, 0);
709
0
    mMathMLChar[i].SetRect(rect);
710
0
  }
711
0
712
0
  return gap;
713
0
}
714
715
nsresult
716
nsMathMLmencloseFrame::AttributeChanged(int32_t         aNameSpaceID,
717
                                        nsAtom*        aAttribute,
718
                                        int32_t         aModType)
719
0
{
720
0
  if (aAttribute == nsGkAtoms::notation_) {
721
0
    InitNotations();
722
0
  }
723
0
724
0
  return nsMathMLContainerFrame::
725
0
    AttributeChanged(aNameSpaceID, aAttribute, aModType);
726
0
}
727
728
//////////////////
729
// the Style System will use these to pass the proper ComputedStyle to our
730
// MathMLChar
731
ComputedStyle*
732
nsMathMLmencloseFrame::GetAdditionalComputedStyle(int32_t aIndex) const
733
0
{
734
0
  int32_t len = mMathMLChar.Length();
735
0
  if (aIndex >= 0 && aIndex < len)
736
0
    return mMathMLChar[aIndex].GetComputedStyle();
737
0
  else
738
0
    return nullptr;
739
0
}
740
741
void
742
nsMathMLmencloseFrame::SetAdditionalComputedStyle(int32_t          aIndex,
743
                                                 ComputedStyle*  aComputedStyle)
744
0
{
745
0
  int32_t len = mMathMLChar.Length();
746
0
  if (aIndex >= 0 && aIndex < len)
747
0
    mMathMLChar[aIndex].SetComputedStyle(aComputedStyle);
748
0
}
749
750
class nsDisplayNotation final : public nsDisplayItem
751
{
752
public:
753
  nsDisplayNotation(nsDisplayListBuilder* aBuilder,
754
                    nsIFrame* aFrame, const nsRect& aRect,
755
                    nscoord aThickness, nsMencloseNotation aType)
756
    : nsDisplayItem(aBuilder, aFrame), mRect(aRect),
757
0
      mThickness(aThickness), mType(aType) {
758
0
    MOZ_COUNT_CTOR(nsDisplayNotation);
759
0
  }
760
#ifdef NS_BUILD_REFCNT_LOGGING
761
  virtual ~nsDisplayNotation() {
762
    MOZ_COUNT_DTOR(nsDisplayNotation);
763
  }
764
#endif
765
766
0
  virtual uint32_t GetPerFrameKey() const override {
767
0
    return (mType << TYPE_BITS) | nsDisplayItem::GetPerFrameKey();
768
0
  }
769
770
  virtual void Paint(nsDisplayListBuilder* aBuilder,
771
                     gfxContext* aCtx) override;
772
  NS_DISPLAY_DECL_NAME("MathMLMencloseNotation", TYPE_MATHML_MENCLOSE_NOTATION)
773
774
private:
775
  nsRect             mRect;
776
  nscoord            mThickness;
777
  nsMencloseNotation mType;
778
};
779
780
void nsDisplayNotation::Paint(nsDisplayListBuilder* aBuilder,
781
                              gfxContext* aCtx)
782
0
{
783
0
  DrawTarget& aDrawTarget = *aCtx->GetDrawTarget();
784
0
  nsPresContext* presContext = mFrame->PresContext();
785
0
786
0
  Float strokeWidth = presContext->AppUnitsToGfxUnits(mThickness);
787
0
788
0
  Rect rect = NSRectToRect(mRect + ToReferenceFrame(),
789
0
                           presContext->AppUnitsPerDevPixel());
790
0
  rect.Deflate(strokeWidth / 2.f);
791
0
792
0
  ColorPattern color(ToDeviceColor(
793
0
    mFrame->GetVisitedDependentColor(&nsStyleText::mWebkitTextFillColor)));
794
0
795
0
  StrokeOptions strokeOptions(strokeWidth);
796
0
797
0
  switch(mType)
798
0
  {
799
0
    case NOTATION_CIRCLE: {
800
0
      RefPtr<Path> ellipse =
801
0
        MakePathForEllipse(aDrawTarget, rect.Center(), rect.Size());
802
0
      aDrawTarget.Stroke(ellipse, color, strokeOptions);
803
0
      return;
804
0
    }
805
0
    case NOTATION_ROUNDEDBOX: {
806
0
      Float radius = 3 * strokeWidth;
807
0
      RectCornerRadii radii(radius, radius);
808
0
      RefPtr<Path> roundedRect =
809
0
        MakePathForRoundedRect(aDrawTarget, rect, radii, true);
810
0
      aDrawTarget.Stroke(roundedRect, color, strokeOptions);
811
0
      return;
812
0
    }
813
0
    case NOTATION_UPDIAGONALSTRIKE: {
814
0
      aDrawTarget.StrokeLine(rect.BottomLeft(), rect.TopRight(),
815
0
                             color, strokeOptions);
816
0
      return;
817
0
    }
818
0
    case NOTATION_DOWNDIAGONALSTRIKE: {
819
0
      aDrawTarget.StrokeLine(rect.TopLeft(), rect.BottomRight(),
820
0
                             color, strokeOptions);
821
0
      return;
822
0
    }
823
0
    case NOTATION_UPDIAGONALARROW: {
824
0
      // Compute some parameters to draw the updiagonalarrow. The values below
825
0
      // are taken from MathJax's HTML-CSS output.
826
0
      Float W = rect.Width(); gfxFloat H = rect.Height();
827
0
      Float l = sqrt(W*W + H*H);
828
0
      Float f = Float(kArrowHeadSize) * strokeWidth / l;
829
0
      Float w = W * f; gfxFloat h = H * f;
830
0
831
0
      // Draw the arrow shaft
832
0
      aDrawTarget.StrokeLine(rect.BottomLeft(),
833
0
                             rect.TopRight() + Point(-.7*w, .7*h),
834
0
                             color, strokeOptions);
835
0
836
0
      // Draw the arrow head
837
0
      RefPtr<PathBuilder> builder = aDrawTarget.CreatePathBuilder();
838
0
      builder->MoveTo(rect.TopRight());
839
0
      builder->LineTo(rect.TopRight() + Point(-w -.4*h, std::max(-strokeWidth / 2.0, h - .4*w)));
840
0
      builder->LineTo(rect.TopRight() + Point(-.7*w, .7*h));
841
0
      builder->LineTo(rect.TopRight() + Point(std::min(strokeWidth / 2.0, -w + .4*h), h + .4*w));
842
0
      builder->Close();
843
0
      RefPtr<Path> path = builder->Finish();
844
0
      aDrawTarget.Fill(path, color);
845
0
      return;
846
0
    }
847
0
    case NOTATION_PHASORANGLE: {
848
0
      // Compute some parameters to draw the angled line,
849
0
      // that uses a slope of 2 (angle = tan^-1(2)).
850
0
      // H = w * tan(angle) = w * 2
851
0
      Float w = Float(kPhasorangleWidth) * strokeWidth;
852
0
      Float H = 2 * w;
853
0
854
0
      // Draw the angled line
855
0
      aDrawTarget.StrokeLine(rect.BottomLeft(),
856
0
                             rect.BottomLeft() + Point(w, -H),
857
0
                             color, strokeOptions);
858
0
      return;
859
0
    }
860
0
    default:
861
0
      MOZ_ASSERT_UNREACHABLE("This notation can not be drawn using "
862
0
                             "nsDisplayNotation");
863
0
  }
864
0
}
865
866
void
867
nsMathMLmencloseFrame::DisplayNotation(nsDisplayListBuilder* aBuilder,
868
                                       nsIFrame* aFrame, const nsRect& aRect,
869
                                       const nsDisplayListSet& aLists,
870
                                       nscoord aThickness,
871
                                       nsMencloseNotation aType)
872
0
{
873
0
  if (!aFrame->StyleVisibility()->IsVisible() || aRect.IsEmpty() ||
874
0
      aThickness <= 0)
875
0
    return;
876
0
877
0
  aLists.Content()->AppendToTop(
878
0
    MakeDisplayItem<nsDisplayNotation>(aBuilder, aFrame, aRect, aThickness, aType));
879
0
}