Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/layout/mathml/nsMathMLmmultiscriptsFrame.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 "nsMathMLmmultiscriptsFrame.h"
9
#include "nsPresContext.h"
10
#include <algorithm>
11
#include "gfxContext.h"
12
#include "gfxMathTable.h"
13
14
using mozilla::WritingMode;
15
16
//
17
// <mmultiscripts> -- attach prescripts and tensor indices to a base - implementation
18
// <msub> -- attach a subscript to a base - implementation
19
// <msubsup> -- attach a subscript-superscript pair to a base - implementation
20
// <msup> -- attach a superscript to a base - implementation
21
//
22
23
nsIFrame*
24
NS_NewMathMLmmultiscriptsFrame(nsIPresShell* aPresShell, ComputedStyle* aStyle)
25
0
{
26
0
  return new (aPresShell) nsMathMLmmultiscriptsFrame(aStyle);
27
0
}
28
29
NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmmultiscriptsFrame)
30
31
nsMathMLmmultiscriptsFrame::~nsMathMLmmultiscriptsFrame()
32
0
{
33
0
}
34
35
uint8_t
36
nsMathMLmmultiscriptsFrame::ScriptIncrement(nsIFrame* aFrame)
37
0
{
38
0
  if (!aFrame)
39
0
    return 0;
40
0
  if (mFrames.ContainsFrame(aFrame)) {
41
0
    if (mFrames.FirstChild() == aFrame ||
42
0
        aFrame->GetContent()->IsMathMLElement(nsGkAtoms::mprescripts_)) {
43
0
      return 0; // No script increment for base frames or prescript markers
44
0
    }
45
0
    return 1;
46
0
  }
47
0
  return 0; //not a child
48
0
}
49
50
NS_IMETHODIMP
51
nsMathMLmmultiscriptsFrame::TransmitAutomaticData()
52
0
{
53
0
  // if our base is an embellished operator, let its state bubble to us
54
0
  mPresentationData.baseFrame = mFrames.FirstChild();
55
0
  GetEmbellishDataFrom(mPresentationData.baseFrame, mEmbellishData);
56
0
57
0
  // The TeXbook (Ch 17. p.141) says the superscript inherits the compression
58
0
  // while the subscript is compressed. So here we collect subscripts and set
59
0
  // the compression flag in them.
60
0
61
0
  int32_t count = 0;
62
0
  bool isSubScript = !mContent->IsMathMLElement(nsGkAtoms::msup_);
63
0
64
0
  AutoTArray<nsIFrame*, 8> subScriptFrames;
65
0
  nsIFrame* childFrame = mFrames.FirstChild();
66
0
  while (childFrame) {
67
0
    if (childFrame->GetContent()->IsMathMLElement(nsGkAtoms::mprescripts_)) {
68
0
      // mprescripts frame
69
0
    } else if (0 == count) {
70
0
      // base frame
71
0
    } else {
72
0
      // super/subscript block
73
0
      if (isSubScript) {
74
0
        // subscript
75
0
        subScriptFrames.AppendElement(childFrame);
76
0
      } else {
77
0
        // superscript
78
0
      }
79
0
      PropagateFrameFlagFor(childFrame, NS_FRAME_MATHML_SCRIPT_DESCENDANT);
80
0
      isSubScript = !isSubScript;
81
0
    }
82
0
    count++;
83
0
    childFrame = childFrame->GetNextSibling();
84
0
  }
85
0
  for (int32_t i = subScriptFrames.Length() - 1; i >= 0; i--) {
86
0
    childFrame = subScriptFrames[i];
87
0
    PropagatePresentationDataFor(childFrame,
88
0
      NS_MATHML_COMPRESSED, NS_MATHML_COMPRESSED);
89
0
  }
90
0
91
0
  return NS_OK;
92
0
}
93
94
/* virtual */ nsresult
95
nsMathMLmmultiscriptsFrame::Place(DrawTarget*          aDrawTarget,
96
                                  bool                 aPlaceOrigin,
97
                                  ReflowOutput& aDesiredSize)
