Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/layout/mathml/nsMathMLmoFrame.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 "gfxContext.h"
8
#include "nsMathMLmoFrame.h"
9
#include "nsPresContext.h"
10
#include "nsContentUtils.h"
11
#include "nsFrameSelection.h"
12
#include "nsMathMLElement.h"
13
#include <algorithm>
14
15
//
16
// <mo> -- operator, fence, or separator - implementation
17
//
18
19
// additional ComputedStyle to be used by our MathMLChar.
20
0
#define NS_MATHML_CHAR_STYLE_CONTEXT_INDEX   0
21
22
nsIFrame*
23
NS_NewMathMLmoFrame(nsIPresShell* aPresShell, ComputedStyle* aStyle)
24
0
{
25
0
  return new (aPresShell) nsMathMLmoFrame(aStyle);
26
0
}
27
28
NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmoFrame)
29
30
nsMathMLmoFrame::~nsMathMLmoFrame()
31
0
{
32
0
}
33
34
static const char16_t kApplyFunction  = char16_t(0x2061);
35
static const char16_t kInvisibleTimes = char16_t(0x2062);
36
static const char16_t kInvisibleSeparator = char16_t(0x2063);
37
static const char16_t kInvisiblePlus = char16_t(0x2064);
38
39
eMathMLFrameType
40
nsMathMLmoFrame::GetMathMLFrameType()
41
0
{
42
0
  return NS_MATHML_OPERATOR_IS_INVISIBLE(mFlags)
43
0
    ? eMathMLFrameType_OperatorInvisible
44
0
    : eMathMLFrameType_OperatorOrdinary;
45
0
}
46
47
// since a mouse click implies selection, we cannot just rely on the
48
// frame's state bit in our child text frame. So we will first check
49
// its selected state bit, and use this little helper to double check.
50
bool
51
nsMathMLmoFrame::IsFrameInSelection(nsIFrame* aFrame)
52
0
{
53
0
  NS_ASSERTION(aFrame, "null arg");
54
0
  if (!aFrame || !aFrame->IsSelected())
55
0
    return false;
56
0
57
0
  const nsFrameSelection* frameSelection = aFrame->GetConstFrameSelection();
58
0
  UniquePtr<SelectionDetails> details =
59
0
    frameSelection->LookUpSelection(aFrame->GetContent(), 0, 1, true);
60
0
61
0
  return details != nullptr;
62
0
}
63
64
bool
65
nsMathMLmoFrame::UseMathMLChar()
66
0
{
67
0
  return (NS_MATHML_OPERATOR_GET_FORM(mFlags) &&
68
0
          NS_MATHML_OPERATOR_IS_MUTABLE(mFlags)) ||
69
0
    NS_MATHML_OPERATOR_IS_CENTERED(mFlags);
70
0
}
71
72
void
73
nsMathMLmoFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
74
                                  const nsDisplayListSet& aLists)
