1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsMathMLFrame.h"
9 #include "mozilla/gfx/2D.h"
10 #include "nsLayoutUtils.h"
11 #include "nsNameSpaceManager.h"
12 #include "nsMathMLChar.h"
13 #include "nsCSSPseudoElements.h"
14 #include "nsMathMLElement.h"
16 // used to map attributes into CSS rules
17 #include "nsStyleSet.h"
18 #include "nsAutoPtr.h"
19 #include "nsDisplayList.h"
20 #include "nsRenderingContext.h"
22 using namespace mozilla
;
23 using namespace mozilla::gfx
;
26 nsMathMLFrame::GetMathMLFrameType()
28 // see if it is an embellished operator (mapped to 'Op' in TeX)
29 if (mEmbellishData
.coreFrame
)
30 return GetMathMLFrameTypeFor(mEmbellishData
.coreFrame
);
32 // if it has a prescribed base, fetch the type from there
33 if (mPresentationData
.baseFrame
)
34 return GetMathMLFrameTypeFor(mPresentationData
.baseFrame
);
36 // everything else is treated as ordinary (mapped to 'Ord' in TeX)
37 return eMathMLFrameType_Ordinary
;
41 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
,
67 NS_ASSERTION(NS_MATHML_IS_COMPRESSED(aWhichFlags
) ||
68 NS_MATHML_IS_DTLS_SET(aWhichFlags
),
69 "aWhichFlags should only be compression or dtls flag");
71 if (NS_MATHML_IS_COMPRESSED(aWhichFlags
)) {
72 // updating the compression flag is allowed
73 if (NS_MATHML_IS_COMPRESSED(aFlagsValues
)) {
74 // 'compressed' means 'prime' style in App. G, TeXbook
75 mPresentationData
.flags
|= NS_MATHML_COMPRESSED
;
77 // no else. the flag is sticky. it retains its value once it is set
79 // These flags determine whether the dtls font feature settings should
81 if (NS_MATHML_IS_DTLS_SET(aWhichFlags
)) {
82 if (NS_MATHML_IS_DTLS_SET(aFlagsValues
)) {
83 mPresentationData
.flags
|= NS_MATHML_DTLS
;
85 mPresentationData
.flags
&= ~NS_MATHML_DTLS
;
91 // Helper to give a style context suitable for doing the stretching of
92 // a MathMLChar. Frame classes that use this should ensure that the
93 // extra leaf style contexts given to the MathMLChars are accessible to
94 // the Style System via the Get/Set AdditionalStyleContext() APIs.
96 nsMathMLFrame::ResolveMathMLCharStyle(nsPresContext
* aPresContext
,
98 nsStyleContext
* aParentStyleContext
,
99 nsMathMLChar
* aMathMLChar
)
101 nsCSSPseudoElements::Type pseudoType
=
102 nsCSSPseudoElements::ePseudo_mozMathAnonymous
; // savings
103 RefPtr
<nsStyleContext
> newStyleContext
;
104 newStyleContext
= aPresContext
->StyleSet()->
105 ResolvePseudoElementStyle(aContent
->AsElement(), pseudoType
,
106 aParentStyleContext
, nullptr);
108 aMathMLChar
->SetStyleContext(newStyleContext
);
112 nsMathMLFrame::GetEmbellishDataFrom(nsIFrame
* aFrame
,
113 nsEmbellishData
& aEmbellishData
)
115 // initialize OUT params
116 aEmbellishData
.flags
= 0;
117 aEmbellishData
.coreFrame
= nullptr;
118 aEmbellishData
.direction
= NS_STRETCH_DIRECTION_UNSUPPORTED
;
119 aEmbellishData
.leadingSpace
= 0;
120 aEmbellishData
.trailingSpace
= 0;
122 if (aFrame
&& aFrame
->IsFrameOfType(nsIFrame::eMathML
)) {
123 nsIMathMLFrame
* mathMLFrame
= do_QueryFrame(aFrame
);
125 mathMLFrame
->GetEmbellishData(aEmbellishData
);
130 // helper to get the presentation data of a frame, by possibly walking up
131 // the frame hierarchy if we happen to be surrounded by non-MathML frames.
133 nsMathMLFrame::GetPresentationDataFrom(nsIFrame
* aFrame
,
134 nsPresentationData
& aPresentationData
,
137 // initialize OUT params
138 aPresentationData
.flags
= 0;
139 aPresentationData
.baseFrame
= nullptr;
141 nsIFrame
* frame
= aFrame
;
143 if (frame
->IsFrameOfType(nsIFrame::eMathML
)) {
144 nsIMathMLFrame
* mathMLFrame
= do_QueryFrame(frame
);
146 mathMLFrame
->GetPresentationData(aPresentationData
);
150 // stop if the caller doesn't want to lookup beyond the frame
154 // stop if we reach the root <math> tag
155 nsIContent
* content
= frame
->GetContent();
156 NS_ASSERTION(content
|| !frame
->GetParent(), // no assert for the root
157 "dangling frame without a content node");
161 if (content
->IsMathMLElement(nsGkAtoms::math
)) {
164 frame
= frame
->GetParent();
166 NS_WARN_IF_FALSE(frame
&& frame
->GetContent(),
167 "bad MathML markup - could not find the top <math> element");
171 nsMathMLFrame::GetRuleThickness(DrawTarget
* aDrawTarget
,
172 nsFontMetrics
* aFontMetrics
,
173 nscoord
& aRuleThickness
)
175 nscoord xHeight
= aFontMetrics
->XHeight();
176 char16_t overBar
= 0x00AF;
177 nsBoundingMetrics bm
=
178 nsLayoutUtils::AppUnitBoundsOfString(&overBar
, 1, *aFontMetrics
,
180 aRuleThickness
= bm
.ascent
+ bm
.descent
;
181 if (aRuleThickness
<= 0 || aRuleThickness
>= xHeight
) {
182 // fall-back to the other version
183 GetRuleThickness(aFontMetrics
, aRuleThickness
);
188 nsMathMLFrame::GetAxisHeight(DrawTarget
* aDrawTarget
,
189 nsFontMetrics
* aFontMetrics
,
190 nscoord
& aAxisHeight
)
192 gfxFont
* mathFont
= aFontMetrics
->GetThebesFontGroup()->GetFirstMathFont();
195 mathFont
->GetMathConstant(gfxFontEntry::AxisHeight
,
196 aFontMetrics
->AppUnitsPerDevPixel());
200 nscoord xHeight
= aFontMetrics
->XHeight();
201 char16_t minus
= 0x2212; // not '-', but official Unicode minus sign
202 nsBoundingMetrics bm
=
203 nsLayoutUtils::AppUnitBoundsOfString(&minus
, 1, *aFontMetrics
, aDrawTarget
);
204 aAxisHeight
= bm
.ascent
- (bm
.ascent
+ bm
.descent
)/2;
205 if (aAxisHeight
<= 0 || aAxisHeight
>= xHeight
) {
206 // fall-back to the other version
207 GetAxisHeight(aFontMetrics
, aAxisHeight
);
212 nsMathMLFrame::CalcLength(nsPresContext
* aPresContext
,
213 nsStyleContext
* aStyleContext
,
214 const nsCSSValue
& aCSSValue
,
215 float aFontSizeInflation
)
217 NS_ASSERTION(aCSSValue
.IsLengthUnit(), "not a length unit");
219 if (aCSSValue
.IsFixedLengthUnit()) {
220 return aCSSValue
.GetFixedLength(aPresContext
);
222 if (aCSSValue
.IsPixelLengthUnit()) {
223 return aCSSValue
.GetPixelLength();
226 nsCSSUnit unit
= aCSSValue
.GetUnit();
228 if (eCSSUnit_EM
== unit
) {
229 const nsStyleFont
* font
= aStyleContext
->StyleFont();
230 return NSToCoordRound(aCSSValue
.GetFloatValue() * (float)font
->mFont
.size
);
232 else if (eCSSUnit_XHeight
== unit
) {
233 aPresContext
->SetUsesExChUnits(true);
234 RefPtr
<nsFontMetrics
> fm
;
235 nsLayoutUtils::GetFontMetricsForStyleContext(aStyleContext
,
238 nscoord xHeight
= fm
->XHeight();
239 return NSToCoordRound(aCSSValue
.GetFloatValue() * (float)xHeight
);
242 // MathML doesn't specify other CSS units such as rem or ch
243 NS_ERROR("Unsupported unit");
248 nsMathMLFrame::ParseNumericValue(const nsString
& aString
,
249 nscoord
* aLengthValue
,
251 nsPresContext
* aPresContext
,
252 nsStyleContext
* aStyleContext
,
253 float aFontSizeInflation
)
257 if (!nsMathMLElement::ParseNumericValue(aString
, cssValue
, aFlags
,
258 aPresContext
->Document())) {
259 // Invalid attribute value. aLengthValue remains unchanged, so the default
260 // length value is used.
264 nsCSSUnit unit
= cssValue
.GetUnit();
266 if (unit
== eCSSUnit_Percent
|| unit
== eCSSUnit_Number
) {
267 // Relative units. A multiple of the default length value is used.
268 *aLengthValue
= NSToCoordRound(*aLengthValue
* (unit
== eCSSUnit_Percent
?
269 cssValue
.GetPercentValue() :
270 cssValue
.GetFloatValue()));
275 *aLengthValue
= CalcLength(aPresContext
, aStyleContext
, cssValue
,
280 // Utils to map attributes into CSS rules (work-around to bug 69409 which
281 // is not scheduled to be fixed anytime soon)
286 int32_t compatibility
;
287 const nsIAtom
* attrAtom
;
288 const char* cssProperty
;
291 #if defined(DEBUG) && defined(SHOW_BOUNDING_BOX)
292 class nsDisplayMathMLBoundingMetrics
: public nsDisplayItem
{
294 nsDisplayMathMLBoundingMetrics(nsDisplayListBuilder
* aBuilder
,
295 nsIFrame
* aFrame
, const nsRect
& aRect
)
296 : nsDisplayItem(aBuilder
, aFrame
), mRect(aRect
) {
297 MOZ_COUNT_CTOR(nsDisplayMathMLBoundingMetrics
);
299 #ifdef NS_BUILD_REFCNT_LOGGING
300 virtual ~nsDisplayMathMLBoundingMetrics() {
301 MOZ_COUNT_DTOR(nsDisplayMathMLBoundingMetrics
);
305 virtual void Paint(nsDisplayListBuilder
* aBuilder
,
306 nsRenderingContext
* aCtx
) override
;
307 NS_DISPLAY_DECL_NAME("MathMLBoundingMetrics", TYPE_MATHML_BOUNDING_METRICS
)
312 void nsDisplayMathMLBoundingMetrics::Paint(nsDisplayListBuilder
* aBuilder
,
313 nsRenderingContext
* aCtx
)
315 DrawTarget
* drawTarget
= aRenderingContext
->GetDrawTarget();
316 Rect r
= NSRectToRect(mRect
+ ToReferenceFrame(),
317 mFrame
->PresContext()->AppUnitsPerDevPixel());
318 ColorPattern
blue(ToDeviceColor(Color(0.f
, 0.f
, 1.f
, 1.f
)));
319 drawTarget
->StrokeRect(r
, blue
);
323 nsMathMLFrame::DisplayBoundingMetrics(nsDisplayListBuilder
* aBuilder
,
324 nsIFrame
* aFrame
, const nsPoint
& aPt
,
325 const nsBoundingMetrics
& aMetrics
,
326 const nsDisplayListSet
& aLists
) {
327 if (!NS_MATHML_PAINT_BOUNDING_METRICS(mPresentationData
.flags
))
330 nscoord x
= aPt
.x
+ aMetrics
.leftBearing
;
331 nscoord y
= aPt
.y
- aMetrics
.ascent
;
332 nscoord w
= aMetrics
.rightBearing
- aMetrics
.leftBearing
;
333 nscoord h
= aMetrics
.ascent
+ aMetrics
.descent
;
335 return aLists
.Content()->AppendNewToTop(new (aBuilder
)
336 nsDisplayMathMLBoundingMetrics(aBuilder
, this, nsRect(x
,y
,w
,h
)));
340 class nsDisplayMathMLBar
: public nsDisplayItem
{
342 nsDisplayMathMLBar(nsDisplayListBuilder
* aBuilder
,
343 nsIFrame
* aFrame
, const nsRect
& aRect
)
344 : nsDisplayItem(aBuilder
, aFrame
), mRect(aRect
) {
345 MOZ_COUNT_CTOR(nsDisplayMathMLBar
);
347 #ifdef NS_BUILD_REFCNT_LOGGING
348 virtual ~nsDisplayMathMLBar() {
349 MOZ_COUNT_DTOR(nsDisplayMathMLBar
);
353 virtual void Paint(nsDisplayListBuilder
* aBuilder
,
354 nsRenderingContext
* aCtx
) override
;
355 NS_DISPLAY_DECL_NAME("MathMLBar", TYPE_MATHML_BAR
)
360 void nsDisplayMathMLBar::Paint(nsDisplayListBuilder
* aBuilder
,
361 nsRenderingContext
* aCtx
)
363 // paint the bar with the current text color
364 DrawTarget
* drawTarget
= aCtx
->GetDrawTarget();
366 NSRectToNonEmptySnappedRect(mRect
+ ToReferenceFrame(),
367 mFrame
->PresContext()->AppUnitsPerDevPixel(),
369 ColorPattern
color(ToDeviceColor(
370 mFrame
->GetVisitedDependentColor(eCSSProperty_color
)));
371 drawTarget
->FillRect(rect
, color
);
375 nsMathMLFrame::DisplayBar(nsDisplayListBuilder
* aBuilder
,
376 nsIFrame
* aFrame
, const nsRect
& aRect
,
377 const nsDisplayListSet
& aLists
) {
378 if (!aFrame
->StyleVisibility()->IsVisible() || aRect
.IsEmpty())
381 aLists
.Content()->AppendNewToTop(new (aBuilder
)
382 nsDisplayMathMLBar(aBuilder
, aFrame
, aRect
));
386 nsMathMLFrame::GetRadicalParameters(nsFontMetrics
* aFontMetrics
,
388 nscoord
& aRadicalRuleThickness
,
389 nscoord
& aRadicalExtraAscender
,
390 nscoord
& aRadicalVerticalGap
)
392 nscoord oneDevPixel
= aFontMetrics
->AppUnitsPerDevPixel();
393 gfxFont
* mathFont
= aFontMetrics
->GetThebesFontGroup()->GetFirstMathFont();
395 // get the radical rulethickness
397 aRadicalRuleThickness
=
398 mathFont
->GetMathConstant(gfxFontEntry::RadicalRuleThickness
,
401 GetRuleThickness(aFontMetrics
, aRadicalRuleThickness
);
404 // get the leading to be left at the top of the resulting frame
406 aRadicalExtraAscender
=
407 mathFont
->GetMathConstant(gfxFontEntry::RadicalExtraAscender
,
410 // This seems more reliable than using aFontMetrics->GetLeading() on
413 GetEmHeight(aFontMetrics
, em
);
414 aRadicalExtraAscender
= nscoord(0.2f
* em
);
417 // get the clearance between rule and content
419 aRadicalVerticalGap
=
420 mathFont
->GetMathConstant(aDisplayStyle
?
421 gfxFontEntry::RadicalDisplayStyleVerticalGap
:
422 gfxFontEntry::RadicalVerticalGap
,
425 // Rule 11, App. G, TeXbook
426 aRadicalVerticalGap
= aRadicalRuleThickness
+
427 (aDisplayStyle
? aFontMetrics
->XHeight() : aRadicalRuleThickness
) / 4;