Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/layout/mathml/nsMathMLmunderoverFrame.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 "nsMathMLmunderoverFrame.h"
8
#include "nsPresContext.h"
9
#include "nsMathMLmmultiscriptsFrame.h"
10
#include "nsMathMLElement.h"
11
#include <algorithm>
12
#include "gfxContext.h"
13
#include "gfxMathTable.h"
14
15
//
16
// <munderover> -- attach an underscript-overscript pair to a base - implementation
17
// <mover> -- attach an overscript to a base - implementation
18
// <munder> -- attach an underscript to a base - implementation
19
//
20
21
nsIFrame*
22
NS_NewMathMLmunderoverFrame(nsIPresShell* aPresShell, ComputedStyle* aStyle)
23
0
{
24
0
  return new (aPresShell) nsMathMLmunderoverFrame(aStyle);
25
0
}
26
27
NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmunderoverFrame)
28
29
nsMathMLmunderoverFrame::~nsMathMLmunderoverFrame()
30
0
{
31
0
}
32
33
nsresult
34
nsMathMLmunderoverFrame::AttributeChanged(int32_t         aNameSpaceID,
35
                                          nsAtom*        aAttribute,
36
                                          int32_t         aModType)
37
0
{
38
0
  if (nsGkAtoms::accent_ == aAttribute ||
39
0
      nsGkAtoms::accentunder_ == aAttribute) {
40
0
    // When we have automatic data to update within ourselves, we ask our
41
0
    // parent to re-layout its children
42
0
    return ReLayoutChildren(GetParent());
43
0
  }
44
0
45
0
  return nsMathMLContainerFrame::
46
0
         AttributeChanged(aNameSpaceID, aAttribute, aModType);
47
0
}
48
49
NS_IMETHODIMP
50
nsMathMLmunderoverFrame::UpdatePresentationData(uint32_t        aFlagsValues,
51
                                                uint32_t        aFlagsToUpdate)
52
0
{
53
0
  nsMathMLContainerFrame::UpdatePresentationData(aFlagsValues, aFlagsToUpdate);
54
0
  // disable the stretch-all flag if we are going to act like a subscript-superscript pair
55
0
  if (NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishData.flags) &&
56
0
      StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_INLINE) {
57
0
    mPresentationData.flags &= ~NS_MATHML_STRETCH_ALL_CHILDREN_HORIZONTALLY;
58
0
  }
59
0
  else {
60
0
    mPresentationData.flags |= NS_MATHML_STRETCH_ALL_CHILDREN_HORIZONTALLY;
61
0
  }
62
0
  return NS_OK;
63
0
}
64
65
NS_IMETHODIMP
66
nsMathMLmunderoverFrame::InheritAutomaticData(nsIFrame* aParent)
67
0
{
68
0
  // let the base class get the default from our parent
69
0
  nsMathMLContainerFrame::InheritAutomaticData(aParent);
70
0
71
0
  mPresentationData.flags |= NS_MATHML_STRETCH_ALL_CHILDREN_HORIZONTALLY;
72
0
73
0
  return NS_OK;
74
0
}
75
76
void
77
nsMathMLmunderoverFrame::DestroyFrom(nsIFrame* aDestroyRoot, PostDestroyData& aPostDestroyData)
78
0
{
79
0
  if (!mPostReflowIncrementScriptLevelCommands.IsEmpty()) {
80
0
    PresShell()->CancelReflowCallback(this);
81
0
  }
82
0
  nsMathMLContainerFrame::DestroyFrom(aDestroyRoot, aPostDestroyData);
83
0
}
84
85
uint8_t
86
nsMathMLmunderoverFrame::ScriptIncrement(nsIFrame* aFrame)
87
0
{
88
0
  nsIFrame* child = mFrames.FirstChild();
89
0
  if (!aFrame || aFrame == child) {
90
0
    return 0;
91
0
  }
92
0
  child = child->GetNextSibling();
93
0
  if (aFrame == child) {
94
0
    if (mContent->IsMathMLElement(nsGkAtoms::mover_)) {
95
0
      return mIncrementOver ? 1 : 0;
96
0
    }
97
0
    return mIncrementUnder ? 1 : 0;
98
0
  }
99
0
  if (child && aFrame == child->GetNextSibling()) {
100
0
    // must be a over frame of munderover
101
0
    return mIncrementOver ? 1 : 0;
102
0
  }
103
0
  return 0;  // frame not found
104
0
}
105
106
void
107
nsMathMLmunderoverFrame::SetIncrementScriptLevel(uint32_t aChildIndex,
108
                                                 bool aIncrement)
