Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/layout/mathml/nsMathMLFrame.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 "nsMathMLFrame.h"
8
9
#include "gfxContext.h"
10
#include "gfxUtils.h"
11
#include "mozilla/gfx/2D.h"
12
#include "nsLayoutUtils.h"
13
#include "nsNameSpaceManager.h"
14
#include "nsMathMLChar.h"
15
#include "nsCSSPseudoElements.h"
16
#include "nsMathMLElement.h"
17
#include "gfxMathTable.h"
18
19
// used to map attributes into CSS rules
20
#include "mozilla/ServoStyleSet.h"
21
#include "nsDisplayList.h"
22
23
using namespace mozilla;
24
using namespace mozilla::gfx;
25
26
eMathMLFrameType
27
nsMathMLFrame::GetMathMLFrameType()
28
0
{
29
0
  // see if it is an embellished operator (mapped to 'Op' in TeX)
30
0
  if (mEmbellishData.coreFrame)
31
0
    return GetMathMLFrameTypeFor(mEmbellishData.coreFrame);
32
0
33
0
  // if it has a prescribed base, fetch the type from there
34
0
  if (mPresentationData.baseFrame)
35
0
    return GetMathMLFrameTypeFor(mPresentationData.baseFrame);
36
0
37
0
  // everything else is treated as ordinary (mapped to 'Ord' in TeX)
38
0
  return eMathMLFrameType_Ordinary;
39
0
}
40
41
NS_IMETHODIMP
42
nsMathMLFrame::InheritAutomaticData(nsIFrame* aParent)
43
0
{
44
0
  mEmbellishData.flags = 0;
45
0
  mEmbellishData.coreFrame = nullptr;
46
0
  mEmbellishData.direction = NS_STRETCH_DIRECTION_UNSUPPORTED;
47
0
  mEmbellishData.leadingSpace = 0;
48
0
  mEmbellishData.trailingSpace = 0;
49
0
50
0
  mPresentationData.flags = 0;
51
0
  mPresentationData.baseFrame = nullptr;
52
0
53
0
  // by default, just inherit the display of our parent
54
0
  nsPresentationData parentData;
55
0
  GetPresentationDataFrom(aParent, parentData);
56
0
57
#if defined(DEBUG) && defined(SHOW_BOUNDING_BOX)
58
  mPresentationData.flags |= NS_MATHML_SHOW_BOUNDING_METRICS;
59
#endif
60
61
0
  return NS_OK;
62
0
}
63
64
NS_IMETHODIMP
65
nsMathMLFrame::UpdatePresentationData(uint32_t        aFlagsValues,
66
                                      uint32_t        aWhichFlags)
67
0
{
68
0
  NS_ASSERTION(NS_MATHML_IS_COMPRESSED(aWhichFlags) ||
69
0
               NS_MATHML_IS_DTLS_SET(aWhichFlags),
70
0
               "aWhichFlags should only be compression or dtls flag");
71
0
72
0
  if (NS_MATHML_IS_COMPRESSED(aWhichFlags)) {
73
0
    // updating the compression flag is allowed
74
0
    if (NS_MATHML_IS_COMPRESSED(aFlagsValues)) {
75
0
      // 'compressed' means 'prime' style in App. G, TeXbook
76
0
      mPresentationData.flags |= NS_MATHML_COMPRESSED;
77
0
    }
78
0
    // no else. the flag is sticky. it retains its value once it is set
79
0
  }
80
0
  // These flags determine whether the dtls font feature settings should
81
0
  // be applied.
82
0
  if (NS_MATHML_IS_DTLS_SET(aWhichFlags)) {
83
0
    if (NS_MATHML_IS_DTLS_SET(aFlagsValues)) {
84
0
      mPresentationData.flags |= NS_MATHML_DTLS;
85
0
    } else {
86
0
      mPresentationData.flags &= ~NS_MATHML_DTLS;
87
0
    }
88
0
  }
89
0
  return NS_OK;
90
0
}
91
92
// Helper to give a ComputedStyle suitable for doing the stretching of
93
// a MathMLChar. Frame classes that use this should ensure that the
94
// extra leaf ComputedStyle given to the MathMLChars are accessible to
95
// the Style System via the Get/Set AdditionalComputedStyle() APIs.
96
/* static */ void
97
nsMathMLFrame::ResolveMathMLCharStyle(nsPresContext*  aPresContext,
98
                                      nsIContent*      aContent,
99
                                      ComputedStyle*  aParentComputedStyle,
100
                                      nsMathMLChar*    aMathMLChar)