75
0
{
76
0
  bool useMathMLChar = UseMathMLChar();
77
0
78
0
  if (!useMathMLChar) {
79
0
    // let the base class do everything
80
0
    nsMathMLTokenFrame::BuildDisplayList(aBuilder, aLists);
81
0
  } else {
82
0
    DisplayBorderBackgroundOutline(aBuilder, aLists);
83
0
84
0
    // make our char selected if our inner child text frame is selected
85
0
    bool isSelected = false;
86
0
    nsRect selectedRect;
87
0
    nsIFrame* firstChild = mFrames.FirstChild();
88
0
    if (IsFrameInSelection(firstChild)) {
89
0
      mMathMLChar.GetRect(selectedRect);
90
0
      // add a one pixel border (it renders better for operators like minus)
91
0
      selectedRect.Inflate(nsPresContext::CSSPixelsToAppUnits(1));
92
0
      isSelected = true;
93
0
    }
94
0
    mMathMLChar.Display(aBuilder, this, aLists, 0, isSelected ? &selectedRect : nullptr);
95
0
96
#if defined(DEBUG) && defined(SHOW_BOUNDING_BOX)
97
    // for visual debug
98
    DisplayBoundingMetrics(aBuilder, this, mReference, mBoundingMetrics, aLists);
99
#endif
100
  }
101
0
}
102
103
// get the text that we enclose and setup our nsMathMLChar
104
void
105
nsMathMLmoFrame::ProcessTextData()
106
0
{
107
0
  mFlags = 0;
108
0
109
0
  nsAutoString data;
110
0
  nsContentUtils::GetNodeTextContent(mContent, false, data);
111
0
112
0
  data.CompressWhitespace();
113
0
  int32_t length = data.Length();
114
0
  char16_t ch = (length == 0) ? char16_t('\0') : data[0];
115
0
116
0
  if ((length == 1) &&
117
0
      (ch == kApplyFunction  ||
118
0
       ch == kInvisibleSeparator ||
119
0
       ch == kInvisiblePlus ||
120
0
       ch == kInvisibleTimes)) {
121
0
    mFlags |= NS_MATHML_OPERATOR_INVISIBLE;
122
0
  }
123
0
124
0
  // don't bother doing anything special if we don't have a single child
125
0
  nsPresContext* presContext = PresContext();
126
0
  if (mFrames.GetLength() != 1) {
127
0
    data.Truncate(); // empty data to reset the char
128
0
    mMathMLChar.SetData(data);
129
0
    ResolveMathMLCharStyle(presContext, mContent, mComputedStyle, &mMathMLChar);
130
0
    return;
131
0
  }
132
0
133
0
  // special... in math mode, the usual minus sign '-' looks too short, so
134
0
  // what we do here is to remap <mo>-</mo> to the official Unicode minus
135
0
  // sign (U+2212) which looks much better. For background on this, see
136
0
  // http://groups.google.com/groups?hl=en&th=66488daf1ade7635&rnum=1
137
0
  if (1 == length && ch == '-') {
138
0
    ch = 0x2212;
139
0
    data = ch;
140
0
  }
141
0
142
0
  // cache the special bits: mutable, accent, movablelimits, centered.
143
0
  // we need to do this in anticipation of other requirements, and these
144
0
  // bits don't change. Do not reset these bits unless the text gets changed.
145
0
146
0
  // lookup all the forms under which the operator is listed in the dictionary,
147
0
  // and record whether the operator has accent="true" or movablelimits="true"
148
0
  nsOperatorFlags flags[4];
149
0
  float lspace[4], rspace[4];
150
0
  nsMathMLOperators::LookupOperators(data, flags, lspace, rspace);
151
0
  nsOperatorFlags allFlags =
152
0
    flags[NS_MATHML_OPERATOR_FORM_INFIX] |
153
0
    flags[NS_MATHML_OPERATOR_FORM_POSTFIX] |
154
0
    flags[NS_MATHML_OPERATOR_FORM_PREFIX];
155
0
156
0
  mFlags |= allFlags & NS_MATHML_OPERATOR_ACCENT;
157
0
  mFlags |= allFlags & NS_MATHML_OPERATOR_MOVABLELIMITS;
158
0
159
0
  // see if this is an operator that should be centered to cater for
160
0
  // fonts that are not math-aware
161
0
  if (1 == length) {
162
0
    if ((ch == '+') || (ch == '=') || (ch == '*') ||
163
0
        (ch == 0x2212) || // &minus;
164
0
        (ch == 0x2264) || // &le;
165
0
        (ch == 0x2265) || // &ge;
166
0
        (ch == 0x00D7)) { // &times;
167
0
      mFlags |= NS_MATHML_OPERATOR_CENTERED;
168
0
    }
169
0
  }
170
0
171
0
  // cache the operator
172
0
  mMathMLChar.SetData(data);
173
0
174
0
  // cache the native direction -- beware of bug 133429...
175
0
  // mEmbellishData.direction must always retain our native direction, whereas
176
0
  // mMathMLChar.GetStretchDirection() may change later, when Stretch() is called
177
0
  mEmbellishData.direction = mMathMLChar.GetStretchDirection();
178
0
179
0
  bool isMutable =
180
0
    NS_MATHML_OPERATOR_IS_LARGEOP(allFlags) ||
181
0
    (mEmbellishData.direction != NS_STRETCH_DIRECTION_UNSUPPORTED);
182
0
  if (isMutable)
183
0
    mFlags |= NS_MATHML_OPERATOR_MUTABLE;
184
0
185
0
  ResolveMathMLCharStyle(presContext, mContent, mComputedStyle, &mMathMLChar);
186
0
}
187
188
// get our 'form' and lookup in the Operator Dictionary to fetch
189
// our default data that may come from there. Then complete our setup
190
// using attributes that we may have. To stay in sync, this function is
191
// called very often. We depend on many things that may change around us.
192
// However, we re-use unchanged values.
193
void
194
nsMathMLmoFrame::ProcessOperatorData()
195
0
{
196
0
  // if we have been here before, we will just use our cached form
197
0
  nsOperatorFlags form = NS_MATHML_OPERATOR_GET_FORM(mFlags);
198
0
  nsAutoString value;
199
0
  float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
200
0
201
0
  // special bits are always kept in mFlags.
202
0
  // remember the mutable bit from ProcessTextData().
203
0
  // Some chars are listed under different forms in the dictionary,
204
0
  // and there could be a form under which the char is mutable.
205
0
  // If the char is the core of an embellished container, we will keep
206
0
  // it mutable irrespective of the form of the embellished container.
207
0
  // Also remember the other special bits that we want to carry forward.
208
0
  mFlags &= NS_MATHML_OPERATOR_MUTABLE |
209
0
            NS_MATHML_OPERATOR_ACCENT |
210
0
            NS_MATHML_OPERATOR_MOVABLELIMITS |
211
0
            NS_MATHML_OPERATOR_CENTERED |
212
0
            NS_MATHML_OPERATOR_INVISIBLE;
213
0
214
0
  if (!mEmbellishData.coreFrame) {
215
0
    // i.e., we haven't been here before, the default form is infix
216
0
    form = NS_MATHML_OPERATOR_FORM_INFIX;
217
0
218
0
    // reset everything so that we don't keep outdated values around
219
0
    // in case of dynamic changes
220
0
    mEmbellishData.flags = 0;
221
0
    mEmbellishData.coreFrame = nullptr;
222
0
    mEmbellishData.leadingSpace = 0;
223
0
    mEmbellishData.trailingSpace = 0;
224
0
    if (mMathMLChar.Length() != 1)
225
0
      mEmbellishData.direction = NS_STRETCH_DIRECTION_UNSUPPORTED;
226
0
    // else... retain the native direction obtained in ProcessTextData()
227
0
228
0
    if (!mFrames.FirstChild()) {
229
0
      return;
230
0
    }
231
0
232
0
    mEmbellishData.flags |= NS_MATHML_EMBELLISH_OPERATOR;
233
0
    mEmbellishData.coreFrame = this;
234
0
235
0
    // there are two particular things that we also need to record so that if our
236
0
    // parent is <mover>, <munder>, or <munderover>, they will treat us properly:
237
0
    // 1) do we have accent="true"
238
0
    // 2) do we have movablelimits="true"
239
0
240
0
    // they need the extra information to decide how to treat their scripts/limits
241
0
    // (note: <mover>, <munder>, or <munderover> need not necessarily be our
242
0
    // direct parent -- case of embellished operators)
243
0
244
0
    // default values from the Operator Dictionary were obtained in ProcessTextData()
245
0
    // and these special bits are always kept in mFlags
246
0
    if (NS_MATHML_OPERATOR_IS_ACCENT(mFlags))
247
0
      mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENT;
248
0
    if (NS_MATHML_OPERATOR_IS_MOVABLELIMITS(mFlags))
249
0
      mEmbellishData.flags |= NS_MATHML_EMBELLISH_MOVABLELIMITS;
250
0
251
0
    // see if the accent attribute is there
252
0
    mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::accent_, value);
253
0
    if (value.EqualsLiteral("true"))
254
0
      mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENT;
255
0
    else if (value.EqualsLiteral("false"))
256
0
      mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_ACCENT;
257
0
258
0
    // see if the movablelimits attribute is there
259
0
    mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::movablelimits_, value);
260
0
    if (value.EqualsLiteral("true"))
261
0
      mEmbellishData.flags |= NS_MATHML_EMBELLISH_MOVABLELIMITS;
262
0
    else if (value.EqualsLiteral("false"))
263
0
      mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_MOVABLELIMITS;
264
0
265
0
     // ---------------------------------------------------------------------
266
0
     // we will be called again to re-sync the rest of our state next time...
267
0
     // (nobody needs the other values below at this stage)
268
0
     mFlags |= form;
269
0
     return;
270
0
  }
271
0
272
0
  nsPresContext* presContext = PresContext();
273
0
274
0
  // beware of bug 133814 - there is a two-way dependency in the
275
0
  // embellished hierarchy: our embellished ancestors need to set
276
0
  // their flags based on some of our state (set above), and here we
277
0
  // need to re-sync our 'form' depending on our outermost embellished
278
0
  // container. A null form here means that an earlier attempt to stretch
279
0
  // our mMathMLChar failed, in which case we don't bother re-stretching again