109
0
{
110
0
  nsIFrame* child = PrincipalChildList().FrameAt(aChildIndex);
111
0
  if (!child || !child->GetContent()->IsMathMLElement() ||
112
0
      child->GetContent()->GetPrimaryFrame() != child) {
113
0
    return;
114
0
  }
115
0
116
0
  auto element = static_cast<nsMathMLElement*>(child->GetContent());
117
0
  if (element->GetIncrementScriptLevel() == aIncrement) {
118
0
    return;
119
0
  }
120
0
121
0
  if (mPostReflowIncrementScriptLevelCommands.IsEmpty()) {
122
0
    PresShell()->PostReflowCallback(this);
123
0
  }
124
0
125
0
  mPostReflowIncrementScriptLevelCommands.AppendElement(
126
0
      SetIncrementScriptLevelCommand { aChildIndex, aIncrement });
127
0
}
128
129
bool
130
nsMathMLmunderoverFrame::ReflowFinished()
131
0
{
132
0
  SetPendingPostReflowIncrementScriptLevel();
133
0
  return true;
134
0
}
135
136
void
137
nsMathMLmunderoverFrame::ReflowCallbackCanceled()
138
0
{
139
0
  // Do nothing, at this point our work will just be useless.
140
0
  mPostReflowIncrementScriptLevelCommands.Clear();
141
0
}
142
143
void
144
nsMathMLmunderoverFrame::SetPendingPostReflowIncrementScriptLevel()
145
0
{
146
0
  MOZ_ASSERT(!mPostReflowIncrementScriptLevelCommands.IsEmpty());
147
0
148
0
  nsTArray<SetIncrementScriptLevelCommand> commands;
149
0
  commands.SwapElements(mPostReflowIncrementScriptLevelCommands);
150
0
151
0
  for (const auto& command : commands) {
152
0
    nsIFrame* child = PrincipalChildList().FrameAt(command.mChildIndex);
153
0
    if (!child || !child->GetContent()->IsMathMLElement()) {
154
0
      continue;
155
0
    }
156
0
157
0
    auto element = static_cast<nsMathMLElement*>(child->GetContent());
158
0
    element->SetIncrementScriptLevel(command.mDoIncrement, true);
159
0
  }
160
0
}
161
162
NS_IMETHODIMP
163
nsMathMLmunderoverFrame::TransmitAutomaticData()
164
0
{
165
0
  // At this stage, all our children are in sync and we can fully
166
0
  // resolve our own mEmbellishData struct
167
0
  //---------------------------------------------------------------------
168
0
169
0
  /*
170
0
  The REC says:
171
0
172
0
  As regards munder (respectively mover) :
173
0
  The default value of accentunder is false, unless underscript
174
0
  is an <mo> element or an embellished operator.  If underscript is
175
0
  an <mo> element, the value of its accent attribute is used as the
176
0
  default value of accentunder. If underscript is an embellished
177
0
  operator, the accent attribute of the <mo> element at its
178
0
  core is used as the default value. As with all attributes, an
179
0
  explicitly given value overrides the default.
180
0
181
0
XXX The winner is the outermost setting in conflicting settings like these:
182
0
<munder accentunder='true'>
183
0
  <mi>...</mi>
184
0
  <mo accentunder='false'> ... </mo>
185
0
</munder>
186
0
187
0
  As regards munderover:
188
0
  The accent and accentunder attributes have the same effect as
189
0
  the attributes with the same names on <mover>  and <munder>,
190
0
  respectively. Their default values are also computed in the
191
0
  same manner as described for those elements, with the default
192
0
  value of accent depending on overscript and the default value
193
0
  of accentunder depending on underscript.
194
0
  */
195
0
196
0
  nsIFrame* overscriptFrame = nullptr;
197
0
  nsIFrame* underscriptFrame = nullptr;
198
0
  nsIFrame* baseFrame = mFrames.FirstChild();
199
0
200
0
  if (baseFrame) {
201
0
    if (mContent->IsAnyOfMathMLElements(nsGkAtoms::munder_,
202
0
                                        nsGkAtoms::munderover_)) {
203
0
      underscriptFrame = baseFrame->GetNextSibling();
204
0
    } else {
205
0
      NS_ASSERTION(mContent->IsMathMLElement(nsGkAtoms::mover_),
206
0
                   "mContent->NodeInfo()->NameAtom() not recognized");
207
0
      overscriptFrame = baseFrame->GetNextSibling();
208
0
    }
209
0
  }
210
0
  if (underscriptFrame &&
211
0
      mContent->IsMathMLElement(nsGkAtoms::munderover_)) {
212
0
    overscriptFrame = underscriptFrame->GetNextSibling();
213
0
214
0
  }
215
0
216
0
  // if our base is an embellished operator, let its state bubble to us (in particular,
217
0
  // this is where we get the flag for NS_MATHML_EMBELLISH_MOVABLELIMITS). Our flags
218
0
  // are reset to the default values of false if the base frame isn't embellished.
219
0
  mPresentationData.baseFrame = baseFrame;
220
0
  GetEmbellishDataFrom(baseFrame, mEmbellishData);
221
0
222
0
  // The default value of accentunder is false, unless the underscript is embellished
223
0
  // and its core <mo> is an accent
224
0
  nsEmbellishData embellishData;
225
0
  nsAutoString value;
226
0
  if (mContent->IsAnyOfMathMLElements(nsGkAtoms::munder_,
227
0
                                      nsGkAtoms::munderover_)) {
228
0
    GetEmbellishDataFrom(underscriptFrame, embellishData);
229
0
    if (NS_MATHML_EMBELLISH_IS_ACCENT(embellishData.flags)) {
230
0
      mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENTUNDER;
231
0
    } else {
232
0
      mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_ACCENTUNDER;
233
0
    }
234
0
235
0
    // if we have an accentunder attribute, it overrides what the underscript said
236
0
    if (mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::accentunder_, value)) {
237
0
      if (value.EqualsLiteral("true")) {
238
0
        mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENTUNDER;
239
0
      } else if (value.EqualsLiteral("false")) {
240
0
        mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_ACCENTUNDER;
241
0
      }
242
0
    }
243
0
  }