101
0
{
102
0
  CSSPseudoElementType pseudoType =
103
0
    CSSPseudoElementType::mozMathAnonymous; // savings
104
0
  RefPtr<ComputedStyle> newComputedStyle;
105
0
  newComputedStyle = aPresContext->StyleSet()->
106
0
    ResolvePseudoElementStyle(aContent->AsElement(), pseudoType,
107
0
                              aParentComputedStyle, nullptr);
108
0
109
0
  aMathMLChar->SetComputedStyle(newComputedStyle);
110
0
}
111
112
/* static */ void
113
nsMathMLFrame::GetEmbellishDataFrom(nsIFrame*        aFrame,
114
                                    nsEmbellishData& aEmbellishData)
115
0
{
116
0
  // initialize OUT params
117
0
  aEmbellishData.flags = 0;
118
0
  aEmbellishData.coreFrame = nullptr;
119
0
  aEmbellishData.direction = NS_STRETCH_DIRECTION_UNSUPPORTED;
120
0
  aEmbellishData.leadingSpace = 0;
121
0
  aEmbellishData.trailingSpace = 0;
122
0
123
0
  if (aFrame && aFrame->IsFrameOfType(nsIFrame::eMathML)) {
124
0
    nsIMathMLFrame* mathMLFrame = do_QueryFrame(aFrame);
125
0
    if (mathMLFrame) {
126
0
      mathMLFrame->GetEmbellishData(aEmbellishData);
127
0
    }
128
0
  }
129
0
}
130
131
// helper to get the presentation data of a frame, by possibly walking up
132
// the frame hierarchy if we happen to be surrounded by non-MathML frames.
133
/* static */ void
134
nsMathMLFrame::GetPresentationDataFrom(nsIFrame*           aFrame,
135
                                       nsPresentationData& aPresentationData,
136
                                       bool                aClimbTree)
137
0
{
138
0
  // initialize OUT params
139
0
  aPresentationData.flags = 0;
140
0
  aPresentationData.baseFrame = nullptr;
141
0
142
0
  nsIFrame* frame = aFrame;
143
0
  while (frame) {
144
0
    if (frame->IsFrameOfType(nsIFrame::eMathML)) {
145
0
      nsIMathMLFrame* mathMLFrame = do_QueryFrame(frame);
146
0
      if (mathMLFrame) {
147
0
        mathMLFrame->GetPresentationData(aPresentationData);
148
0
        break;
149
0
      }
150
0
    }
151
0
    // stop if the caller doesn't want to lookup beyond the frame
152
0
    if (!aClimbTree) {
153
0
      break;
154
0
    }
155
0
    // stop if we reach the root <math> tag
156
0
    nsIContent* content = frame->GetContent();
157
0
    NS_ASSERTION(content || !frame->GetParent(), // no assert for the root
158
0
                 "dangling frame without a content node");
159
0
    if (!content)
160
0
      break;
161
0
162
0
    if (content->IsMathMLElement(nsGkAtoms::math)) {
163
0
      break;
164
0
    }
165
0
    frame = frame->GetParent();
166
0
  }
167
0
  NS_WARNING_ASSERTION(
168
0
    frame && frame->GetContent(),
169
0
    "bad MathML markup - could not find the top <math> element");
170
0
}
171
172
/* static */ void
173
nsMathMLFrame::GetRuleThickness(DrawTarget*    aDrawTarget,
174
                                nsFontMetrics* aFontMetrics,
175
                                nscoord&       aRuleThickness)
