no bug - Import translations from android-l10n r=release a=l10n CLOSED TREE
[gecko.git] / layout / mathml / nsMathMLFrame.cpp
blob64d4fd8e6f05589ef9555d00ad32e95aa563a340
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"
10 #include "gfxUtils.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;
41 NS_IMETHODIMP
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;
58 #endif
60 return NS_OK;
63 NS_IMETHODIMP
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
79 // be applied.
80 if (NS_MATHML_IS_DTLS_SET(aWhichFlags)) {
81 if (NS_MATHML_IS_DTLS_SET(aFlagsValues)) {
82 mPresentationData.flags |= NS_MATHML_DTLS;
83 } else {
84 mPresentationData.flags &= ~NS_MATHML_DTLS;
87 return NS_OK;
90 /* static */
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->IsMathMLFrame()) {
101 nsIMathMLFrame* mathMLFrame = do_QueryFrame(aFrame);
102 if (mathMLFrame) {
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.
110 /* static */
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;
118 while (frame) {
119 if (frame->IsMathMLFrame()) {
120 nsIMathMLFrame* mathMLFrame = do_QueryFrame(frame);
121 if (mathMLFrame) {
122 mathMLFrame->GetPresentationData(aPresentationData);
123 break;
126 // stop if the caller doesn't want to lookup beyond the frame
127 if (!aClimbTree) {
128 break;
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");
134 if (!content) break;
136 if (content->IsMathMLElement(nsGkAtoms::math)) {
137 break;
139 frame = frame->GetParent();
141 NS_WARNING_ASSERTION(
142 frame && frame->GetContent(),
143 "bad MathML markup - could not find the top <math> element");
146 /* static */
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);
161 /* static */
162 void nsMathMLFrame::GetAxisHeight(DrawTarget* aDrawTarget,
163 nsFontMetrics* aFontMetrics,
164 nscoord& aAxisHeight) {
165 RefPtr<gfxFont> mathFont =
166 aFontMetrics->GetThebesFontGroup()->GetFirstMathFont();
167 if (mathFont) {
168 aAxisHeight = mathFont->MathTable()->Constant(
169 gfxMathTable::AxisHeight, aFontMetrics->AppUnitsPerDevPixel());
170 return;
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);
184 /* static */
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");
211 return 0;
214 /* static */
215 void nsMathMLFrame::GetSubDropFromChild(nsIFrame* aChild, nscoord& aSubDrop,
216 float aFontSizeInflation) {
217 RefPtr<nsFontMetrics> fm =
218 nsLayoutUtils::GetFontMetricsForFrame(aChild, aFontSizeInflation);
219 GetSubDrop(fm, aSubDrop);
222 /* static */
223 void nsMathMLFrame::GetSupDropFromChild(nsIFrame* aChild, nscoord& aSupDrop,
224 float aFontSizeInflation) {
225 RefPtr<nsFontMetrics> fm =
226 nsLayoutUtils::GetFontMetricsForFrame(aChild, aFontSizeInflation);
227 GetSupDrop(fm, aSupDrop);
230 /* static */
231 void nsMathMLFrame::ParseNumericValue(const nsString& aString,
232 nscoord* aLengthValue, uint32_t aFlags,
233 nsPresContext* aPresContext,
234 ComputedStyle* aComputedStyle,
235 float aFontSizeInflation) {
236 nsCSSValue cssValue;
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.
242 return;
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()));
252 return;
255 // Absolute units.
256 *aLengthValue =
257 CalcLength(aPresContext, aComputedStyle, cssValue, aFontSizeInflation);
260 namespace mozilla {
261 #if defined(DEBUG) && defined(SHOW_BOUNDING_BOX)
262 class nsDisplayMathMLBoundingMetrics final : public nsDisplayItem {
263 public:
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)
273 private:
274 nsRect mRect;
277 void nsDisplayMathMLBoundingMetrics::Paint(nsDisplayListBuilder* aBuilder,
278 gfxContext* aCtx) {
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));
300 #endif
302 class nsDisplayMathMLBar final : public nsPaintedDisplayItem {
303 public:
304 nsDisplayMathMLBar(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
305 const nsRect& aRect)
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)
314 private:
315 nsRect mRect;
318 void nsDisplayMathMLBar::Paint(nsDisplayListBuilder* aBuilder,
319 gfxContext* aCtx) {
320 // paint the bar with the current text color
321 DrawTarget* drawTarget = aCtx->GetDrawTarget();
322 Rect rect = NSRectToNonEmptySnappedRect(
323 mRect + ToReferenceFrame(), mFrame->PresContext()->AppUnitsPerDevPixel(),
324 *drawTarget);
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,
333 const nsRect& aRect,
334 const nsDisplayListSet& aLists,
335 uint32_t aIndex) {
336 if (!aFrame->StyleVisibility()->IsVisible() || aRect.IsEmpty()) return;
338 aLists.Content()->AppendNewToTopWithIndex<nsDisplayMathMLBar>(
339 aBuilder, aFrame, aIndex, aRect);
342 void nsMathMLFrame::GetRadicalParameters(nsFontMetrics* aFontMetrics,
343 bool aDisplayStyle,
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
352 if (mathFont) {
353 aRadicalRuleThickness = mathFont->MathTable()->Constant(
354 gfxMathTable::RadicalRuleThickness, oneDevPixel);
355 } else {
356 GetRuleThickness(aFontMetrics, aRadicalRuleThickness);
359 // get the leading to be left at the top of the resulting frame
360 if (mathFont) {
361 aRadicalExtraAscender = mathFont->MathTable()->Constant(
362 gfxMathTable::RadicalExtraAscender, oneDevPixel);
363 } else {
364 // This seems more reliable than using aFontMetrics->GetLeading() on
365 // suspicious fonts.
366 nscoord em;
367 GetEmHeight(aFontMetrics, em);
368 aRadicalExtraAscender = nscoord(0.2f * em);
371 // get the clearance between rule and content
372 if (mathFont) {
373 aRadicalVerticalGap = mathFont->MathTable()->Constant(
374 aDisplayStyle ? gfxMathTable::RadicalDisplayStyleVerticalGap
375 : gfxMathTable::RadicalVerticalGap,
376 oneDevPixel);
377 } else {
378 // Rule 11, App. G, TeXbook
379 aRadicalVerticalGap =
380 aRadicalRuleThickness +
381 (aDisplayStyle ? aFontMetrics->XHeight() : aRadicalRuleThickness) / 4;