280
0
  if (form) {
281
0
    // get our outermost embellished container and its parent.
282
0
    // (we ensure that we are the core, not just a sibling of the core)
283
0
    nsIFrame* embellishAncestor = this;
284
0
    nsEmbellishData embellishData;
285
0
    nsIFrame* parentAncestor = this;
286
0
    do {
287
0
      embellishAncestor = parentAncestor;
288
0
      parentAncestor = embellishAncestor->GetParent();
289
0
      GetEmbellishDataFrom(parentAncestor, embellishData);
290
0
    } while (embellishData.coreFrame == this);
291
0
292
0
    // flag if we have an embellished ancestor
293
0
    if (embellishAncestor != this)
294
0
      mFlags |= NS_MATHML_OPERATOR_EMBELLISH_ANCESTOR;
295
0
    else
296
0
      mFlags &= ~NS_MATHML_OPERATOR_EMBELLISH_ANCESTOR;
297
0
298
0
    // find the position of our outermost embellished container w.r.t
299
0
    // its siblings.
300
0
301
0
    nsIFrame* nextSibling = embellishAncestor->GetNextSibling();
302
0
    nsIFrame* prevSibling = embellishAncestor->GetPrevSibling();
303
0
304
0
    // flag to distinguish from a real infix.  Set for (embellished) operators
305
0
    // that live in (inferred) mrows.
306
0
    nsIMathMLFrame* mathAncestor = do_QueryFrame(parentAncestor);
307
0
    bool zeroSpacing = false;
308
0
    if (mathAncestor) {
309
0
      zeroSpacing =  !mathAncestor->IsMrowLike();
310
0
    } else {
311
0
      nsMathMLmathBlockFrame* blockFrame = do_QueryFrame(parentAncestor);
312
0
      if (blockFrame) {
313
0
        zeroSpacing = !blockFrame->IsMrowLike();
314
0
      }
315
0
    }
316
0
    if (zeroSpacing) {
317
0
      mFlags |= NS_MATHML_OPERATOR_EMBELLISH_ISOLATED;
318
0
    } else {
319
0
      mFlags &= ~NS_MATHML_OPERATOR_EMBELLISH_ISOLATED;
320
0
    }
321
0
322
0
    // find our form
323
0
    form = NS_MATHML_OPERATOR_FORM_INFIX;
324
0
    mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::form, value);
325
0
    if (!value.IsEmpty()) {
326
0
      if (value.EqualsLiteral("prefix"))
327
0
        form = NS_MATHML_OPERATOR_FORM_PREFIX;
328
0
      else if (value.EqualsLiteral("postfix"))
329
0
        form = NS_MATHML_OPERATOR_FORM_POSTFIX;
330
0
    }
331
0
    else {
332
0
      // set our form flag depending on the position
333
0
      if (!prevSibling && nextSibling)
334
0
        form = NS_MATHML_OPERATOR_FORM_PREFIX;
335
0
      else if (prevSibling && !nextSibling)
336
0
        form = NS_MATHML_OPERATOR_FORM_POSTFIX;
337
0
    }
338
0
    mFlags &= ~NS_MATHML_OPERATOR_FORM; // clear the old form bits
339
0
    mFlags |= form;
340
0
341
0
    // Use the default value suggested by the MathML REC.
342
0
    // http://www.w3.org/TR/MathML/chapter3.html#presm.mo.attrs
343
0
    // thickmathspace = 5/18em
344
0
    float lspace = 5.0f/18.0f;
345
0
    float rspace = 5.0f/18.0f;
346
0
    // lookup the operator dictionary
347
0
    nsAutoString data;
348
0
    mMathMLChar.GetData(data);
349
0
    nsMathMLOperators::LookupOperator(data, form, &mFlags, &lspace, &rspace);
350
0
    // Spacing is zero if our outermost embellished operator is not in an
351
0
    // inferred mrow.
352
0
    if (!NS_MATHML_OPERATOR_EMBELLISH_IS_ISOLATED(mFlags) &&
353
0
        (lspace || rspace)) {
354
0
      // Cache the default values of lspace and rspace.
355
0
      // since these values are relative to the 'em' unit, convert to twips now
356
0
      nscoord em;
357
0
      RefPtr<nsFontMetrics> fm =
358
0
        nsLayoutUtils::GetFontMetricsForFrame(this, fontSizeInflation);
359
0
      GetEmHeight(fm, em);
360
0
361
0
      mEmbellishData.leadingSpace = NSToCoordRound(lspace * em);
362
0
      mEmbellishData.trailingSpace = NSToCoordRound(rspace * em);
363
0
364
0
      // tuning if we don't want too much extra space when we are a script.
365
0
      // (with its fonts, TeX sets lspace=0 & rspace=0 as soon as scriptlevel>0.
366
0
      // Our fonts can be anything, so...)
367
0
      if (StyleFont()->mScriptLevel > 0 &&
368
0
          !NS_MATHML_OPERATOR_HAS_EMBELLISH_ANCESTOR(mFlags)) {
369
0
        mEmbellishData.leadingSpace /= 2;
370
0
        mEmbellishData.trailingSpace /= 2;
371
0
      }
372
0
    }
373
0
  }
374
0
375
0
  // If we are an accent without explicit lspace="." or rspace=".",
376
0
  // we will ignore our default leading/trailing space
377
0
378
0
  // lspace
379
0
  //
380
0
  // "Specifies the leading space appearing before the operator"
381
0
  //
382
0
  // values: length
383
0
  // default: set by dictionary (thickmathspace)
384
0
  //
385
0
  // XXXfredw Support for negative and relative values is not implemented
386
0
  // (bug 805926).
387
0
  // Relative values will give a multiple of the current leading space,
388
0
  // which is not necessarily the default one.
389
0
  //
390
0
  nscoord leadingSpace = mEmbellishData.leadingSpace;
391
0
  mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::lspace_, value);
392
0
  if (!value.IsEmpty()) {
393
0
    nsCSSValue cssValue;
394
0
    if (nsMathMLElement::ParseNumericValue(value, cssValue, 0,
395
0
                                           mContent->OwnerDoc())) {
396
0
      if ((eCSSUnit_Number == cssValue.GetUnit()) && !cssValue.GetFloatValue())
397
0
        leadingSpace = 0;
398
0
      else if (cssValue.IsLengthUnit())
399
0
        leadingSpace = CalcLength(presContext, mComputedStyle, cssValue,
400
0
                                  fontSizeInflation);
401
0
      mFlags |= NS_MATHML_OPERATOR_LSPACE_ATTR;
402
0
    }
403
0
  }
404
0
405
0
  // rspace
406
0
  //
407
0
  // "Specifies the trailing space appearing after the operator"
408
0
  //
409
0
  // values: length
410
0
  // default: set by dictionary (thickmathspace)
411
0
  //
412
0
  // XXXfredw Support for negative and relative values is not implemented
413
0
  // (bug 805926).
414
0
  // Relative values will give a multiple of the current leading space,
415
0
  // which is not necessarily the default one.
416
0
  //
417
0
  nscoord trailingSpace = mEmbellishData.trailingSpace;
418
0
  mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::rspace_, value);
419
0
  if (!value.IsEmpty()) {
420
0
    nsCSSValue cssValue;
421
0
    if (nsMathMLElement::ParseNumericValue(value, cssValue, 0,
422
0
                                           mContent->OwnerDoc())) {
423
0
      if ((eCSSUnit_Number == cssValue.GetUnit()) && !cssValue.GetFloatValue())
424
0
        trailingSpace = 0;
425
0
      else if (cssValue.IsLengthUnit())
426
0
        trailingSpace = CalcLength(presContext, mComputedStyle, cssValue,
427
0
                                   fontSizeInflation);
428
0
      mFlags |= NS_MATHML_OPERATOR_RSPACE_ATTR;
429
0
    }
430
0
  }
431
0
432
0
  // little extra tuning to round lspace & rspace to at least a pixel so that
433
0
  // operators don't look as if they are colliding with their operands