176
0
{
177
0
  nscoord xHeight = aFontMetrics->XHeight();
178
0
  char16_t overBar = 0x00AF;
179
0
  nsBoundingMetrics bm =
180
0
    nsLayoutUtils::AppUnitBoundsOfString(&overBar, 1, *aFontMetrics,
181
0
                                         aDrawTarget);
182
0
  aRuleThickness = bm.ascent + bm.descent;
183
0
  if (aRuleThickness <= 0 || aRuleThickness >= xHeight) {
184
0
    // fall-back to the other version
185
0
    GetRuleThickness(aFontMetrics, aRuleThickness);
186
0
  }
187
0
}
188
189
/* static */ void
190
nsMathMLFrame::GetAxisHeight(DrawTarget*    aDrawTarget,
191
                             nsFontMetrics* aFontMetrics,
192
                             nscoord&       aAxisHeight)
193
0
{
194
0
  gfxFont* mathFont = aFontMetrics->GetThebesFontGroup()->GetFirstMathFont();
195
0
  if (mathFont) {
196
0
    aAxisHeight =
197
0
      mathFont->MathTable()->Constant(gfxMathTable::AxisHeight,
198
0
                                      aFontMetrics->AppUnitsPerDevPixel());
199
0
    return;
200
0
  }
201
0
202
0
  nscoord xHeight = aFontMetrics->XHeight();
203
0
  char16_t minus = 0x2212; // not '-', but official Unicode minus sign
204
0
  nsBoundingMetrics bm =
205
0
    nsLayoutUtils::AppUnitBoundsOfString(&minus, 1, *aFontMetrics, aDrawTarget);
206
0
  aAxisHeight = bm.ascent - (bm.ascent + bm.descent)/2;
207
0
  if (aAxisHeight <= 0 || aAxisHeight >= xHeight) {
208
0
    // fall-back to the other version
209
0
    GetAxisHeight(aFontMetrics, aAxisHeight);
210
0
  }
211
0
}
212
213
/* static */ nscoord
214
nsMathMLFrame::CalcLength(nsPresContext*   aPresContext,
215
                          ComputedStyle*   aComputedStyle,
216
                          const nsCSSValue& aCSSValue,
217
                          float             aFontSizeInflation)
218
0
{
219
0
  NS_ASSERTION(aCSSValue.IsLengthUnit(), "not a length unit");
220
0
221
0
  if (aCSSValue.IsPixelLengthUnit()) {
222
0
    return aCSSValue.GetPixelLength();
223
0
  }
224
0
225
0
  nsCSSUnit unit = aCSSValue.GetUnit();
226
0
227
0
  if (eCSSUnit_EM == unit) {
228
0
    const nsStyleFont* font = aComputedStyle->StyleFont();
229
0
    return NSToCoordRound(aCSSValue.GetFloatValue() * (float)font->mFont.size);
230
0
  }
231
0
  else if (eCSSUnit_XHeight == unit) {
232
0
    aPresContext->SetUsesExChUnits(true);
233
0
    RefPtr<nsFontMetrics> fm = nsLayoutUtils::
234
0
      GetFontMetricsForComputedStyle(aComputedStyle,
235
0
                                     aPresContext,
236
0
                                     aFontSizeInflation);
237
0
    nscoord xHeight = fm->XHeight();
238
0
    return NSToCoordRound(aCSSValue.GetFloatValue() * (float)xHeight);
239
0
  }
240
0
241
0
  // MathML doesn't specify other CSS units such as rem or ch
242
0
  NS_ERROR("Unsupported unit");
243
0
  return 0;
244
0
}
245
246
/* static */ void
247
nsMathMLFrame::ParseNumericValue(const nsString&   aString,
248
                                 nscoord*          aLengthValue,
249
                                 uint32_t          aFlags,
250
                                 nsPresContext*    aPresContext,
251
                                 ComputedStyle*   aComputedStyle,
252
                                 float             aFontSizeInflation)