244
0
245
0
  // The default value of accent is false, unless the overscript is embellished
246
0
  // and its core <mo> is an accent
247
0
  if (mContent->IsAnyOfMathMLElements(nsGkAtoms::mover_,
248
0
                                      nsGkAtoms::munderover_)) {
249
0
    GetEmbellishDataFrom(overscriptFrame, embellishData);
250
0
    if (NS_MATHML_EMBELLISH_IS_ACCENT(embellishData.flags)) {
251
0
      mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENTOVER;
252
0
    } else {
253
0
      mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_ACCENTOVER;
254
0
    }
255
0
256
0
    // if we have an accent attribute, it overrides what the overscript said
257
0
    if (mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::accent_, value)) {
258
0
      if (value.EqualsLiteral("true")) {
259
0
        mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENTOVER;
260
0
      } else if (value.EqualsLiteral("false")) {
261
0
        mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_ACCENTOVER;
262
0
      }
263
0
    }
264
0
  }
265
0
266
0
  bool subsupDisplay =
267
0
    NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishData.flags) &&
268
0
    StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_INLINE;
269
0
270
0
  // disable the stretch-all flag if we are going to act like a superscript
271
0
  if (subsupDisplay) {
272
0
    mPresentationData.flags &= ~NS_MATHML_STRETCH_ALL_CHILDREN_HORIZONTALLY;
273
0
  }
274
0
275
0
  // Now transmit any change that we want to our children so that they
276
0
  // can update their mPresentationData structs
277
0
  //---------------------------------------------------------------------
278
0
279
0
  /* The REC says:
280
0
     Within underscript, <munderover> always sets displaystyle to "false",
281
0
     but increments scriptlevel by 1 only when accentunder is "false".
282
0
283
0
     Within overscript, <munderover> always sets displaystyle to "false",
284
0
     but increments scriptlevel by 1 only when accent is "false".
285
0
286
0
     Within subscript and superscript it increments scriptlevel by 1, and
287
0
     sets displaystyle to "false", but leaves both attributes unchanged within
288
0
     base.
289
0
290
0
     The TeXBook treats 'over' like a superscript, so p.141 or Rule 13a
291
0
     say it shouldn't be compressed. However, The TeXBook says
292
0
     that math accents and \overline change uncramped styles to their
293
0
     cramped counterparts.
294
0
  */
295
0
  if (mContent->IsAnyOfMathMLElements(nsGkAtoms::mover_,
296
0
                                      nsGkAtoms::munderover_)) {
297
0
    uint32_t compress = NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData.flags)
298
0
      ? NS_MATHML_COMPRESSED : 0;
299
0
    mIncrementOver =
300
0
      !NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData.flags) ||
301
0
      subsupDisplay;
302
0
    SetIncrementScriptLevel(
303
0
        mContent->IsMathMLElement(nsGkAtoms::mover_) ? 1 : 2, mIncrementOver);
304
0
    if (mIncrementOver) {
305
0
      PropagateFrameFlagFor(overscriptFrame,
306
0
                            NS_FRAME_MATHML_SCRIPT_DESCENDANT);
307
0
    }