434
0
  if (leadingSpace || trailingSpace) {
435
0
    nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
436
0
    if (leadingSpace && leadingSpace < onePixel)
437
0
      leadingSpace = onePixel;
438
0
    if (trailingSpace && trailingSpace < onePixel)
439
0
      trailingSpace = onePixel;
440
0
  }
441
0
442
0
  // the values that we get from our attributes override the dictionary
443
0
  mEmbellishData.leadingSpace = leadingSpace;
444
0
  mEmbellishData.trailingSpace = trailingSpace;
445
0
446
0
  // Now see if there are user-defined attributes that override the dictionary.
447
0
  // XXX Bug 1197771 - forcing an attribute to true when it is false in the
448
0
  // dictionary can cause conflicts in the rest of the stretching algorithms
449
0
  // (e.g. all largeops are assumed to have a vertical direction)
450
0
451
0
  // For each attribute overriden by the user, turn off its bit flag.
452
0
  // symmetric|movablelimits|separator|largeop|accent|fence|stretchy|form
453
0
  // special: accent and movablelimits are handled above,
454
0
  // don't process them here
455
0
456
0
  mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::stretchy_, value);
457
0
  if (value.EqualsLiteral("false")) {
458
0
    mFlags &= ~NS_MATHML_OPERATOR_STRETCHY;
459
0
  } else if (value.EqualsLiteral("true")) {
460
0
    mFlags |= NS_MATHML_OPERATOR_STRETCHY;
461
0
  }
462
0
  if (NS_MATHML_OPERATOR_IS_FENCE(mFlags)) {
463
0
    mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::fence_, value);
464
0
    if (value.EqualsLiteral("false"))
465
0
      mFlags &= ~NS_MATHML_OPERATOR_FENCE;
466
0
    else
467
0
      mEmbellishData.flags |= NS_MATHML_EMBELLISH_FENCE;
468
0
  }
469
0
  mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::largeop_, value);
470
0
  if (value.EqualsLiteral("false")) {
471
0
    mFlags &= ~NS_MATHML_OPERATOR_LARGEOP;
472
0
  } else if (value.EqualsLiteral("true")) {
473
0
    mFlags |= NS_MATHML_OPERATOR_LARGEOP;
474
0
  }
475
0
  if (NS_MATHML_OPERATOR_IS_SEPARATOR(mFlags)) {
476
0
    mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::separator_, value);
477
0
    if (value.EqualsLiteral("false"))
478
0
      mFlags &= ~NS_MATHML_OPERATOR_SEPARATOR;
479
0
    else
480
0
      mEmbellishData.flags |= NS_MATHML_EMBELLISH_SEPARATOR;
481
0
  }
482
0
  mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::symmetric_, value);
483
0
  if (value.EqualsLiteral("false"))
484
0
    mFlags &= ~NS_MATHML_OPERATOR_SYMMETRIC;
485
0
  else if (value.EqualsLiteral("true"))
486
0
    mFlags |= NS_MATHML_OPERATOR_SYMMETRIC;
487
0
488
0
489
0
  // minsize
490
0
  //
491
0
  // "Specifies the minimum size of the operator when stretchy"
492
0
  //
493
0
  // values: length
494
0
  // default: set by dictionary (1em)
495
0
  //
496
0
  // We don't allow negative values.
497
0
  // Note: Contrary to other "length" values, unitless and percentage do not
498
0
  // give a multiple of the defaut value but a multiple of the operator at
499
0
  // normal size.
500
0
  //
501
0
  mMinSize = 0;
502
0
  mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::minsize_, value);
503
0
  if (!value.IsEmpty()) {
504
0
    nsCSSValue cssValue;
505
0
    if (nsMathMLElement::ParseNumericValue(value, cssValue,
506
0
                                           nsMathMLElement::
507
0
                                           PARSE_ALLOW_UNITLESS,
508
0
                                           mContent->OwnerDoc())) {
509
0
      nsCSSUnit unit = cssValue.GetUnit();
510
0
      if (eCSSUnit_Number == unit)
511
0
        mMinSize = cssValue.GetFloatValue();
512
0
      else if (eCSSUnit_Percent == unit)
513
0
        mMinSize = cssValue.GetPercentValue();
514
0
      else if (eCSSUnit_Null != unit) {
515
0
        mMinSize = float(CalcLength(presContext, mComputedStyle, cssValue,
516
0
                                    fontSizeInflation));
517
0
        mFlags |= NS_MATHML_OPERATOR_MINSIZE_ABSOLUTE;
518
0
      }
519
0
    }
520
0
  }
521
0
522
0
  // maxsize
523
0
  //
524
0
  // "Specifies the maximum size of the operator when stretchy"
525
0
  //
526
0
  // values: length | "infinity"
527
0
  // default: set by dictionary (infinity)
528
0
  //
529
0
  // We don't allow negative values.
530
0
  // Note: Contrary to other "length" values, unitless and percentage do not
531
0
  // give a multiple of the defaut value but a multiple of the operator at
532
0
  // normal size.
533
0
  //
534
0
  mMaxSize = NS_MATHML_OPERATOR_SIZE_INFINITY;
535
0
  mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::maxsize_, value);
536
0
  if (!value.IsEmpty()) {
537
0
    nsCSSValue cssValue;
538
0
    if (nsMathMLElement::ParseNumericValue(value, cssValue,
539
0
                                           nsMathMLElement::
540
0
                                           PARSE_ALLOW_UNITLESS,
541
0
                                           mContent->OwnerDoc())) {
542
0
      nsCSSUnit unit = cssValue.GetUnit();
543
0
      if (eCSSUnit_Number == unit)
544
0
        mMaxSize = cssValue.GetFloatValue();
545
0
      else if (eCSSUnit_Percent == unit)
546
0
        mMaxSize = cssValue.GetPercentValue();
547
0
      else if (eCSSUnit_Null != unit) {
548
0
        mMaxSize = float(CalcLength(presContext, mComputedStyle, cssValue,
549
0
                                    fontSizeInflation));
550
0
        mFlags |= NS_MATHML_OPERATOR_MAXSIZE_ABSOLUTE;
551
0
      }
552
0
    }
553
0
  }
554
0
}
555
556
static uint32_t
557
GetStretchHint(nsOperatorFlags aFlags, nsPresentationData aPresentationData,
558
               bool aIsVertical, const nsStyleFont* aStyleFont)