253
0
{
254
0
  nsCSSValue cssValue;
255
0
256
0
  if (!nsMathMLElement::ParseNumericValue(aString, cssValue, aFlags,
257
0
                                          aPresContext->Document())) {
258
0
    // Invalid attribute value. aLengthValue remains unchanged, so the default
259
0
    // length value is used.
260
0
    return;
261
0
  }
262
0
263
0
  nsCSSUnit unit = cssValue.GetUnit();
264
0
265
0
  if (unit == eCSSUnit_Percent || unit == eCSSUnit_Number) {
266
0
    // Relative units. A multiple of the default length value is used.
267
0
    *aLengthValue = NSToCoordRound(*aLengthValue * (unit == eCSSUnit_Percent ?
268
0
                                                    cssValue.GetPercentValue() :
269
0
                                                    cssValue.GetFloatValue()));
270
0
    return;
271
0
  }
272
0
273
0
  // Absolute units.
274
0
  *aLengthValue = CalcLength(aPresContext, aComputedStyle, cssValue,
275
0
                             aFontSizeInflation);
276
0
}
277
278
#if defined(DEBUG) && defined(SHOW_BOUNDING_BOX)
279
class nsDisplayMathMLBoundingMetrics final : public nsDisplayItem {
280
public:
281
  nsDisplayMathMLBoundingMetrics(nsDisplayListBuilder* aBuilder,
282
                                 nsIFrame* aFrame, const nsRect& aRect)
283
    : nsDisplayItem(aBuilder, aFrame), mRect(aRect) {
284
    MOZ_COUNT_CTOR(nsDisplayMathMLBoundingMetrics);
285
  }
286
#ifdef NS_BUILD_REFCNT_LOGGING
287
  virtual ~nsDisplayMathMLBoundingMetrics() {
288
    MOZ_COUNT_DTOR(nsDisplayMathMLBoundingMetrics);
289
  }
290
#endif
291
292
  virtual void Paint(nsDisplayListBuilder* aBuilder,
293
                     gfxContext* aCtx) override;
294
  NS_DISPLAY_DECL_NAME("MathMLBoundingMetrics", TYPE_MATHML_BOUNDING_METRICS)
295
private:
296
  nsRect    mRect;
297
};
298
299
void nsDisplayMathMLBoundingMetrics::Paint(nsDisplayListBuilder* aBuilder,
300
                                           gfxContext* aCtx)
301
{
302
  DrawTarget* drawTarget = aCtx->GetDrawTarget();
303
  Rect r = NSRectToRect(mRect + ToReferenceFrame(),
304
                        mFrame->PresContext()->AppUnitsPerDevPixel());
305
  ColorPattern blue(ToDeviceColor(Color(0.f, 0.f, 1.f, 1.f)));
306
  drawTarget->StrokeRect(r, blue);
307
}
308
309
void
310
nsMathMLFrame::DisplayBoundingMetrics(nsDisplayListBuilder* aBuilder,
311
                                      nsIFrame* aFrame, const nsPoint& aPt,
312
                                      const nsBoundingMetrics& aMetrics,
313
                                      const nsDisplayListSet& aLists) {
314
  if (!NS_MATHML_PAINT_BOUNDING_METRICS(mPresentationData.flags))
315
    return;
316
317
  nscoord x = aPt.x + aMetrics.leftBearing;
318
  nscoord y = aPt.y - aMetrics.ascent;
319
  nscoord w = aMetrics.rightBearing - aMetrics.leftBearing;
320
  nscoord h = aMetrics.ascent + aMetrics.descent;
321
322
  aLists.Content()->AppendToTop(
323
      MakeDisplayItem<nsDisplayMathMLBoundingMetrics>(aBuilder, aFrame, nsRect(x,y,w,h)));
324
}
325
#endif
326
327
class nsDisplayMathMLBar final : public nsDisplayItem
328
{
329
public:
330
  nsDisplayMathMLBar(nsDisplayListBuilder* aBuilder,
331
                     nsIFrame* aFrame, const nsRect& aRect, uint32_t aIndex)
332
0
    : nsDisplayItem(aBuilder, aFrame), mRect(aRect), mIndex(aIndex) {
333
0
    MOZ_COUNT_CTOR(nsDisplayMathMLBar);
334
0
  }
335
#ifdef NS_BUILD_REFCNT_LOGGING
336
  virtual ~nsDisplayMathMLBar() {
337
    MOZ_COUNT_DTOR(nsDisplayMathMLBar);
338
  }
339
#endif
340
341
0
  virtual uint32_t GetPerFrameKey() const override {
342
0
    return (mIndex << TYPE_BITS) | nsDisplayItem::GetPerFrameKey();
343
0
  }
344
345
  virtual void Paint(nsDisplayListBuilder* aBuilder,
346
                     gfxContext* aCtx) override;
347
  NS_DISPLAY_DECL_NAME("MathMLBar", TYPE_MATHML_BAR)
348
private:
349
  nsRect    mRect;
350
  uint32_t  mIndex;
351
};
352
353
void nsDisplayMathMLBar::Paint(nsDisplayListBuilder* aBuilder,
354
                               gfxContext* aCtx)