308
0
    PropagatePresentationDataFor(overscriptFrame, compress, compress);
309
0
  }
310
0
  /*
311
0
     The TeXBook treats 'under' like a subscript, so p.141 or Rule 13a
312
0
     say it should be compressed
313
0
  */
314
0
  if (mContent->IsAnyOfMathMLElements(nsGkAtoms::munder_,
315
0
                                      nsGkAtoms::munderover_)) {
316
0
    mIncrementUnder =
317
0
      !NS_MATHML_EMBELLISH_IS_ACCENTUNDER(mEmbellishData.flags) ||
318
0
      subsupDisplay;
319
0
    SetIncrementScriptLevel(1, mIncrementUnder);
320
0
    if (mIncrementUnder) {
321
0
      PropagateFrameFlagFor(underscriptFrame,
322
0
                            NS_FRAME_MATHML_SCRIPT_DESCENDANT);
323
0
    }
324
0
    PropagatePresentationDataFor(underscriptFrame,
325
0
                                 NS_MATHML_COMPRESSED,
326
0
                                 NS_MATHML_COMPRESSED);
327
0
  }
328
0
329
0
  /* Set flags for dtls font feature settings.
330
0
331
0
     dtls
332
0
     Dotless Forms
333
0
     This feature provides dotless forms for Math Alphanumeric
334
0
     characters, such as U+1D422 MATHEMATICAL BOLD SMALL I,
335
0
     U+1D423 MATHEMATICAL BOLD SMALL J, U+1D456
336
0
     U+MATHEMATICAL ITALIC SMALL I, U+1D457 MATHEMATICAL ITALIC
337
0
     SMALL J, and so on.
338
0
     The dotless forms are to be used as base forms for placing
339
0
     mathematical accents over them.
340
0
341
0
     To opt out of this change, add the following to the stylesheet:
342
0
     "font-feature-settings: 'dtls' 0"
343
0
   */
344
0
  if (overscriptFrame &&
345
0
      NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData.flags) &&
346
0
      !NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishData.flags)) {
347
0
    PropagatePresentationDataFor(baseFrame, NS_MATHML_DTLS, NS_MATHML_DTLS);
348
0
  }
349
0
350
0
  return NS_OK;
351
0
}
352
353
/*
354
The REC says:
355
*  If the base is an operator with movablelimits="true" (or an embellished
356
   operator whose <mo> element core has movablelimits="true"), and
357
   displaystyle="false", then underscript and overscript are drawn in
358
   a subscript and superscript position, respectively. In this case,
359
   the accent and accentunder attributes are ignored. This is often
360
   used for limits on symbols such as &sum;.
361
362
i.e.,:
363
 if (NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishDataflags) &&
364
     StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_INLINE) {
365
  // place like subscript-superscript pair
366
 }
367
 else {
368
  // place like underscript-overscript pair
369
 }
370
*/
371
372
/* virtual */ nsresult
373
nsMathMLmunderoverFrame::Place(DrawTarget*          aDrawTarget,
374
                               bool                 aPlaceOrigin,
375
                               ReflowOutput& aDesiredSize)
376
0
{
377
0
  float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
378
0
  if (NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishData.flags) &&
379
0
      StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_INLINE) {
380
0
    //place like sub sup or subsup
381
0
    if (mContent->IsMathMLElement(nsGkAtoms::munderover_)) {
382
0
      return nsMathMLmmultiscriptsFrame::PlaceMultiScript(PresContext(),
383
0
                                                          aDrawTarget,
384
0
                                                          aPlaceOrigin,
385
0
                                                          aDesiredSize,
386
0
                                                          this, 0, 0,
387
0
                                                          fontSizeInflation);
388
0
    } else if (mContent->IsMathMLElement( nsGkAtoms::munder_)) {
389
0
      return nsMathMLmmultiscriptsFrame::PlaceMultiScript(PresContext(),
390
0
                                                          aDrawTarget,
391
0
                                                          aPlaceOrigin,
392
0
                                                          aDesiredSize,
393
0
                                                          this, 0, 0,
394
0
                                                          fontSizeInflation);
395
0
    } else {
396
0
      NS_ASSERTION(mContent->IsMathMLElement(nsGkAtoms::mover_),
397
0
                   "mContent->NodeInfo()->NameAtom() not recognized");
398
0
      return nsMathMLmmultiscriptsFrame::PlaceMultiScript(PresContext(),
399
0
                                                          aDrawTarget,
400
0
                                                          aPlaceOrigin,
401
0
                                                          aDesiredSize,
402
0
                                                          this, 0, 0,
403
0
                                                          fontSizeInflation);
404
0
    }
405
0
406
0
  }