559
0
{
560
0
  uint32_t stretchHint = NS_STRETCH_NONE;
561
0
  // See if it is okay to stretch,
562
0
  // starting from what the Operator Dictionary said
563
0
  if (NS_MATHML_OPERATOR_IS_MUTABLE(aFlags)) {
564
0
    // set the largeop or largeopOnly flags to suitably cover all the
565
0
    // 8 possible cases depending on whether displaystyle, largeop,
566
0
    // stretchy are true or false (see bug 69325).
567
0
    // . largeopOnly is taken if largeop=true and stretchy=false
568
0
    // . largeop is taken if largeop=true and stretchy=true
569
0
    if (aStyleFont->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK &&
570
0
        NS_MATHML_OPERATOR_IS_LARGEOP(aFlags)) {
571
0
      stretchHint = NS_STRETCH_LARGEOP; // (largeopOnly, not mask!)
572
0
      if (NS_MATHML_OPERATOR_IS_INTEGRAL(aFlags)) {
573
0
        stretchHint |= NS_STRETCH_INTEGRAL;
574
0
      }
575
0
      if (NS_MATHML_OPERATOR_IS_STRETCHY(aFlags)) {
576
0
        stretchHint |= NS_STRETCH_NEARER | NS_STRETCH_LARGER;
577
0
      }
578
0
    }
579
0
    else if(NS_MATHML_OPERATOR_IS_STRETCHY(aFlags)) {
580
0
      if (aIsVertical) {
581
0
        // TeX hint. Can impact some sloppy markups missing <mrow></mrow>
582
0
        stretchHint = NS_STRETCH_NEARER;
583
0
      }
584
0
      else {
585
0
        stretchHint = NS_STRETCH_NORMAL;
586
0
      }
587
0
    }
588
0
    // else if the stretchy and largeop attributes have been disabled,
589
0
    // the operator is not mutable
590
0
  }
591
0
  return stretchHint;
592
0
}
593
594
// NOTE: aDesiredStretchSize is an IN/OUT parameter
595
//       On input  - it contains our current size
596
//       On output - the same size or the new size that we want
597
NS_IMETHODIMP
598
nsMathMLmoFrame::Stretch(DrawTarget*          aDrawTarget,
599
                         nsStretchDirection   aStretchDirection,
600
                         nsBoundingMetrics&   aContainerSize,
601
                         ReflowOutput& aDesiredStretchSize)