355
0
{
356
0
  // paint the bar with the current text color
357
0
  DrawTarget* drawTarget = aCtx->GetDrawTarget();
358
0
  Rect rect =
359
0
    NSRectToNonEmptySnappedRect(mRect + ToReferenceFrame(),
360
0
                                mFrame->PresContext()->AppUnitsPerDevPixel(),
361
0
                                *drawTarget);
362
0
  ColorPattern color(ToDeviceColor(
363
0
    mFrame->GetVisitedDependentColor(&nsStyleText::mWebkitTextFillColor)));
364
0
  drawTarget->FillRect(rect, color);
365
0
}
366
367
void
368
nsMathMLFrame::DisplayBar(nsDisplayListBuilder* aBuilder,
369
                          nsIFrame* aFrame, const nsRect& aRect,
370
                          const nsDisplayListSet& aLists,
371
0
                          uint32_t aIndex) {
372
0
  if (!aFrame->StyleVisibility()->IsVisible() || aRect.IsEmpty())
373
0
    return;
374
0
375
0
  aLists.Content()->AppendToTop(
376
0
    MakeDisplayItem<nsDisplayMathMLBar>(aBuilder, aFrame, aRect, aIndex));
377
0
}
378
379
void
380
nsMathMLFrame::GetRadicalParameters(nsFontMetrics* aFontMetrics,
381
                                    bool aDisplayStyle,
382
                                    nscoord& aRadicalRuleThickness,
383
                                    nscoord& aRadicalExtraAscender,
384
                                    nscoord& aRadicalVerticalGap)
385
0
{
386
0
  nscoord oneDevPixel = aFontMetrics->AppUnitsPerDevPixel();
387
0
  gfxFont* mathFont = aFontMetrics->GetThebesFontGroup()->GetFirstMathFont();
388
0
389
0
  // get the radical rulethickness
390
0
  if (mathFont) {
391
0
    aRadicalRuleThickness = mathFont->MathTable()->
392
0
      Constant(gfxMathTable::RadicalRuleThickness, oneDevPixel);
393
0
  } else {
394
0
    GetRuleThickness(aFontMetrics, aRadicalRuleThickness);
395
0
  }
396
0
397
0
  // get the leading to be left at the top of the resulting frame
398
0
  if (mathFont) {
399
0
    aRadicalExtraAscender = mathFont->MathTable()->
400
0
      Constant(gfxMathTable::RadicalExtraAscender, oneDevPixel);
401
0
  } else {
402
0
    // This seems more reliable than using aFontMetrics->GetLeading() on
403
0
    // suspicious fonts.
404
0
    nscoord em;
405
0
    GetEmHeight(aFontMetrics, em);
406
0
    aRadicalExtraAscender = nscoord(0.2f * em);
407
0
  }
408
0
409
0
  // get the clearance between rule and content
410
0
  if (mathFont) {
411
0
    aRadicalVerticalGap = mathFont->MathTable()->
412
0
      Constant(aDisplayStyle ?
413
0
               gfxMathTable::RadicalDisplayStyleVerticalGap :
414
0
               gfxMathTable::RadicalVerticalGap,
415
0
               oneDevPixel);
416
0
  } else {
417
0
    // Rule 11, App. G, TeXbook
418
0
    aRadicalVerticalGap = aRadicalRuleThickness +
419
0
      (aDisplayStyle ? aFontMetrics->XHeight() : aRadicalRuleThickness) / 4;
420
0
  }
421
0
}