407
0
408
0
  ////////////////////////////////////
409
0
  // Get the children's desired sizes
410
0
411
0
  nsBoundingMetrics bmBase, bmUnder, bmOver;
412
0
  ReflowOutput baseSize(aDesiredSize.GetWritingMode());
413
0
  ReflowOutput underSize(aDesiredSize.GetWritingMode());
414
0
  ReflowOutput overSize(aDesiredSize.GetWritingMode());
415
0
  nsIFrame* overFrame = nullptr;
416
0
  nsIFrame* underFrame = nullptr;
417
0
  nsIFrame* baseFrame = mFrames.FirstChild();
418
0
  underSize.SetBlockStartAscent(0);
419
0
  overSize.SetBlockStartAscent(0);
420
0
  bool haveError = false;
421
0
  if (baseFrame) {
422
0
    if (mContent->IsAnyOfMathMLElements(nsGkAtoms::munder_,
423
0
                                        nsGkAtoms::munderover_)) {
424
0
      underFrame = baseFrame->GetNextSibling();
425
0
    } else if (mContent->IsMathMLElement(nsGkAtoms::mover_)) {
426
0
      overFrame = baseFrame->GetNextSibling();
427
0
    }
428
0
  }
429
0
  if (underFrame && mContent->IsMathMLElement(nsGkAtoms::munderover_)) {
430
0
    overFrame = underFrame->GetNextSibling();
431
0
  }
432
0
433
0
  if (mContent->IsMathMLElement(nsGkAtoms::munder_)) {
434
0
    if (!baseFrame || !underFrame || underFrame->GetNextSibling()) {
435
0
      // report an error, encourage people to get their markups in order
436
0
      haveError = true;
437
0
    }
438
0
  }
439
0
  if (mContent->IsMathMLElement(nsGkAtoms::mover_)) {
440
0
    if (!baseFrame || !overFrame || overFrame->GetNextSibling()) {
441
0
      // report an error, encourage people to get their markups in order
442
0
      haveError = true;
443
0
    }
444
0
  }
445
0
  if (mContent->IsMathMLElement(nsGkAtoms::munderover_)) {
446
0
    if (!baseFrame || !underFrame || !overFrame || overFrame->GetNextSibling()) {
447
0
      // report an error, encourage people to get their markups in order
448
0
      haveError = true;
449
0
    }
450
0
  }
451
0
  if (haveError) {
452
0
    if (aPlaceOrigin) {
453
0
      ReportChildCountError();
454
0
    }
455
0
    return ReflowError(aDrawTarget, aDesiredSize);
456
0
  }
457
0
  GetReflowAndBoundingMetricsFor(baseFrame, baseSize, bmBase);
458
0
  if (underFrame) {
459
0
    GetReflowAndBoundingMetricsFor(underFrame, underSize, bmUnder);
460
0
  }
461
0
  if (overFrame) {
462
0
    GetReflowAndBoundingMetricsFor(overFrame, overSize, bmOver);
463
0
  }
464
0
465
0
  nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
466
0
467
0
  ////////////////////
468
0
  // Place Children
469
0
470
0
  RefPtr<nsFontMetrics> fm =
471
0
    nsLayoutUtils::GetFontMetricsForFrame(this, fontSizeInflation);
472
0
473
0
  nscoord xHeight = fm->XHeight();
474
0
  nscoord oneDevPixel = fm->AppUnitsPerDevPixel();
475
0
  gfxFont* mathFont = fm->GetThebesFontGroup()->GetFirstMathFont();
476
0
477
0
  nscoord ruleThickness;
478
0
  GetRuleThickness (aDrawTarget, fm, ruleThickness);
479
0
480
0
  nscoord correction = 0;
481
0
  GetItalicCorrection (bmBase, correction);
482
0
483
0
  // there are 2 different types of placement depending on
484
0
  // whether we want an accented under or not
485
0
486
0
  nscoord underDelta1 = 0; // gap between base and underscript
487
0
  nscoord underDelta2 = 0; // extra space beneath underscript