98
0
{
99
0
  nscoord subScriptShift = 0;
100
0
  nscoord supScriptShift = 0;
101
0
  float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
102
0
103
0
  // subscriptshift
104
0
  //
105
0
  // "Specifies the minimum amount to shift the baseline of subscript down; the
106
0
  // default is for the rendering agent to use its own positioning rules."
107
0
  //
108
0
  // values: length
109
0
  // default: automatic
110
0
  //
111
0
  // We use 0 as the default value so unitless values can be ignored.
112
0
  // As a minimum, negative values can be ignored.
113
0
  //
114
0
  nsAutoString value;
115
0
  if (!mContent->IsMathMLElement(nsGkAtoms::msup_)) {
116
0
    mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::subscriptshift_, value);
117
0
    if (!value.IsEmpty()) {
118
0
      ParseNumericValue(value, &subScriptShift, 0, PresContext(),
119
0
                        mComputedStyle, fontSizeInflation);
120
0
    }
121
0
  }
122
0
  // superscriptshift
123
0
  //
124
0
  // "Specifies the minimum amount to shift the baseline of superscript up; the
125
0
  // default is for the rendering agent to use its own positioning rules."
126
0
  //
127
0
  // values: length
128
0
  // default: automatic
129
0
  //
130
0
  // We use 0 as the default value so unitless values can be ignored.
131
0
  // As a minimum, negative values can be ignored.
132
0
  //
133
0
  if (!mContent->IsMathMLElement(nsGkAtoms::msub_)) {
134
0
    mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::superscriptshift_, value);
135
0
    if (!value.IsEmpty()) {
136
0
      ParseNumericValue(value, &supScriptShift, 0, PresContext(),
137
0
                        mComputedStyle, fontSizeInflation);
138
0
    }
139
0
  }
140
0
  return PlaceMultiScript(PresContext(), aDrawTarget, aPlaceOrigin,
141
0
                          aDesiredSize, this, subScriptShift, supScriptShift,
142
0
                          fontSizeInflation);
143
0
}
144
145
// exported routine that both munderover and mmultiscripts share.
146
// munderover uses this when movablelimits is set.
147
nsresult
148
nsMathMLmmultiscriptsFrame::PlaceMultiScript(nsPresContext*  aPresContext,
149
                                        DrawTarget*          aDrawTarget,
150
                                        bool                 aPlaceOrigin,
151
                                        ReflowOutput& aDesiredSize,
152
                                        nsMathMLContainerFrame* aFrame,
153
                                        nscoord              aUserSubScriptShift,
154
                                        nscoord              aUserSupScriptShift,
155
                                        float                aFontSizeInflation)