602
0
{
603
0
  if (NS_MATHML_STRETCH_WAS_DONE(mPresentationData.flags)) {
604
0
    NS_WARNING("it is wrong to fire stretch more than once on a frame");
605
0
    return NS_OK;
606
0
  }
607
0
  mPresentationData.flags |= NS_MATHML_STRETCH_DONE;
608
0
609
0
  nsIFrame* firstChild = mFrames.FirstChild();
610
0
611
0
  // get the axis height;
612
0
  float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
613
0
  RefPtr<nsFontMetrics> fm =
614
0
    nsLayoutUtils::GetFontMetricsForFrame(this, fontSizeInflation);
615
0
  nscoord axisHeight, height;
616
0
  GetAxisHeight(aDrawTarget, fm, axisHeight);
617
0
618
0
  // get the leading to be left at the top and the bottom of the stretched char
619
0
  // this seems more reliable than using fm->GetLeading() on suspicious fonts
620
0
  nscoord em;
621
0
  GetEmHeight(fm, em);
622
0
  nscoord leading = NSToCoordRound(0.2f * em);
623
0
624
0
  // Operators that are stretchy, or those that are to be centered
625
0
  // to cater for fonts that are not math-aware, are handled by the MathMLChar
626
0
  // ('form' is reset if stretch fails -- i.e., we don't bother to stretch next time)
627
0
  bool useMathMLChar = UseMathMLChar();
628
0
629
0
  nsBoundingMetrics charSize;
630
0
  nsBoundingMetrics container = aDesiredStretchSize.mBoundingMetrics;
631
0
  bool isVertical = false;
632
0
633
0
  if (((aStretchDirection == NS_STRETCH_DIRECTION_VERTICAL) ||
634
0
       (aStretchDirection == NS_STRETCH_DIRECTION_DEFAULT))  &&
635
0
      (mEmbellishData.direction == NS_STRETCH_DIRECTION_VERTICAL)) {
636
0
    isVertical = true;
637
0
  }
638
0
639
0
  uint32_t stretchHint =
640
0
    GetStretchHint(mFlags, mPresentationData, isVertical, StyleFont());
641
0
642
0
  if (useMathMLChar) {
643
0
    nsBoundingMetrics initialSize = aDesiredStretchSize.mBoundingMetrics;
644
0
645
0
    if (stretchHint != NS_STRETCH_NONE) {
646
0
647
0
      container = aContainerSize;
648
0
649
0
      // some adjustments if the operator is symmetric and vertical
650
0
651
0
      if (isVertical && NS_MATHML_OPERATOR_IS_SYMMETRIC(mFlags)) {
652
0
        // we need to center about the axis
653
0
        nscoord delta = std::max(container.ascent - axisHeight,
654
0
                               container.descent + axisHeight);
655
0
        container.ascent = delta + axisHeight;
656
0
        container.descent = delta - axisHeight;
657
0
658
0
        // get ready in case we encounter user-desired min-max size
659
0
        delta = std::max(initialSize.ascent - axisHeight,
660
0
                       initialSize.descent + axisHeight);
661
0
        initialSize.ascent = delta + axisHeight;
662
0
        initialSize.descent = delta - axisHeight;
663
0
      }
664
0
665
0
      // check for user-desired min-max size
666
0
667
0
      if (mMaxSize != NS_MATHML_OPERATOR_SIZE_INFINITY && mMaxSize > 0.0f) {
668
0
        // if we are here, there is a user defined maxsize ...
669
0
        //XXX Set stretchHint = NS_STRETCH_NORMAL? to honor the maxsize as close as possible?
670
0
        if (NS_MATHML_OPERATOR_MAXSIZE_IS_ABSOLUTE(mFlags)) {
671
0
          // there is an explicit value like maxsize="20pt"
672
0
          // try to maintain the aspect ratio of the char
673
0
          float aspect = mMaxSize / float(initialSize.ascent + initialSize.descent);
674
0
          container.ascent =
675
0
            std::min(container.ascent, nscoord(initialSize.ascent * aspect));
676
0
          container.descent =
677
0
            std::min(container.descent, nscoord(initialSize.descent * aspect));
678
0
          // below we use a type cast instead of a conversion to avoid a VC++ bug
679
0
          // see http://support.microsoft.com/support/kb/articles/Q115/7/05.ASP
680
0
          container.width =
681
0
            std::min(container.width, (nscoord)mMaxSize);
682
0
        }
683
0
        else { // multiplicative value
684
0
          container.ascent =
685
0
            std::min(container.ascent, nscoord(initialSize.ascent * mMaxSize));
686
0
          container.descent =
687
0
            std::min(container.descent, nscoord(initialSize.descent * mMaxSize));
688
0
          container.width =
689
0
            std::min(container.width, nscoord(initialSize.width * mMaxSize));
690
0
        }
691
0
692
0
        if (isVertical && !NS_MATHML_OPERATOR_IS_SYMMETRIC(mFlags)) {
693
0
          // re-adjust to align the char with the bottom of the initial container
694
0
          height = container.ascent + container.descent;
695
0
          container.descent = aContainerSize.descent;
696
0
          container.ascent = height - container.descent;
697
0
        }
698
0
      }
699
0
700
0
      if (mMinSize > 0.0f) {
701
0
        // if we are here, there is a user defined minsize ...
702
0
        // always allow the char to stretch in its natural direction,
703
0
        // even if it is different from the caller's direction
704
0
        if (aStretchDirection != NS_STRETCH_DIRECTION_DEFAULT &&
705
0
            aStretchDirection != mEmbellishData.direction) {
706
0
          aStretchDirection = NS_STRETCH_DIRECTION_DEFAULT;
707
0
          // but when we are not honoring the requested direction
708
0
          // we should not use the caller's container size either
709
0
          container = initialSize;
710
0
        }
711
0
        if (NS_MATHML_OPERATOR_MINSIZE_IS_ABSOLUTE(mFlags)) {
712
0
          // there is an explicit value like minsize="20pt"
713
0
          // try to maintain the aspect ratio of the char
714
0
          float aspect = mMinSize / float(initialSize.ascent + initialSize.descent);
715
0
          container.ascent =
716
0
            std::max(container.ascent, nscoord(initialSize.ascent * aspect));
717
0
          container.descent =
718
0
            std::max(container.descent, nscoord(initialSize.descent * aspect));
719
0
          container.width =
720
0
            std::max(container.width, (nscoord)mMinSize);
721
0
        }
722
0
        else { // multiplicative value
723
0
          container.ascent =
724
0
            std::max(container.ascent, nscoord(initialSize.ascent * mMinSize));
725
0
          container.descent =
726
0
            std::max(container.descent, nscoord(initialSize.descent * mMinSize));
727
0
          container.width =
728
0
            std::max(container.width, nscoord(initialSize.width * mMinSize));
729
0
        }
730
0
731
0
        if (isVertical && !NS_MATHML_OPERATOR_IS_SYMMETRIC(mFlags)) {
732
0
          // re-adjust to align the char with the bottom of the initial container
733
0
          height = container.ascent + container.descent;
734
0
          container.descent = aContainerSize.descent;
735
0
          container.ascent = height - container.descent;
736
0
        }
737
0
      }
738
0
    }
739
0
740
0
    // let the MathMLChar stretch itself...
741
0
    nsresult res = mMathMLChar.Stretch(this,
742
0
                                       aDrawTarget,
743
0
                                       fontSizeInflation,
744
0
                                       aStretchDirection, container, charSize,
745
0
                                       stretchHint,
746
0
                                       StyleVisibility()->mDirection);
747
0
    if (NS_FAILED(res)) {
748
0
      // gracefully handle cases where stretching the char failed (i.e., GetBoundingMetrics failed)
749
0
      // clear our 'form' to behave as if the operator wasn't in the dictionary
750
0
      mFlags &= ~NS_MATHML_OPERATOR_FORM;
751
0
      useMathMLChar = false;
752
0
    }
753
0
  }
754
0
755
0
  // Place our children using the default method
756
0
  // This will allow our child text frame to get its DidReflow()
757
0
  nsresult rv = Place(aDrawTarget, true, aDesiredStretchSize);
758
0
  if (NS_MATHML_HAS_ERROR(mPresentationData.flags) || NS_FAILED(rv)) {
759
0
    // Make sure the child frames get their DidReflow() calls.
760
0
    DidReflowChildren(mFrames.FirstChild());
761
0
  }
762
0
763
0
  if (useMathMLChar) {
764
0
    // update our bounding metrics... it becomes that of our MathML char
765
0
    mBoundingMetrics = charSize;
766
0
767
0
    // if the returned direction is 'unsupported', the char didn't actually change.
768
0
    // So we do the centering only if necessary
769
0
    if (mMathMLChar.GetStretchDirection() != NS_STRETCH_DIRECTION_UNSUPPORTED ||
770
0
        NS_MATHML_OPERATOR_IS_CENTERED(mFlags)) {
771
0
772
0
      bool largeopOnly =
773
0
        (NS_STRETCH_LARGEOP & stretchHint) != 0 &&
774
0
        (NS_STRETCH_VARIABLE_MASK & stretchHint) == 0;
775
0
776
0
      if (isVertical || NS_MATHML_OPERATOR_IS_CENTERED(mFlags)) {
777
0
        // the desired size returned by mMathMLChar maybe different
778
0
        // from the size of the container.
779
0
        // the mMathMLChar.mRect.y calculation is subtle, watch out!!!
780
0
781
0
        height = mBoundingMetrics.ascent + mBoundingMetrics.descent;
782
0
        if (NS_MATHML_OPERATOR_IS_SYMMETRIC(mFlags) ||
783
0
            NS_MATHML_OPERATOR_IS_CENTERED(mFlags)) {
784
0
          // For symmetric and vertical operators, or for operators that are always
785
0
          // centered ('+', '*', etc) we want to center about the axis of the container
786
0
          mBoundingMetrics.descent = height/2 - axisHeight;
787
0
        } else if (!largeopOnly) {
788
0
          // Align the center of the char with the center of the container
789
0
          mBoundingMetrics.descent = height/2 +
790
0
            (container.ascent + container.descent)/2 - container.ascent;
791
0
        } // else align the baselines
792
0
        mBoundingMetrics.ascent = height - mBoundingMetrics.descent;
793
0
      }
794
0
    }
795
0
  }
796
0
797
0
  // Fixup for the final height.
798
0
  // On one hand, our stretchy height can sometimes be shorter than surrounding
799
0
  // ASCII chars, e.g., arrow symbols have |mBoundingMetrics.ascent + leading|
800
0
  // that is smaller than the ASCII's ascent, hence when painting the background
801
0
  // later, it won't look uniform along the line.
802
0
  // On the other hand, sometimes we may leave too much gap when our glyph happens
803
0
  // to come from a font with tall glyphs. For example, since CMEX10 has very tall
804
0
  // glyphs, its natural font metrics are large, even if we pick a small glyph
805
0
  // whose size is comparable to the size of a normal ASCII glyph.
806
0
  // So to avoid uneven spacing in either of these two cases, we use the height
807
0
  // of the ASCII font as a reference and try to match it if possible.
808
0
809
0
  // special case for accents... keep them short to improve mouse operations...
810
0
  // an accent can only be the non-first child of <mover>, <munder>, <munderover>
811
0
  bool isAccent =
812
0
    NS_MATHML_EMBELLISH_IS_ACCENT(mEmbellishData.flags);
813
0
  if (isAccent) {
814
0
    nsEmbellishData parentData;
815
0
    GetEmbellishDataFrom(GetParent(), parentData);
816
0
    isAccent =
817
0
       (NS_MATHML_EMBELLISH_IS_ACCENTOVER(parentData.flags) ||
818
0
        NS_MATHML_EMBELLISH_IS_ACCENTUNDER(parentData.flags)) &&
819
0
       parentData.coreFrame != this;
820
0
  }
821
0
  if (isAccent && firstChild) {
822
0
    // see bug 188467 for what is going on here
823
0
    nscoord dy = aDesiredStretchSize.BlockStartAscent() -
824
0
      (mBoundingMetrics.ascent + leading);
825
0
    aDesiredStretchSize.SetBlockStartAscent(mBoundingMetrics.ascent + leading);
826
0
    aDesiredStretchSize.Height() = aDesiredStretchSize.BlockStartAscent() +
827
0
                                   mBoundingMetrics.descent;
828
0
829
0
    firstChild->SetPosition(firstChild->GetPosition() - nsPoint(0, dy));
830
0
  }
831
0
  else if (useMathMLChar) {
832
0
    nscoord ascent = fm->MaxAscent();
833
0
    nscoord descent = fm->MaxDescent();
834
0
    aDesiredStretchSize.SetBlockStartAscent(std::max(mBoundingMetrics.ascent + leading, ascent));
835
0
    aDesiredStretchSize.Height() = aDesiredStretchSize.BlockStartAscent() +
836
0
                                 std::max(mBoundingMetrics.descent + leading, descent);
837
0
  }
838
0
  aDesiredStretchSize.Width() = mBoundingMetrics.width;
839
0
  aDesiredStretchSize.mBoundingMetrics = mBoundingMetrics;
840
0
  mReference.x = 0;
841
0
  mReference.y = aDesiredStretchSize.BlockStartAscent();
842
0
  // Place our mMathMLChar, its origin is in our coordinate system
843
0
  if (useMathMLChar) {
844
0
    nscoord dy = aDesiredStretchSize.BlockStartAscent() - mBoundingMetrics.ascent;
845
0
    mMathMLChar.SetRect(nsRect(0, dy, charSize.width, charSize.ascent + charSize.descent));
846
0
  }
847
0
848
0
  // Before we leave... there is a last item in the check-list:
849
0
  // If our parent is not embellished, it means we are the outermost embellished
850
0
  // container and so we put the spacing, otherwise we don't include the spacing,
851
0
  // the outermost embellished container will take care of it.
852
0
853
0
  if (!NS_MATHML_OPERATOR_HAS_EMBELLISH_ANCESTOR(mFlags)) {
854
0
855
0
    // Account the spacing if we are not an accent with explicit attributes
856
0
    nscoord leadingSpace = mEmbellishData.leadingSpace;
857
0
    if (isAccent && !NS_MATHML_OPERATOR_HAS_LSPACE_ATTR(mFlags)) {
858
0
      leadingSpace = 0;
859
0
    }
860
0
    nscoord trailingSpace = mEmbellishData.trailingSpace;
861
0
    if (isAccent && !NS_MATHML_OPERATOR_HAS_RSPACE_ATTR(mFlags)) {
862
0
      trailingSpace = 0;
863
0
    }
864
0
865
0
    mBoundingMetrics.width += leadingSpace + trailingSpace;
866
0
    aDesiredStretchSize.Width() = mBoundingMetrics.width;
867
0
    aDesiredStretchSize.mBoundingMetrics.width = mBoundingMetrics.width;
868
0
869
0
    nscoord dx = (StyleVisibility()->mDirection ?
870
0
                  trailingSpace : leadingSpace);
871
0
    if (dx) {
872
0
      // adjust the offsets
873
0
      mBoundingMetrics.leftBearing += dx;
874
0
      mBoundingMetrics.rightBearing += dx;
875
0
      aDesiredStretchSize.mBoundingMetrics.leftBearing += dx;
876
0
      aDesiredStretchSize.mBoundingMetrics.rightBearing += dx;
877
0
878
0
      if (useMathMLChar) {
879
0
        nsRect rect;
880
0
        mMathMLChar.GetRect(rect);
881
0
        mMathMLChar.SetRect(nsRect(rect.x + dx, rect.y,
882
0
                                   rect.width, rect.height));
883
0
      }
884
0
      else {
885
0
        nsIFrame* childFrame = firstChild;
886
0
        while (childFrame) {
887
0
          childFrame->SetPosition(childFrame->GetPosition() +
888
0
                                  nsPoint(dx, 0));
889
0
          childFrame = childFrame->GetNextSibling();
890
0
        }
891
0
      }
892
0
    }
893
0
  }
894
0
895
0
  // Finished with these:
896
0
  ClearSavedChildMetrics();
897
0
  // Set our overflow area
898
0
  GatherAndStoreOverflow(&aDesiredStretchSize);
899
0
900
0
  // There used to be code here to change the height of the child frame to
901
0
  // change the caret height, but the text frame that manages the caret is now
902
0
  // not a direct child but wrapped in a block frame.  See also bug 412033.
903
0
904
0
  return NS_OK;
905
0
}
906
907
NS_IMETHODIMP
908
nsMathMLmoFrame::InheritAutomaticData(nsIFrame* aParent)
909
0
{
910
0
  // retain our native direction, it only changes if our text content changes
911
0
  nsStretchDirection direction = mEmbellishData.direction;
912
0
  nsMathMLTokenFrame::InheritAutomaticData(aParent);
913
0
  ProcessTextData();
914
0
  mEmbellishData.direction = direction;
915
0
  return NS_OK;
916
0
}
917
918
NS_IMETHODIMP
919
nsMathMLmoFrame::TransmitAutomaticData()
920
0
{
921
0
  // this will cause us to re-sync our flags from scratch
922
0
  // but our returned 'form' is still not final (bug 133429), it will
923
0
  // be recomputed to its final value during the next call in Reflow()
924
0
  mEmbellishData.coreFrame = nullptr;
925
0
  ProcessOperatorData();
926
0
  return NS_OK;
927
0
}
928
929
void
930
nsMathMLmoFrame::SetInitialChildList(ChildListID     aListID,
931
                                     nsFrameList&    aChildList)