488
0
489
0
  if (!NS_MATHML_EMBELLISH_IS_ACCENTUNDER(mEmbellishData.flags)) {
490
0
    // Rule 13a, App. G, TeXbook
491
0
    nscoord bigOpSpacing2, bigOpSpacing4, bigOpSpacing5, dummy;
492
0
    GetBigOpSpacings (fm,
493
0
                      dummy, bigOpSpacing2,
494
0
                      dummy, bigOpSpacing4,
495
0
                      bigOpSpacing5);
496
0
    if (mathFont) {
497
0
      // XXXfredw The Open Type MATH table has some StretchStack* parameters
498
0
      // that we may use when the base is a stretchy horizontal operator. See
499
0
      // bug 963131.
500
0
      bigOpSpacing2 =
501
0
        mathFont->MathTable()->Constant(gfxMathTable::LowerLimitGapMin,
502
0
                                        oneDevPixel);
503
0
      bigOpSpacing4 =
504
0
        mathFont->MathTable()->Constant(gfxMathTable::LowerLimitBaselineDropMin,
505
0
                                        oneDevPixel);
506
0
      bigOpSpacing5 = 0;
507
0
    }
508
0
    underDelta1 = std::max(bigOpSpacing2, (bigOpSpacing4 - bmUnder.ascent));
509
0
    underDelta2 = bigOpSpacing5;
510
0
  }
511
0
  else {
512
0
    // No corresponding rule in TeXbook - we are on our own here
513
0
    // XXX tune the gap delta between base and underscript
514
0
    // XXX Should we use Rule 10 like \underline does?
515
0
    // XXXfredw Perhaps use the Underbar* parameters of the MATH table. See
516
0
    // bug 963125.
517
0
    underDelta1 = ruleThickness + onePixel/2;
518
0
    underDelta2 = ruleThickness;
519
0
  }
520
0
  // empty under?
521
0
  if (!(bmUnder.ascent + bmUnder.descent)) {
522
0
    underDelta1 = 0;
523
0
    underDelta2 = 0;
524
0
  }
525
0
526
0
  nscoord overDelta1 = 0; // gap between base and overscript
527
0
  nscoord overDelta2 = 0; // extra space above overscript
528
0
529
0
  if (!NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData.flags)) {
530
0
    // Rule 13a, App. G, TeXbook
531
0
    // XXXfredw The Open Type MATH table has some StretchStack* parameters
532
0
    // that we may use when the base is a stretchy horizontal operator. See
533
0
    // bug 963131.
534
0
    nscoord bigOpSpacing1, bigOpSpacing3, bigOpSpacing5, dummy;
535
0
    GetBigOpSpacings (fm,
536
0
                      bigOpSpacing1, dummy,
537
0
                      bigOpSpacing3, dummy,
538
0
                      bigOpSpacing5);
539
0
    if (mathFont) {
540
0
      // XXXfredw The Open Type MATH table has some StretchStack* parameters
541
0
      // that we may use when the base is a stretchy horizontal operator. See
542
0
      // bug 963131.
543
0
      bigOpSpacing1 =
544
0
        mathFont->MathTable()->Constant(gfxMathTable::UpperLimitGapMin,
545
0
                                        oneDevPixel);
546
0
      bigOpSpacing3 =
547
0
        mathFont->MathTable()->Constant(gfxMathTable::UpperLimitBaselineRiseMin,
548
0
                                        oneDevPixel);
549
0
      bigOpSpacing5 = 0;
550
0
    }
551
0
    overDelta1 = std::max(bigOpSpacing1, (bigOpSpacing3 - bmOver.descent));
552
0
    overDelta2 = bigOpSpacing5;
553
0
554
0
    // XXX This is not a TeX rule...
555
0
    // delta1 (as computed abvove) can become really big when bmOver.descent is
556
0
    // negative,  e.g., if the content is &OverBar. In such case, we use the height
557
0
    if (bmOver.descent < 0)
558
0
      overDelta1 = std::max(bigOpSpacing1, (bigOpSpacing3 - (bmOver.ascent + bmOver.descent)));
559
0
  }