156
0
{
157
0
  nsAtom* tag = aFrame->GetContent()->NodeInfo()->NameAtom();
158
0
159
0
  // This function deals with both munderover etc. as well as msubsup etc.
160
0
  // As the former behaves identically to the later, we treat it as such
161
0
  // to avoid additional checks later.
162
0
  if (aFrame->GetContent()->IsMathMLElement(nsGkAtoms::mover_))
163
0
    tag = nsGkAtoms::msup_;
164
0
  else if (aFrame->GetContent()->IsMathMLElement(nsGkAtoms::munder_))
165
0
    tag = nsGkAtoms::msub_;
166
0
  else if (aFrame->GetContent()->IsMathMLElement(nsGkAtoms::munderover_))
167
0
    tag = nsGkAtoms::msubsup_;
168
0
169
0
  nsBoundingMetrics bmFrame;
170
0
171
0
  nscoord minShiftFromXHeight, subDrop, supDrop;
172
0
173
0
  ////////////////////////////////////////
174
0
  // Initialize super/sub shifts that
175
0
  // depend only on the current font
176
0
  ////////////////////////////////////////
177
0
178
0
  nsIFrame* baseFrame = aFrame->PrincipalChildList().FirstChild();
179
0
180
0
  if (!baseFrame) {
181
0
    if (tag == nsGkAtoms::mmultiscripts_)
182
0
      aFrame->ReportErrorToConsole("NoBase");
183
0
    else
184
0
      aFrame->ReportChildCountError();
185
0
    return aFrame->ReflowError(aDrawTarget, aDesiredSize);
186
0
  }
187
0
188
0
  // get x-height (an ex)
189
0
  const nsStyleFont* font = aFrame->StyleFont();
190
0
  RefPtr<nsFontMetrics> fm =
191
0
    nsLayoutUtils::GetFontMetricsForFrame(baseFrame, aFontSizeInflation);
192
0
193
0
  nscoord xHeight = fm->XHeight();
194
0
195
0
  nscoord oneDevPixel = fm->AppUnitsPerDevPixel();
196
0
  gfxFont* mathFont = fm->GetThebesFontGroup()->GetFirstMathFont();
197
0
  // scriptspace from TeX for extra spacing after sup/subscript
198
0
  nscoord scriptSpace;
199
0
  if (mathFont) {
200
0
    scriptSpace = mathFont->MathTable()->
201
0
      Constant(gfxMathTable::SpaceAfterScript, oneDevPixel);
202
0
  } else {
203
0
    // (0.5pt in plain TeX)
204
0
    scriptSpace = nsPresContext::CSSPointsToAppUnits(0.5f);
205
0
  }
206
0
207
0
  // Try and read sub and sup drops from the MATH table.
208
0
  if (mathFont) {
209
0
    subDrop = mathFont->MathTable()->
210
0
      Constant(gfxMathTable::SubscriptBaselineDropMin, oneDevPixel);
211
0
    supDrop = mathFont->MathTable()->
212
0
      Constant(gfxMathTable::SuperscriptBaselineDropMax, oneDevPixel);
213
0
  }
214
0
215
0
  // force the scriptSpace to be at least 1 pixel
216
0
  nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
217
0
  scriptSpace = std::max(onePixel, scriptSpace);
218
0
219
0
  /////////////////////////////////////
220
0
  // first the shift for the subscript
221
0
222
0
  nscoord subScriptShift;
223
0
  if (mathFont) {
224
0
    // Try and get the sub script shift from the MATH table. Note that contrary
225
0
    // to TeX we only have one parameter.
226
0
    subScriptShift = mathFont->MathTable()->
227
0
      Constant(gfxMathTable::SubscriptShiftDown, oneDevPixel);
228
0
  } else {
229
0
    // subScriptShift{1,2}
230
0
    // = minimum amount to shift the subscript down
231
0
    // = sub{1,2} in TeXbook
232
0
    // subScriptShift1 = subscriptshift attribute * x-height
233
0
    nscoord subScriptShift1, subScriptShift2;
234
0
    // Get subScriptShift{1,2} default from font
235
0
    GetSubScriptShifts (fm, subScriptShift1, subScriptShift2);
236
0
    if (tag == nsGkAtoms::msub_) {
237
0
      subScriptShift = subScriptShift1;
238
0
    } else {
239
0
      subScriptShift = std::max(subScriptShift1, subScriptShift2);
240
0
    }
241
0
  }
242
0
243
0
  if (0 < aUserSubScriptShift) {
244
0
    // the user has set the subscriptshift attribute
245
0
    subScriptShift = std::max(subScriptShift, aUserSubScriptShift);
246
0
  }
247
0
248
0
  /////////////////////////////////////
249
0
  // next the shift for the superscript
250
0
251
0
  nscoord supScriptShift;
252
0
  nsPresentationData presentationData;
253
0
  aFrame->GetPresentationData(presentationData);
254
0
  if (mathFont) {
255
0
    // Try and get the super script shift from the MATH table. Note that
256
0
    // contrary to TeX we only have two parameters.
257
0
    supScriptShift = mathFont->
258
0
      MathTable()->Constant(NS_MATHML_IS_COMPRESSED(presentationData.flags) ?
259
0
                            gfxMathTable::SuperscriptShiftUpCramped :
260
0
                            gfxMathTable::SuperscriptShiftUp,
261
0
                            oneDevPixel);
262
0
  } else {
263
0
    // supScriptShift{1,2,3}
264
0
    // = minimum amount to shift the supscript up
265
0
    // = sup{1,2,3} in TeX
266
0
    // supScriptShift1 = superscriptshift attribute * x-height
267
0
    // Note that there are THREE values for supscript shifts depending
268
0
    // on the current style
269
0
    nscoord supScriptShift1, supScriptShift2, supScriptShift3;
270
0
    // Set supScriptShift{1,2,3} default from font
271
0
    GetSupScriptShifts (fm, supScriptShift1, supScriptShift2, supScriptShift3);
272
0
273
0
    // get sup script shift depending on current script level and display style
274
0
    // Rule 18c, App. G, TeXbook
275
0
    if (font->mScriptLevel == 0 &&
276
0
        font->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK &&
277
0
        !NS_MATHML_IS_COMPRESSED(presentationData.flags)) {
278
0
      // Style D in TeXbook
279
0
      supScriptShift = supScriptShift1;
280
0
    } else if (NS_MATHML_IS_COMPRESSED(presentationData.flags)) {
281
0
      // Style C' in TeXbook = D',T',S',SS'
282
0
      supScriptShift = supScriptShift3;
283
0
    } else {
284
0
      // everything else = T,S,SS
285
0
      supScriptShift = supScriptShift2;
286
0
    }
287
0
  }
288
0
289
0
  if (0 < aUserSupScriptShift) {
290
0
    // the user has set the supscriptshift attribute
291
0
    supScriptShift = std::max(supScriptShift, aUserSupScriptShift);
292
0
  }
293
0
294
0
  ////////////////////////////////////
295
0
  // Get the children's sizes
296
0
  ////////////////////////////////////
297
0
298
0
  const WritingMode wm(aDesiredSize.GetWritingMode());
299
0
  nscoord width = 0, prescriptsWidth = 0, rightBearing = 0;
300
0
  nscoord minSubScriptShift = 0, minSupScriptShift = 0;
301
0
  nscoord trySubScriptShift = subScriptShift;
302
0
  nscoord trySupScriptShift = supScriptShift;
303
0
  nscoord maxSubScriptShift = subScriptShift;
304
0
  nscoord maxSupScriptShift = supScriptShift;
305
0
  ReflowOutput baseSize(wm);
306
0
  ReflowOutput subScriptSize(wm);
307
0
  ReflowOutput supScriptSize(wm);
308
0
  ReflowOutput multiSubSize(wm), multiSupSize(wm);
309
0
  baseFrame = nullptr;
310
0
  nsIFrame* subScriptFrame = nullptr;
311
0
  nsIFrame* supScriptFrame = nullptr;
312
0
  nsIFrame* prescriptsFrame = nullptr; // frame of <mprescripts/>, if there.
313
0
314
0
  bool firstPrescriptsPair = false;
315
0
  nsBoundingMetrics bmBase, bmSubScript, bmSupScript, bmMultiSub, bmMultiSup;
316
0
  multiSubSize.SetBlockStartAscent(-0x7FFFFFFF);
317
0
  multiSupSize.SetBlockStartAscent(-0x7FFFFFFF);
318
0
  bmMultiSub.ascent = bmMultiSup.ascent = -0x7FFFFFFF;
319
0
  bmMultiSub.descent = bmMultiSup.descent = -0x7FFFFFFF;
320
0
  nscoord italicCorrection = 0;
321
0
322
0
  nsBoundingMetrics boundingMetrics;
323
0
  boundingMetrics.width = 0;
324
0
  boundingMetrics.ascent = boundingMetrics.descent = -0x7FFFFFFF;
325
0
  aDesiredSize.Width() = aDesiredSize.Height() = 0;
326
0
327
0
  int32_t count = 0;
328
0
  bool foundNoneTag = false;
329
0
330
0
  // Boolean to determine whether the current child is a subscript.
331
0
  // Note that only msup starts with a superscript.
332
0
  bool isSubScript = (tag != nsGkAtoms::msup_);
333
0
334
0
  nsIFrame* childFrame = aFrame->PrincipalChildList().FirstChild();
335
0
  while (childFrame) {
336
0
    if (childFrame->GetContent()->IsMathMLElement(nsGkAtoms::mprescripts_)) {
337
0
      if (tag != nsGkAtoms::mmultiscripts_) {
338
0
        if (aPlaceOrigin) {
339
0
          aFrame->ReportInvalidChildError(nsGkAtoms::mprescripts_);
340
0
        }
341
0
        return aFrame->ReflowError(aDrawTarget, aDesiredSize);
342
0
      }
343
0
      if (prescriptsFrame) {
344
0
        // duplicate <mprescripts/> found
345
0
        // report an error, encourage people to get their markups in order
346
0
        if (aPlaceOrigin) {
347
0
          aFrame->ReportErrorToConsole("DuplicateMprescripts");
348
0
        }
349
0
        return aFrame->ReflowError(aDrawTarget, aDesiredSize);
350
0
      }
351
0
      if (!isSubScript) {
352
0
        if (aPlaceOrigin) {
353
0
          aFrame->ReportErrorToConsole("SubSupMismatch");
354
0
        }
355
0
        return aFrame->ReflowError(aDrawTarget, aDesiredSize);
356
0
      }
357
0
358
0
      prescriptsFrame = childFrame;
359
0
      firstPrescriptsPair = true;
360
0
    } else if (0 == count) {
361
0
      // base
362
0
363
0
      if (childFrame->GetContent()->IsMathMLElement(nsGkAtoms::none)) {
364
0
        if (tag == nsGkAtoms::mmultiscripts_) {
365
0
          if (aPlaceOrigin) {
366
0
            aFrame->ReportErrorToConsole("NoBase");
367
0
          }
368
0
          return aFrame->ReflowError(aDrawTarget, aDesiredSize);
369
0
        } else {
370
0
          //A different error message is triggered later for the other tags
371
0
          foundNoneTag = true;
372
0
        }
373
0
      }
374
0
      baseFrame = childFrame;
375
0
      GetReflowAndBoundingMetricsFor(baseFrame, baseSize, bmBase);
376
0
377
0
      if (tag != nsGkAtoms::msub_) {
378
0
        // Apply italics correction if there is the potential for a
379
0
        // postsupscript.
380
0
        GetItalicCorrection(bmBase, italicCorrection);
381
0
        // If italics correction is applied, we always add "a little to spare"
382
0
        // (see TeXbook Ch.11, p.64), as we estimate the italic creation
383
0
        // ourselves and it isn't the same as TeX.
384
0
        italicCorrection += onePixel;
385
0
      }
386
0
387
0
      // we update boundingMetrics.{ascent,descent} with that
388
0
      // of the baseFrame only after processing all the sup/sub pairs
389
0
      boundingMetrics.width = bmBase.width;
390
0
      boundingMetrics.rightBearing = bmBase.rightBearing;
391
0
      boundingMetrics.leftBearing = bmBase.leftBearing; // until overwritten
392
0
    } else {
393
0
      // super/subscript block
394
0
      if (childFrame->GetContent()->IsMathMLElement(nsGkAtoms::none)) {
395
0
        foundNoneTag = true;
396
0
      }
397
0
398
0
      if (isSubScript) {
399
0
        // subscript
400
0
        subScriptFrame = childFrame;
401
0
        GetReflowAndBoundingMetricsFor(subScriptFrame, subScriptSize, bmSubScript);
402
0
        if (!mathFont) {
403
0
          // get the subdrop from the subscript font
404
0
          GetSubDropFromChild (subScriptFrame, subDrop, aFontSizeInflation);
405
0
        }
406
0
407
0
        // parameter v, Rule 18a, App. G, TeXbook
408
0
        minSubScriptShift = bmBase.descent + subDrop;
409
0
        trySubScriptShift = std::max(minSubScriptShift,subScriptShift);
410
0
        multiSubSize.SetBlockStartAscent(
411
0
           std::max(multiSubSize.BlockStartAscent(),
412
0
                    subScriptSize.BlockStartAscent()));
413
0
        bmMultiSub.ascent = std::max(bmMultiSub.ascent, bmSubScript.ascent);
414
0
        bmMultiSub.descent = std::max(bmMultiSub.descent, bmSubScript.descent);
415
0
        multiSubSize.Height() =
416
0
          std::max(multiSubSize.Height(),
417
0
                   subScriptSize.Height() - subScriptSize.BlockStartAscent());
418
0
        if (bmSubScript.width)
419
0
          width = bmSubScript.width + scriptSpace;
420
0
        rightBearing = bmSubScript.rightBearing;
421
0
422
0
        if (tag == nsGkAtoms::msub_) {
423
0
          boundingMetrics.rightBearing = boundingMetrics.width + rightBearing;
424
0
          boundingMetrics.width += width;
425
0
426
0
          nscoord subscriptTopMax;
427
0
          if (mathFont) {
428
0
            subscriptTopMax =
429
0
              mathFont->MathTable()->Constant(gfxMathTable::SubscriptTopMax,
430
0
                                              oneDevPixel);
431
0
          } else {
432
0
            // get min subscript shift limit from x-height
433
0
            // = h(x) - 4/5 * sigma_5, Rule 18b, App. G, TeXbook
434
0
            subscriptTopMax = NSToCoordRound((4.0f/5.0f) * xHeight);
435
0
          }
436
0
          nscoord minShiftFromXHeight = bmSubScript.ascent - subscriptTopMax;
437
0
          maxSubScriptShift = std::max(trySubScriptShift,minShiftFromXHeight);
438
0
439
0
          maxSubScriptShift = std::max(maxSubScriptShift, trySubScriptShift);
440
0
          trySubScriptShift = subScriptShift;
441
0
        }
442
0
      } else {
443
0
        // supscript
444
0
        supScriptFrame = childFrame;
445
0
        GetReflowAndBoundingMetricsFor(supScriptFrame, supScriptSize, bmSupScript);
446
0
        if (!mathFont) {
447
0
          // get the supdrop from the supscript font
448
0
          GetSupDropFromChild (supScriptFrame, supDrop, aFontSizeInflation);
449
0
        }
450
0
        // parameter u, Rule 18a, App. G, TeXbook
451
0
        minSupScriptShift = bmBase.ascent - supDrop;
452
0
        nscoord superscriptBottomMin;
453
0
        if (mathFont) {
454
0
          superscriptBottomMin =
455
0
            mathFont->MathTable()->Constant(gfxMathTable::SuperscriptBottomMin,
456
0
                                            oneDevPixel);
457
0
        } else {
458
0
          // get min supscript shift limit from x-height
459
0
          // = d(x) + 1/4 * sigma_5, Rule 18c, App. G, TeXbook
460
0
          superscriptBottomMin = NSToCoordRound((1.0f / 4.0f) * xHeight);
461
0
        }
462
0
        minShiftFromXHeight = bmSupScript.descent + superscriptBottomMin;
463
0
        trySupScriptShift = std::max(minSupScriptShift,
464
0
                                     std::max(minShiftFromXHeight,
465
0
                                              supScriptShift));
466
0
        multiSupSize.SetBlockStartAscent(
467
0
          std::max(multiSupSize.BlockStartAscent(),
468
0
                   supScriptSize.BlockStartAscent()));
469
0
        bmMultiSup.ascent = std::max(bmMultiSup.ascent, bmSupScript.ascent);
470
0
        bmMultiSup.descent = std::max(bmMultiSup.descent, bmSupScript.descent);
471
0
        multiSupSize.Height() =
472
0
          std::max(multiSupSize.Height(),
473
0
                   supScriptSize.Height() - supScriptSize.BlockStartAscent());
474
0
475
0
        if (bmSupScript.width)
476
0
          width = std::max(width, bmSupScript.width + scriptSpace);
477
0
478
0
        if (!prescriptsFrame) { // we are still looping over base & postscripts
479
0
          rightBearing = std::max(rightBearing,
480
0
                                  italicCorrection + bmSupScript.rightBearing);
481
0
          boundingMetrics.rightBearing = boundingMetrics.width + rightBearing;
482
0
          boundingMetrics.width += width;
483
0
        } else {
484
0
          prescriptsWidth += width;
485
0
          if (firstPrescriptsPair) {
486
0
            firstPrescriptsPair = false;
487
0
            boundingMetrics.leftBearing =
488
0
              std::min(bmSubScript.leftBearing, bmSupScript.leftBearing);
489
0
          }
490
0
        }
491
0
        width = rightBearing = 0;
492
0
493
0
        // negotiate between the various shifts so that
494
0
        // there is enough gap between the sup and subscripts
495
0
        // Rule 18e, App. G, TeXbook
496
0
        if (tag == nsGkAtoms::mmultiscripts_ ||
497
0
            tag == nsGkAtoms::msubsup_) {
498
0
          nscoord subSuperscriptGapMin;
499
0
          if (mathFont) {
500
0
            subSuperscriptGapMin = mathFont->MathTable()->
501
0
              Constant(gfxMathTable::SubSuperscriptGapMin, oneDevPixel);
502
0
          } else {
503
0
            nscoord ruleSize;
504
0
            GetRuleThickness(aDrawTarget, fm, ruleSize);
505
0
            subSuperscriptGapMin = 4 * ruleSize;
506
0
          }
507
0
          nscoord gap =
508
0
            (trySupScriptShift - bmSupScript.descent) -
509
0
            (bmSubScript.ascent - trySubScriptShift);
510
0
          if (gap < subSuperscriptGapMin) {
511
0
            // adjust trySubScriptShift to get a gap of subSuperscriptGapMin
512
0
            trySubScriptShift += subSuperscriptGapMin - gap;
513
0
          }
514
0
515
0
          // next we want to ensure that the bottom of the superscript
516
0
          // will be > superscriptBottomMaxWithSubscript
517
0
          nscoord superscriptBottomMaxWithSubscript;
518
0
          if (mathFont) {
519
0
            superscriptBottomMaxWithSubscript = mathFont->MathTable()->
520
0
              Constant(gfxMathTable::SuperscriptBottomMaxWithSubscript,
521
0
                       oneDevPixel);
522
0
          } else {
523
0
            superscriptBottomMaxWithSubscript =
524
0
              NSToCoordRound((4.0f / 5.0f) * xHeight);
525
0
          }
526
0
          gap = superscriptBottomMaxWithSubscript -
527
0
            (trySupScriptShift - bmSupScript.descent);
528
0
          if (gap > 0) {
529
0
            trySupScriptShift += gap;
530
0
            trySubScriptShift -= gap;
531
0
          }
532
0
        }
533
0
534
0
        maxSubScriptShift = std::max(maxSubScriptShift, trySubScriptShift);
535
0
        maxSupScriptShift = std::max(maxSupScriptShift, trySupScriptShift);
536
0
537
0
        trySubScriptShift = subScriptShift;
538
0
        trySupScriptShift = supScriptShift;
539
0
      }
540
0
541
0
      isSubScript = !isSubScript;
542
0
    }
543
0
    count++;
544
0
    childFrame = childFrame->GetNextSibling();
545
0
  }
546
0
547
0
  //NoBase error may also have been reported above
548
0
  if ((count != 2 && (tag == nsGkAtoms::msup_ || tag == nsGkAtoms::msub_)) ||
549
0
      (count != 3 && tag == nsGkAtoms::msubsup_) || !baseFrame ||
550
0
      (foundNoneTag && tag != nsGkAtoms::mmultiscripts_) ||
551
0
      (!isSubScript && tag == nsGkAtoms::mmultiscripts_)) {
552
0
    // report an error, encourage people to get their markups in order
553
0
    if (aPlaceOrigin) {
554
0
      if ((count != 2 && (tag == nsGkAtoms::msup_ ||
555
0
          tag == nsGkAtoms::msub_)) ||
556
0
          (count != 3 && tag == nsGkAtoms::msubsup_ )) {
557
0
        aFrame->ReportChildCountError();
558
0
      } else if (foundNoneTag && tag != nsGkAtoms::mmultiscripts_) {
559
0
        aFrame->ReportInvalidChildError(nsGkAtoms::none);
560
0
      } else if (!baseFrame) {
561
0
        aFrame->ReportErrorToConsole("NoBase");
562
0
      } else {
563
0
        aFrame->ReportErrorToConsole("SubSupMismatch");
564
0
      }
565
0
    }
566
0
    return aFrame->ReflowError(aDrawTarget, aDesiredSize);
567
0
  }
568
0
569
0
  // we left out the width of prescripts, so ...
570
0
  boundingMetrics.rightBearing += prescriptsWidth;
571
0
  boundingMetrics.width += prescriptsWidth;
572
0
573
0
  // Zero out the shifts in where a frame isn't present to avoid the potential
574
0
  // for overflow.
575
0
  if (!subScriptFrame)
576
0
    maxSubScriptShift = 0;
577
0
  if (!supScriptFrame)
578
0
    maxSupScriptShift = 0;
579
0
580
0
  // we left out the base during our bounding box updates, so ...
581
0
  if (tag == nsGkAtoms::msub_) {
582
0
    boundingMetrics.ascent = std::max(bmBase.ascent,
583
0
                                      bmMultiSub.ascent - maxSubScriptShift);
584
0
  } else {
585
0
    boundingMetrics.ascent =
586
0
      std::max(bmBase.ascent, (bmMultiSup.ascent + maxSupScriptShift));
587
0
  }
588
0
  if (tag == nsGkAtoms::msup_) {
589
0
    boundingMetrics.descent = std::max(bmBase.descent,
590
0
                                       bmMultiSup.descent - maxSupScriptShift);
591
0
  } else {
592
0
    boundingMetrics.descent =
593
0
      std::max(bmBase.descent, (bmMultiSub.descent + maxSubScriptShift));
594
0
  }
595
0
  aFrame->SetBoundingMetrics(boundingMetrics);
596
0
597
0
  // get the reflow metrics ...
598
0
  aDesiredSize.SetBlockStartAscent(
599
0
    std::max(baseSize.BlockStartAscent(),
600
0
             std::max(multiSubSize.BlockStartAscent() - maxSubScriptShift,
601
0
                      multiSupSize.BlockStartAscent() + maxSupScriptShift)));
602
0
  aDesiredSize.Height() = aDesiredSize.BlockStartAscent() +
603
0
    std::max(baseSize.Height() - baseSize.BlockStartAscent(),
604
0
             std::max(multiSubSize.Height() + maxSubScriptShift,
605
0
                      multiSupSize.Height() - maxSupScriptShift));
606
0
  aDesiredSize.Width() = boundingMetrics.width;
607
0
  aDesiredSize.mBoundingMetrics = boundingMetrics;
608
0
609
0
  aFrame->SetReference(nsPoint(0, aDesiredSize.BlockStartAscent()));
610
0
611
0
  //////////////////
612
0
  // Place Children
613
0
614
0
  // Place prescripts, followed by base, and then postscripts.
615
0
  // The list of frames is in the order: {base} {postscripts} {prescripts}
616
0
  // We go over the list in a circular manner, starting at <prescripts/>
617
0
618
0
  if (aPlaceOrigin) {
619
0
    nscoord dx = 0, dy = 0;
620
0
621
0
    // With msub and msup there is only one element and
622
0
    // subscriptFrame/supScriptFrame have already been set above where
623
0
    // relevant.  In these cases we skip to the reflow part.
624
0
    if (tag == nsGkAtoms::msub_ || tag == nsGkAtoms::msup_)
625
0
      count = 1;
626
0
    else
627
0
      count = 0;
628
0
    childFrame = prescriptsFrame;
629
0
    bool isPreScript = true;
630
0
    do {
631
0
      if (!childFrame) { // end of prescripts,
632
0
        isPreScript = false;
633
0
        // place the base ...
634
0
        childFrame = baseFrame;
635
0
        dy = aDesiredSize.BlockStartAscent() - baseSize.BlockStartAscent();
636
0
        FinishReflowChild (baseFrame, aPresContext, baseSize, nullptr,
637
0
                           aFrame->MirrorIfRTL(aDesiredSize.Width(),
638
0
                                               baseSize.Width(),
639
0
                                               dx),
640
0
                           dy, 0);
641
0
        dx += bmBase.width;
642
0
      } else if (prescriptsFrame == childFrame) {
643
0
        // Clear reflow flags of prescripts frame.
644
0
        prescriptsFrame->DidReflow(aPresContext, nullptr);
645
0
      } else {
646
0
        // process each sup/sub pair
647
0
        if (0 == count) {
648
0
          subScriptFrame = childFrame;
649
0
          count = 1;
650
0
        } else if (1 == count) {
651
0
          if (tag != nsGkAtoms::msub_)
652
0
            supScriptFrame = childFrame;
653
0
          count = 0;
654
0
655
0
          // get the ascent/descent of sup/subscripts stored in their rects
656
0
          // rect.x = descent, rect.y = ascent
657
0
          if (subScriptFrame)
658
0
            GetReflowAndBoundingMetricsFor(subScriptFrame, subScriptSize, bmSubScript);
659
0
          if (supScriptFrame)
660
0
            GetReflowAndBoundingMetricsFor(supScriptFrame, supScriptSize, bmSupScript);
661
0
662
0
          width = std::max(subScriptSize.Width(), supScriptSize.Width());
663
0
664
0
          if (subScriptFrame) {
665
0
            nscoord x = dx;
666
0
            // prescripts should be right aligned
667
0
            // https://bugzilla.mozilla.org/show_bug.cgi?id=928675
668
0
            if (isPreScript)
669
0
              x += width - subScriptSize.Width();
670
0
            dy = aDesiredSize.BlockStartAscent() - subScriptSize.BlockStartAscent() +
671
0
              maxSubScriptShift;
672
0
            FinishReflowChild (subScriptFrame, aPresContext, subScriptSize,
673
0
                               nullptr,
674
0
                               aFrame->MirrorIfRTL(aDesiredSize.Width(),
675
0
                                                   subScriptSize.Width(),
676
0
                                                   x),
677
0
                               dy, 0);
678
0
          }
679
0
680
0
          if (supScriptFrame) {
681
0
            nscoord x = dx;
682
0
            if (isPreScript) {
683
0
              x += width - supScriptSize.Width();
684
0
            } else {
685
0
              // post superscripts are shifted by the italic correction value
686
0
              x += italicCorrection;
687
0
            }
688
0
            dy = aDesiredSize.BlockStartAscent() - supScriptSize.BlockStartAscent() -
689
0
              maxSupScriptShift;
690
0
            FinishReflowChild (supScriptFrame, aPresContext, supScriptSize,
691
0
                               nullptr,
692
0
                               aFrame->MirrorIfRTL(aDesiredSize.Width(),
693
0
                                                   supScriptSize.Width(),
694
0
                                                   x),
695
0
                               dy, 0);
696
0
          }
697
0
          dx += width + scriptSpace;
698
0
        }
699
0
      }
700
0
      childFrame = childFrame->GetNextSibling();
701
0
    } while (prescriptsFrame != childFrame);
702
0
  }
703
0
704
0
  return NS_OK;
705
0
}