932
0
{
933
0
  // First, let the parent class do its work
934
0
  nsMathMLTokenFrame::SetInitialChildList(aListID, aChildList);
935
0
  ProcessTextData();
936
0
}
937
938
void
939
nsMathMLmoFrame::Reflow(nsPresContext*          aPresContext,
940
                        ReflowOutput&     aDesiredSize,
941
                        const ReflowInput& aReflowInput,
942
                        nsReflowStatus&          aStatus)
943
0
{
944
0
  MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
945
0
946
0
  // certain values use units that depend on our ComputedStyle, so
947
0
  // it is safer to just process the whole lot here
948
0
  ProcessOperatorData();
949
0
950
0
  nsMathMLTokenFrame::Reflow(aPresContext, aDesiredSize,
951
0
                             aReflowInput, aStatus);
952
0
}
953
954
nsresult
955
nsMathMLmoFrame::Place(DrawTarget*          aDrawTarget,
956
                       bool                 aPlaceOrigin,
957
                       ReflowOutput& aDesiredSize)
958
0
{
959
0
  nsresult rv = nsMathMLTokenFrame::Place(aDrawTarget, aPlaceOrigin, aDesiredSize);
960
0
961
0
  if (NS_FAILED(rv)) {
962
0
    return rv;
963
0
  }
964
0
965
0
  /* Special behaviour for largeops.
966
0
     In MathML "stretchy" and displaystyle "largeop" are different notions,
967
0
     even if we use the same technique to draw them (picking size variants).
968
0
     So largeop display operators should be considered "non-stretchy" and
969
0
     thus their sizes should be taken into account for the stretch size of
970
0
     other elements.
971
0
972
0
     This is a preliminary stretch - exact sizing/placement is handled by the
973
0
     Stretch() method.
974
0
  */
975
0
976
0
  if (!aPlaceOrigin &&
977
0
      StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK &&
978
0
      NS_MATHML_OPERATOR_IS_LARGEOP(mFlags) && UseMathMLChar()) {
979
0
    nsBoundingMetrics newMetrics;
980
0
    rv = mMathMLChar.Stretch(this, aDrawTarget,
981
0
                             nsLayoutUtils::FontSizeInflationFor(this),
982
0
                             NS_STRETCH_DIRECTION_VERTICAL,
983
0
                             aDesiredSize.mBoundingMetrics, newMetrics,
984
0
                             NS_STRETCH_LARGEOP, StyleVisibility()->mDirection);
985
0
986
0
    if (NS_FAILED(rv)) {
987
0
      // Just use the initial size
988
0
      return NS_OK;
989
0
    }
990
0
991
0
    aDesiredSize.mBoundingMetrics = newMetrics;
992
0
     /* Treat the ascent/descent values calculated in the TokenFrame place
993
0
       calculations as the minimum for aDesiredSize calculations, rather
994
0
       than fetching them from font metrics again.
995
0
    */
996
0
    aDesiredSize.SetBlockStartAscent(std::max(mBoundingMetrics.ascent,
997
0
                                              newMetrics.ascent));
998
0
    aDesiredSize.Height() = aDesiredSize.BlockStartAscent() +
999
0
                            std::max(mBoundingMetrics.descent,
1000
0
                                     newMetrics.descent);
1001
0
    aDesiredSize.Width() = newMetrics.width;
1002
0
    mBoundingMetrics = newMetrics;
1003
0
  }
1004
0
  return NS_OK;
1005
0
}
1006
1007
/* virtual */ void
1008
nsMathMLmoFrame::MarkIntrinsicISizesDirty()
1009
0
{
1010
0
  // if we get this, it may mean that something changed in the text
1011
0
  // content. So blow away everything an re-build the automatic data
1012
0
  // from the parent of our outermost embellished container (we ensure
1013
0
  // that we are the core, not just a sibling of the core)
1014
0
1015
0
  ProcessTextData();
1016
0
1017
0
  nsIFrame* target = this;
1018
0
  nsEmbellishData embellishData;
1019
0
  do {
1020
0
    target = target->GetParent();
1021
0
    GetEmbellishDataFrom(target, embellishData);
1022
0
  } while (embellishData.coreFrame == this);
1023
0
1024
0
  // we have automatic data to update in the children of the target frame
1025
0
  // XXXldb This should really be marking dirty rather than rebuilding
1026
0
  // so that we don't rebuild multiple times for the same change.
1027
0
  RebuildAutomaticDataForChildren(target);
1028
0
1029
0
  nsMathMLContainerFrame::MarkIntrinsicISizesDirty();
1030
0
}
1031
1032
/* virtual */ void
1033
nsMathMLmoFrame::GetIntrinsicISizeMetrics(gfxContext* aRenderingContext,
1034
                                          ReflowOutput& aDesiredSize)