560
0
  else {
561
0
    // Rule 12, App. G, TeXbook
562
0
    // We are going to modify this rule to make it more general.
563
0
    // The idea behind Rule 12 in the TeXBook is to keep the accent
564
0
    // as close to the base as possible, while ensuring that the
565
0
    // distance between the *baseline* of the accent char and
566
0
    // the *baseline* of the base is atleast x-height.
567
0
    // The idea is that for normal use, we would like all the accents
568
0
    // on a line to line up atleast x-height above the baseline
569
0
    // if possible.
570
0
    // When the ascent of the base is >= x-height,
571
0
    // the baseline of the accent char is placed just above the base
572
0
    // (specifically, the baseline of the accent char is placed
573
0
    // above the baseline of the base by the ascent of the base).
574
0
    // For ease of implementation,
575
0
    // this assumes that the font-designer designs accents
576
0
    // in such a way that the bottom of the accent is atleast x-height
577
0
    // above its baseline, otherwise there will be collisions
578
0
    // with the base. Also there should be proper padding between
579
0
    // the bottom of the accent char and its baseline.
580
0
    // The above rule may not be obvious from a first
581
0
    // reading of rule 12 in the TeXBook !!!
582
0
    // The mathml <mover> tag can use accent chars that
583
0
    // do not follow this convention. So we modify TeX's rule
584
0
    // so that TeX's rule gets subsumed for accents that follow
585
0
    // TeX's convention,
586
0
    // while also allowing accents that do not follow the convention :
587
0
    // we try to keep the *bottom* of the accent char atleast x-height
588
0
    // from the baseline of the base char. we also slap on an extra
589
0
    // padding between the accent and base chars.
590
0
    overDelta1 = ruleThickness + onePixel/2;
591
0
    nscoord accentBaseHeight = xHeight;
592
0
    if (mathFont) {
593
0
      accentBaseHeight =
594
0
        mathFont->MathTable()->Constant(gfxMathTable::AccentBaseHeight,
595
0
                                        oneDevPixel);
596
0
    }
597
0
    if (bmBase.ascent < accentBaseHeight) {
598
0
      // also ensure at least accentBaseHeight above the baseline of the base
599
0
      overDelta1 += accentBaseHeight - bmBase.ascent;
600
0
    }
601
0
    overDelta2 = ruleThickness;
602
0
  }
603
0
  // empty over?
604
0
  if (!(bmOver.ascent + bmOver.descent)) {
605
0
    overDelta1 = 0;
606
0
    overDelta2 = 0;
607
0
  }
608
0
609
0
  nscoord dxBase = 0, dxOver = 0, dxUnder = 0;
610
0
  nsAutoString valueAlign;
611
0
  enum {
612
0
    center,
613
0
    left,
614
0
    right
615
0
  } alignPosition = center;
616
0
617
0
  if (mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::align, valueAlign)) {
618
0
    if (valueAlign.EqualsLiteral("left")) {
619
0
      alignPosition = left;
620
0
    } else if (valueAlign.EqualsLiteral("right")) {
621
0
      alignPosition = right;
622
0
    }
623
0
  }
624
0
625
0
  //////////
626
0
  // pass 1, do what <mover> does: attach the overscript on the base
627
0
628
0
  // Ad-hoc - This is to override fonts which have ready-made _accent_
629
0
  // glyphs with negative lbearing and rbearing. We want to position
630
0
  // the overscript ourselves
631
0
  nscoord overWidth = bmOver.width;
632
0
  if (!overWidth && (bmOver.rightBearing - bmOver.leftBearing > 0)) {
633
0
    overWidth = bmOver.rightBearing - bmOver.leftBearing;
634
0
    dxOver = -bmOver.leftBearing;
635
0
  }
636
0
637
0
  if (NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData.flags)) {
638
0
    mBoundingMetrics.width = bmBase.width;
639
0
    if (alignPosition == center) {
640
0
      dxOver += correction;
641
0
    }
642
0
  }
643
0
  else {
644
0
    mBoundingMetrics.width = std::max(bmBase.width, overWidth);
645
0
    if (alignPosition == center) {
646
0
      dxOver += correction/2;
647
0
    }
648
0
  }
649
0
650
0
  if (alignPosition == center) {
651
0
    dxOver += (mBoundingMetrics.width - overWidth)/2;
652
0
    dxBase = (mBoundingMetrics.width - bmBase.width)/2;
653
0
  } else if (alignPosition == right) {
654
0
    dxOver += mBoundingMetrics.width - overWidth;
655
0
    dxBase = mBoundingMetrics.width - bmBase.width;
656
0
  }
657
0
658
0
  mBoundingMetrics.ascent =
659
0
    bmBase.ascent + overDelta1 + bmOver.ascent + bmOver.descent;
660
0
  mBoundingMetrics.descent = bmBase.descent;
661
0
  mBoundingMetrics.leftBearing =
662
0
    std::min(dxBase + bmBase.leftBearing, dxOver + bmOver.leftBearing);
663
0
  mBoundingMetrics.rightBearing =
664
0
    std::max(dxBase + bmBase.rightBearing, dxOver + bmOver.rightBearing);
665
0
666
0
  //////////
667
0
  // pass 2, do what <munder> does: attach the underscript on the previous
668
0
  // result. We conceptually view the previous result as an "anynomous base"
669
0
  // from where to attach the underscript. Hence if the underscript is empty,
670
0
  // we should end up like <mover>. If the overscript is empty, we should
671
0
  // end up like <munder>.
672
0
673
0
  nsBoundingMetrics bmAnonymousBase = mBoundingMetrics;
674
0
  nscoord ascentAnonymousBase =
