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/. */
7 #include "nsMathMLFrame.h"
9 #include "gfxContext.h"
11 #include "mozilla/gfx/2D.h"
12 #include "nsCSSValue.h"
13 #include "nsLayoutUtils.h"
14 #include "nsNameSpaceManager.h"
15 #include "nsMathMLChar.h"
16 #include "nsCSSPseudoElements.h"
17 #include "mozilla/dom/MathMLElement.h"
18 #include "gfxMathTable.h"
19 #include "nsPresContextInlines.h"
21 // used to map attributes into CSS rules
22 #include "mozilla/ServoStyleSet.h"
23 #include "nsDisplayList.h"
25 using namespace mozilla
;
26 using namespace mozilla::gfx
;
28 eMathMLFrameType
nsMathMLFrame::GetMathMLFrameType() {
29 // see if it is an embellished operator (mapped to 'Op' in TeX)
30 if (mEmbellishData
.coreFrame
)
31 return GetMathMLFrameTypeFor(mEmbellishData
.coreFrame
);
33 // if it has a prescribed base, fetch the type from there
34 if (mPresentationData
.baseFrame
)
35 return GetMathMLFrameTypeFor(mPresentationData
.baseFrame
);
37 // everything else is treated as ordinary (mapped to 'Ord' in TeX)
38 return eMathMLFrameType_Ordinary
;
42 nsMathMLFrame::InheritAutomaticData(nsIFrame
* aParent
) {
43 mEmbellishData
.flags
= 0;
44 mEmbellishData
.coreFrame
= nullptr;
45 mEmbellishData
.direction
= NS_STRETCH_DIRECTION_UNSUPPORTED
;
46 mEmbellishData
.leadingSpace
= 0;
47 mEmbellishData
.trailingSpace
= 0;
49 mPresentationData
.flags
= 0;
50 mPresentationData
.baseFrame
= nullptr;
52 // by default, just inherit the display of our parent
53 nsPresentationData parentData
;
54 GetPresentationDataFrom(aParent
, parentData
);
56 #if defined(DEBUG) && defined(SHOW_BOUNDING_BOX)
57 mPresentationData
.flags
|= NS_MATHML_SHOW_BOUNDING_METRICS
;
64 nsMathMLFrame::UpdatePresentationData(uint32_t aFlagsValues
,
65 uint32_t aWhichFlags
) {
66 NS_ASSERTION(NS_MATHML_IS_COMPRESSED(aWhichFlags
) ||
67 NS_MATHML_IS_DTLS_SET(aWhichFlags
),
68 "aWhichFlags should only be compression or dtls flag");
70 if (NS_MATHML_IS_COMPRESSED(aWhichFlags
)) {
71 // updating the compression flag is allowed
72 if (NS_MATHML_IS_COMPRESSED(aFlagsValues
)) {
73 // 'compressed' means 'prime' style in App. G, TeXbook
74 mPresentationData
.flags
|= NS_MATHML_COMPRESSED
;
76 // no else. the flag is sticky. it retains its value once it is set
78 // These flags determine whether the dtls font feature settings should
80 if (NS_MATHML_IS_DTLS_SET(aWhichFlags
)) {
81 if (NS_MATHML_IS_DTLS_SET(aFlagsValues
)) {
82 mPresentationData
.flags
|= NS_MATHML_DTLS
;
84 mPresentationData
.flags
&= ~NS_MATHML_DTLS
;
91 void nsMathMLFrame::GetEmbellishDataFrom(nsIFrame
* aFrame
,
92 nsEmbellishData
& aEmbellishData
) {
93 // initialize OUT params
94 aEmbellishData
.flags
= 0;
95 aEmbellishData
.coreFrame
= nullptr;
96 aEmbellishData
.direction
= NS_STRETCH_DIRECTION_UNSUPPORTED
;
97 aEmbellishData
.leadingSpace
= 0;
98 aEmbellishData
.trailingSpace
= 0;
100 if (aFrame
&& aFrame
->IsFrameOfType(nsIFrame::eMathML
)) {
101 nsIMathMLFrame
* mathMLFrame
= do_QueryFrame(aFrame
);
103 mathMLFrame
->GetEmbellishData(aEmbellishData
);
108 // helper to get the presentation data of a frame, by possibly walking up
109 // the frame hierarchy if we happen to be surrounded by non-MathML frames.
111 void nsMathMLFrame::GetPresentationDataFrom(
112 nsIFrame
* aFrame
, nsPresentationData
& aPresentationData
, bool aClimbTree
) {
113 // initialize OUT params
114 aPresentationData
.flags
= 0;
115 aPresentationData
.baseFrame
= nullptr;
117 nsIFrame
* frame
= aFrame
;
119 if (frame
->IsFrameOfType(nsIFrame::eMathML
)) {
120 nsIMathMLFrame
* mathMLFrame
= do_QueryFrame(frame
);
122 mathMLFrame
->GetPresentationData(aPresentationData
);
126 // stop if the caller doesn't want to lookup beyond the frame
130 // stop if we reach the root <math> tag
131 nsIContent
* content
= frame
->GetContent();
132 NS_ASSERTION(content
|| !frame
->GetParent(), // no assert for the root
133 "dangling frame without a content node");
136 if (content
->IsMathMLElement(nsGkAtoms::math
)) {
139 frame
= frame
->GetParent();
141 NS_WARNING_ASSERTION(
142 frame
&& frame
->GetContent(),
143 "bad MathML markup - could not find the top <math> element");
147 void nsMathMLFrame::GetRuleThickness(DrawTarget
* aDrawTarget
,
148 nsFontMetrics
* aFontMetrics
,
149 nscoord
& aRuleThickness
) {
150 nscoord xHeight
= aFontMetrics
->XHeight();
151 char16_t overBar
= 0x00AF;
152 nsBoundingMetrics bm
= nsLayoutUtils::AppUnitBoundsOfString(
153 &overBar
, 1, *aFontMetrics
, aDrawTarget
);
154 aRuleThickness
= bm
.ascent
+ bm
.descent
;
155 if (aRuleThickness
<= 0 || aRuleThickness
>= xHeight
) {
156 // fall-back to the other version
157 GetRuleThickness(aFontMetrics
, aRuleThickness
);
162 void nsMathMLFrame::GetAxisHeight(DrawTarget
* aDrawTarget
,
163 nsFontMetrics
* aFontMetrics
,
164 nscoord
& aAxisHeight
) {
165 RefPtr
<gfxFont
> mathFont
=
166 aFontMetrics
->GetThebesFontGroup()->GetFirstMathFont();
168 aAxisHeight
= mathFont
->MathTable()->Constant(
169 gfxMathTable::AxisHeight
, aFontMetrics
->AppUnitsPerDevPixel());
173 nscoord xHeight
= aFontMetrics
->XHeight();
174 char16_t minus
= 0x2212; // not '-', but official Unicode minus sign
175 nsBoundingMetrics bm
= nsLayoutUtils::AppUnitBoundsOfString(
176 &minus
, 1, *aFontMetrics
, aDrawTarget
);
177 aAxisHeight
= bm
.ascent
- (bm
.ascent
+ bm
.descent
) / 2;
178 if (aAxisHeight
<= 0 || aAxisHeight
>= xHeight
) {
179 // fall-back to the other version
180 GetAxisHeight(aFontMetrics
, aAxisHeight
);
185 nscoord
nsMathMLFrame::CalcLength(nsPresContext
* aPresContext
,
186 ComputedStyle
* aComputedStyle
,
187 const nsCSSValue
& aCSSValue
,
188 float aFontSizeInflation
) {
189 NS_ASSERTION(aCSSValue
.IsLengthUnit(), "not a length unit");
191 if (aCSSValue
.IsPixelLengthUnit()) {
192 return aCSSValue
.GetPixelLength();
195 nsCSSUnit unit
= aCSSValue
.GetUnit();
197 if (eCSSUnit_EM
== unit
) {
198 const nsStyleFont
* font
= aComputedStyle
->StyleFont();
199 return font
->mFont
.size
.ScaledBy(aCSSValue
.GetFloatValue()).ToAppUnits();
202 if (eCSSUnit_XHeight
== unit
) {
203 RefPtr
<nsFontMetrics
> fm
= nsLayoutUtils::GetFontMetricsForComputedStyle(
204 aComputedStyle
, aPresContext
, aFontSizeInflation
);
205 nscoord xHeight
= fm
->XHeight();
206 return NSToCoordRound(aCSSValue
.GetFloatValue() * (float)xHeight
);
209 // MathML doesn't specify other CSS units such as rem or ch
210 NS_ERROR("Unsupported unit");
215 void nsMathMLFrame::GetSubDropFromChild(nsIFrame
* aChild
, nscoord
& aSubDrop
,
216 float aFontSizeInflation
) {
217 RefPtr
<nsFontMetrics
> fm
=
218 nsLayoutUtils::GetFontMetricsForFrame(aChild
, aFontSizeInflation
);
219 GetSubDrop(fm
, aSubDrop
);
223 void nsMathMLFrame::GetSupDropFromChild(nsIFrame
* aChild
, nscoord
& aSupDrop
,
224 float aFontSizeInflation
) {
225 RefPtr
<nsFontMetrics
> fm
=
226 nsLayoutUtils::GetFontMetricsForFrame(aChild
, aFontSizeInflation
);
227 GetSupDrop(fm
, aSupDrop
);
231 void nsMathMLFrame::ParseNumericValue(const nsString
& aString
,
232 nscoord
* aLengthValue
, uint32_t aFlags
,
233 nsPresContext
* aPresContext
,
234 ComputedStyle
* aComputedStyle
,
235 float aFontSizeInflation
) {
238 if (!dom::MathMLElement::ParseNumericValue(aString
, cssValue
, aFlags
,
239 aPresContext
->Document())) {
240 // Invalid attribute value. aLengthValue remains unchanged, so the default
241 // length value is used.
245 nsCSSUnit unit
= cssValue
.GetUnit();
247 if (unit
== eCSSUnit_Percent
|| unit
== eCSSUnit_Number
) {
248 // Relative units. A multiple of the default length value is used.
249 *aLengthValue
= NSToCoordRound(
250 *aLengthValue
* (unit
== eCSSUnit_Percent
? cssValue
.GetPercentValue()
251 : cssValue
.GetFloatValue()));
257 CalcLength(aPresContext
, aComputedStyle
, cssValue
, aFontSizeInflation
);
261 #if defined(DEBUG) && defined(SHOW_BOUNDING_BOX)
262 class nsDisplayMathMLBoundingMetrics final
: public nsDisplayItem
{
264 nsDisplayMathMLBoundingMetrics(nsDisplayListBuilder
* aBuilder
,
265 nsIFrame
* aFrame
, const nsRect
& aRect
)
266 : nsDisplayItem(aBuilder
, aFrame
), mRect(aRect
) {
267 MOZ_COUNT_CTOR(nsDisplayMathMLBoundingMetrics
);
269 MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayMathMLBoundingMetrics
)
271 virtual void Paint(nsDisplayListBuilder
* aBuilder
, gfxContext
* aCtx
) override
;
272 NS_DISPLAY_DECL_NAME("MathMLBoundingMetrics", TYPE_MATHML_BOUNDING_METRICS
)
277 void nsDisplayMathMLBoundingMetrics::Paint(nsDisplayListBuilder
* aBuilder
,
279 DrawTarget
* drawTarget
= aCtx
->GetDrawTarget();
280 Rect r
= NSRectToRect(mRect
+ ToReferenceFrame(),
281 mFrame
->PresContext()->AppUnitsPerDevPixel());
282 ColorPattern
blue(ToDeviceColor(Color(0.f
, 0.f
, 1.f
, 1.f
)));
283 drawTarget
->StrokeRect(r
, blue
);
286 void nsMathMLFrame::DisplayBoundingMetrics(nsDisplayListBuilder
* aBuilder
,
287 nsIFrame
* aFrame
, const nsPoint
& aPt
,
288 const nsBoundingMetrics
& aMetrics
,
289 const nsDisplayListSet
& aLists
) {
290 if (!NS_MATHML_PAINT_BOUNDING_METRICS(mPresentationData
.flags
)) return;
292 nscoord x
= aPt
.x
+ aMetrics
.leftBearing
;
293 nscoord y
= aPt
.y
- aMetrics
.ascent
;
294 nscoord w
= aMetrics
.rightBearing
- aMetrics
.leftBearing
;
295 nscoord h
= aMetrics
.ascent
+ aMetrics
.descent
;
297 aLists
.Content()->AppendNewToTop
<nsDisplayMathMLBoundingMetrics
>(
298 aBuilder
, aFrame
, nsRect(x
, y
, w
, h
));
302 class nsDisplayMathMLBar final
: public nsPaintedDisplayItem
{
304 nsDisplayMathMLBar(nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
,
306 : nsPaintedDisplayItem(aBuilder
, aFrame
), mRect(aRect
) {
307 MOZ_COUNT_CTOR(nsDisplayMathMLBar
);
309 MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayMathMLBar
)
311 virtual void Paint(nsDisplayListBuilder
* aBuilder
, gfxContext
* aCtx
) override
;
312 NS_DISPLAY_DECL_NAME("MathMLBar", TYPE_MATHML_BAR
)
318 void nsDisplayMathMLBar::Paint(nsDisplayListBuilder
* aBuilder
,
320 // paint the bar with the current text color
321 DrawTarget
* drawTarget
= aCtx
->GetDrawTarget();
322 Rect rect
= NSRectToNonEmptySnappedRect(
323 mRect
+ ToReferenceFrame(), mFrame
->PresContext()->AppUnitsPerDevPixel(),
325 ColorPattern
color(ToDeviceColor(
326 mFrame
->GetVisitedDependentColor(&nsStyleText::mWebkitTextFillColor
)));
327 drawTarget
->FillRect(rect
, color
);
330 } // namespace mozilla
332 void nsMathMLFrame::DisplayBar(nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
,
334 const nsDisplayListSet
& aLists
,
336 if (!aFrame
->StyleVisibility()->IsVisible() || aRect
.IsEmpty()) return;
338 aLists
.Content()->AppendNewToTopWithIndex
<nsDisplayMathMLBar
>(
339 aBuilder
, aFrame
, aIndex
, aRect
);
342 void nsMathMLFrame::GetRadicalParameters(nsFontMetrics
* aFontMetrics
,
344 nscoord
& aRadicalRuleThickness
,
345 nscoord
& aRadicalExtraAscender
,
346 nscoord
& aRadicalVerticalGap
) {
347 nscoord oneDevPixel
= aFontMetrics
->AppUnitsPerDevPixel();
348 RefPtr
<gfxFont
> mathFont
=
349 aFontMetrics
->GetThebesFontGroup()->GetFirstMathFont();
351 // get the radical rulethickness
353 aRadicalRuleThickness
= mathFont
->MathTable()->Constant(
354 gfxMathTable::RadicalRuleThickness
, oneDevPixel
);
356 GetRuleThickness(aFontMetrics
, aRadicalRuleThickness
);
359 // get the leading to be left at the top of the resulting frame
361 aRadicalExtraAscender
= mathFont
->MathTable()->Constant(
362 gfxMathTable::RadicalExtraAscender
, oneDevPixel
);
364 // This seems more reliable than using aFontMetrics->GetLeading() on
367 GetEmHeight(aFontMetrics
, em
);
368 aRadicalExtraAscender
= nscoord(0.2f
* em
);
371 // get the clearance between rule and content
373 aRadicalVerticalGap
= mathFont
->MathTable()->Constant(
374 aDisplayStyle
? gfxMathTable::RadicalDisplayStyleVerticalGap
375 : gfxMathTable::RadicalVerticalGap
,
378 // Rule 11, App. G, TeXbook
379 aRadicalVerticalGap
=
380 aRadicalRuleThickness
+
381 (aDisplayStyle
? aFontMetrics
->XHeight() : aRadicalRuleThickness
) / 4;