1035
0
{
1036
0
  ProcessOperatorData();
1037
0
  if (UseMathMLChar()) {
1038
0
    uint32_t stretchHint = GetStretchHint(mFlags, mPresentationData, true,
1039
0
                                          StyleFont());
1040
0
    aDesiredSize.Width() = mMathMLChar.
1041
0
      GetMaxWidth(this, aRenderingContext->GetDrawTarget(),
1042
0
                  nsLayoutUtils::FontSizeInflationFor(this),
1043
0
                  stretchHint);
1044
0
  }
1045
0
  else {
1046
0
    nsMathMLTokenFrame::GetIntrinsicISizeMetrics(aRenderingContext,
1047
0
                                                 aDesiredSize);
1048
0
  }
1049
0
1050
0
  // leadingSpace and trailingSpace are actually applied to the outermost
1051
0
  // embellished container but for determining total intrinsic width it should
1052
0
  // be safe to include it for the core here instead.
1053
0
  bool isRTL = StyleVisibility()->mDirection;
1054
0
  aDesiredSize.Width() +=
1055
0
    mEmbellishData.leadingSpace + mEmbellishData.trailingSpace;
1056
0
  aDesiredSize.mBoundingMetrics.width = aDesiredSize.Width();
1057
0
  if (isRTL) {
1058
0
    aDesiredSize.mBoundingMetrics.leftBearing += mEmbellishData.trailingSpace;
1059
0
    aDesiredSize.mBoundingMetrics.rightBearing += mEmbellishData.trailingSpace;
1060
0
  } else {
1061
0
    aDesiredSize.mBoundingMetrics.leftBearing += mEmbellishData.leadingSpace;
1062
0
    aDesiredSize.mBoundingMetrics.rightBearing += mEmbellishData.leadingSpace;
1063
0
  }
1064
0
}
1065
1066
nsresult
1067
nsMathMLmoFrame::AttributeChanged(int32_t         aNameSpaceID,
1068
                                  nsAtom*        aAttribute,
1069
                                  int32_t         aModType)
1070
0
{
1071
0
  // check if this is an attribute that can affect the embellished hierarchy
1072
0
  // in a significant way and re-layout the entire hierarchy.
1073
0
  if (nsGkAtoms::accent_ == aAttribute ||
1074
0
      nsGkAtoms::movablelimits_ == aAttribute) {
1075
0
1076
0
    // set the target as the parent of our outermost embellished container
1077
0
    // (we ensure that we are the core, not just a sibling of the core)
1078
0
    nsIFrame* target = this;
1079
0
    nsEmbellishData embellishData;
1080
0
    do {
1081
0
      target = target->GetParent();
1082
0
      GetEmbellishDataFrom(target, embellishData);
1083
0
    } while (embellishData.coreFrame == this);
1084
0
1085
0
    // we have automatic data to update in the children of the target frame
1086
0
    return ReLayoutChildren(target);
1087
0
  }
1088
0
1089
0
  return nsMathMLTokenFrame::
1090
0
         AttributeChanged(aNameSpaceID, aAttribute, aModType);
1091
0
}
1092
1093
// ----------------------
1094
// No need to track the ComputedStyle given to our MathML char.
1095
// the Style System will use these to pass the proper ComputedStyle to our MathMLChar
1096
ComputedStyle*
1097
nsMathMLmoFrame::GetAdditionalComputedStyle(int32_t aIndex) const
1098
0
{
1099
0
  switch (aIndex) {
1100
0
  case NS_MATHML_CHAR_STYLE_CONTEXT_INDEX:
1101
0
    return mMathMLChar.GetComputedStyle();
1102
0
  default:
1103
0
    return nullptr;
1104
0
  }
1105
0
}
1106
1107
void
1108
nsMathMLmoFrame::SetAdditionalComputedStyle(int32_t          aIndex,
1109
                                           ComputedStyle*  aComputedStyle)
1110
0
{
1111
0
  switch (aIndex) {
1112
0
  case NS_MATHML_CHAR_STYLE_CONTEXT_INDEX:
1113
0
    mMathMLChar.SetComputedStyle(aComputedStyle);
1114
0
    break;
1115
0
  }
1116
0
}