675
0
    std::max(mBoundingMetrics.ascent + overDelta2,
676
0
             overSize.BlockStartAscent() + bmOver.descent +
677
0
             overDelta1 + bmBase.ascent);
678
0
  ascentAnonymousBase = std::max(ascentAnonymousBase,
679
0
                                 baseSize.BlockStartAscent());
680
0
681
0
  // Width of non-spacing marks is zero so use left and right bearing.
682
0
  nscoord underWidth = bmUnder.width;
683
0
  if (!underWidth) {
684
0
    underWidth = bmUnder.rightBearing - bmUnder.leftBearing;
685
0
    dxUnder = -bmUnder.leftBearing;
686
0
  }
687
0
688
0
  nscoord maxWidth = std::max(bmAnonymousBase.width, underWidth);
689
0
  if (alignPosition == center &&
690
0
      !NS_MATHML_EMBELLISH_IS_ACCENTUNDER(mEmbellishData.flags)) {
691
0
    GetItalicCorrection(bmAnonymousBase, correction);
692
0
    dxUnder += -correction/2;
693
0
  }
694
0
  nscoord dxAnonymousBase = 0;
695
0
  if (alignPosition == center) {
696
0
    dxUnder += (maxWidth - underWidth)/2;
697
0
    dxAnonymousBase = (maxWidth - bmAnonymousBase.width)/2;
698
0
  } else if (alignPosition == right) {
699
0
    dxUnder += maxWidth - underWidth;
700
0
    dxAnonymousBase = maxWidth - bmAnonymousBase.width;
701
0
  }
702
0
703
0
  // adjust the offsets of the real base and overscript since their
704
0
  // final offsets should be relative to us...
705
0
  dxOver += dxAnonymousBase;
706
0
  dxBase += dxAnonymousBase;
707
0
708
0
  mBoundingMetrics.width =
709
0
    std::max(dxAnonymousBase + bmAnonymousBase.width, dxUnder + bmUnder.width);
710
0
  // At this point, mBoundingMetrics.ascent = bmAnonymousBase.ascent
711
0
  mBoundingMetrics.descent =
712
0
    bmAnonymousBase.descent + underDelta1 + bmUnder.ascent + bmUnder.descent;
713
0
  mBoundingMetrics.leftBearing =
714
0
    std::min(dxAnonymousBase + bmAnonymousBase.leftBearing, dxUnder + bmUnder.leftBearing);
715
0
  mBoundingMetrics.rightBearing =
716
0
    std::max(dxAnonymousBase + bmAnonymousBase.rightBearing, dxUnder + bmUnder.rightBearing);
717
0
718
0
  aDesiredSize.SetBlockStartAscent(ascentAnonymousBase);
719
0
  aDesiredSize.Height() = aDesiredSize.BlockStartAscent() +
720
0
    std::max(mBoundingMetrics.descent + underDelta2,
721
0
           bmAnonymousBase.descent + underDelta1 + bmUnder.ascent +
722
0
             underSize.Height() - underSize.BlockStartAscent());
723
0
  aDesiredSize.Height() = std::max(aDesiredSize.Height(),
724
0
                               aDesiredSize.BlockStartAscent() +
725
0
                               baseSize.Height() - baseSize.BlockStartAscent());
726
0
  aDesiredSize.Width() = mBoundingMetrics.width;
727
0
  aDesiredSize.mBoundingMetrics = mBoundingMetrics;
728
0
729
0
  mReference.x = 0;
730
0
  mReference.y = aDesiredSize.BlockStartAscent();
731
0
732
0
  if (aPlaceOrigin) {
733
0
    nscoord dy;
734
0
    // place overscript
735
0
    if (overFrame) {
736
0
      dy = aDesiredSize.BlockStartAscent() -
737
0
           mBoundingMetrics.ascent + bmOver.ascent -
738
0
           overSize.BlockStartAscent();
739
0
      FinishReflowChild (overFrame, PresContext(), overSize, nullptr, dxOver, dy, 0);
740
0
    }
741
0
    // place base
742
0
    dy = aDesiredSize.BlockStartAscent() - baseSize.BlockStartAscent();
743
0
    FinishReflowChild (baseFrame, PresContext(), baseSize, nullptr, dxBase, dy, 0);
744
0
    // place underscript
745
0
    if (underFrame) {
746
0
      dy = aDesiredSize.BlockStartAscent() +
747
0
           mBoundingMetrics.descent - bmUnder.descent -
748
0
           underSize.BlockStartAscent();
749
0
      FinishReflowChild (underFrame, PresContext(), underSize, nullptr,
750
0
                         dxUnder, dy, 0);
751
0
    }
752
0
  }
753
0
  return NS_OK;
754
0
}