Bug 1226301. Remove Shumway from b2gdroid nightly builds. r=fabrice
[gecko.git] / layout / mathml / nsMathMLFrame.cpp
blob7a2c578f3116f9930dc89727ba4dd7d60ac04a15
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"
8 #include "gfxUtils.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;
25 eMathMLFrameType
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;
40 NS_IMETHODIMP
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;
58 #endif
60 return NS_OK;
63 NS_IMETHODIMP
64 nsMathMLFrame::UpdatePresentationData(uint32_t aFlagsValues,
65 uint32_t aWhichFlags)
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
80 // be applied.
81 if (NS_MATHML_IS_DTLS_SET(aWhichFlags)) {
82 if (NS_MATHML_IS_DTLS_SET(aFlagsValues)) {
83 mPresentationData.flags |= NS_MATHML_DTLS;
84 } else {
85 mPresentationData.flags &= ~NS_MATHML_DTLS;
88 return NS_OK;
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.
95 /* static */ void
96 nsMathMLFrame::ResolveMathMLCharStyle(nsPresContext* aPresContext,
97 nsIContent* aContent,
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);
111 /* static */ void
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);
124 if (mathMLFrame) {
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.
132 /* static */ void
133 nsMathMLFrame::GetPresentationDataFrom(nsIFrame* aFrame,
134 nsPresentationData& aPresentationData,
135 bool aClimbTree)
137 // initialize OUT params
138 aPresentationData.flags = 0;
139 aPresentationData.baseFrame = nullptr;
141 nsIFrame* frame = aFrame;
142 while (frame) {
143 if (frame->IsFrameOfType(nsIFrame::eMathML)) {
144 nsIMathMLFrame* mathMLFrame = do_QueryFrame(frame);
145 if (mathMLFrame) {
146 mathMLFrame->GetPresentationData(aPresentationData);
147 break;
150 // stop if the caller doesn't want to lookup beyond the frame
151 if (!aClimbTree) {
152 break;
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");
158 if (!content)
159 break;
161 if (content->IsMathMLElement(nsGkAtoms::math)) {
162 break;
164 frame = frame->GetParent();
166 NS_WARN_IF_FALSE(frame && frame->GetContent(),
167 "bad MathML markup - could not find the top <math> element");
170 /* static */ void
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,
179 aDrawTarget);
180 aRuleThickness = bm.ascent + bm.descent;
181 if (aRuleThickness <= 0 || aRuleThickness >= xHeight) {
182 // fall-back to the other version
183 GetRuleThickness(aFontMetrics, aRuleThickness);
187 /* static */ void
188 nsMathMLFrame::GetAxisHeight(DrawTarget* aDrawTarget,
189 nsFontMetrics* aFontMetrics,
190 nscoord& aAxisHeight)
192 gfxFont* mathFont = aFontMetrics->GetThebesFontGroup()->GetFirstMathFont();
193 if (mathFont) {
194 aAxisHeight =
195 mathFont->GetMathConstant(gfxFontEntry::AxisHeight,
196 aFontMetrics->AppUnitsPerDevPixel());
197 return;
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);
211 /* static */ nscoord
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,
236 getter_AddRefs(fm),
237 aFontSizeInflation);
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");
244 return 0;
247 /* static */ void
248 nsMathMLFrame::ParseNumericValue(const nsString& aString,
249 nscoord* aLengthValue,
250 uint32_t aFlags,
251 nsPresContext* aPresContext,
252 nsStyleContext* aStyleContext,
253 float aFontSizeInflation)
255 nsCSSValue cssValue;
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.
261 return;
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()));
271 return;
274 // Absolute units.
275 *aLengthValue = CalcLength(aPresContext, aStyleContext, cssValue,
276 aFontSizeInflation);
279 // ================
280 // Utils to map attributes into CSS rules (work-around to bug 69409 which
281 // is not scheduled to be fixed anytime soon)
284 struct
285 nsCSSMapping {
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 {
293 public:
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);
303 #endif
305 virtual void Paint(nsDisplayListBuilder* aBuilder,
306 nsRenderingContext* aCtx) override;
307 NS_DISPLAY_DECL_NAME("MathMLBoundingMetrics", TYPE_MATHML_BOUNDING_METRICS)
308 private:
309 nsRect mRect;
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);
322 nsresult
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))
328 return NS_OK;
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)));
338 #endif
340 class nsDisplayMathMLBar : public nsDisplayItem {
341 public:
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);
351 #endif
353 virtual void Paint(nsDisplayListBuilder* aBuilder,
354 nsRenderingContext* aCtx) override;
355 NS_DISPLAY_DECL_NAME("MathMLBar", TYPE_MATHML_BAR)
356 private:
357 nsRect mRect;
360 void nsDisplayMathMLBar::Paint(nsDisplayListBuilder* aBuilder,
361 nsRenderingContext* aCtx)
363 // paint the bar with the current text color
364 DrawTarget* drawTarget = aCtx->GetDrawTarget();
365 Rect rect =
366 NSRectToNonEmptySnappedRect(mRect + ToReferenceFrame(),
367 mFrame->PresContext()->AppUnitsPerDevPixel(),
368 *drawTarget);
369 ColorPattern color(ToDeviceColor(
370 mFrame->GetVisitedDependentColor(eCSSProperty_color)));
371 drawTarget->FillRect(rect, color);
374 void
375 nsMathMLFrame::DisplayBar(nsDisplayListBuilder* aBuilder,
376 nsIFrame* aFrame, const nsRect& aRect,
377 const nsDisplayListSet& aLists) {
378 if (!aFrame->StyleVisibility()->IsVisible() || aRect.IsEmpty())
379 return;
381 aLists.Content()->AppendNewToTop(new (aBuilder)
382 nsDisplayMathMLBar(aBuilder, aFrame, aRect));
385 void
386 nsMathMLFrame::GetRadicalParameters(nsFontMetrics* aFontMetrics,
387 bool aDisplayStyle,
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
396 if (mathFont) {
397 aRadicalRuleThickness =
398 mathFont->GetMathConstant(gfxFontEntry::RadicalRuleThickness,
399 oneDevPixel);
400 } else {
401 GetRuleThickness(aFontMetrics, aRadicalRuleThickness);
404 // get the leading to be left at the top of the resulting frame
405 if (mathFont) {
406 aRadicalExtraAscender =
407 mathFont->GetMathConstant(gfxFontEntry::RadicalExtraAscender,
408 oneDevPixel);
409 } else {
410 // This seems more reliable than using aFontMetrics->GetLeading() on
411 // suspicious fonts.
412 nscoord em;
413 GetEmHeight(aFontMetrics, em);
414 aRadicalExtraAscender = nscoord(0.2f * em);
417 // get the clearance between rule and content
418 if (mathFont) {
419 aRadicalVerticalGap =
420 mathFont->GetMathConstant(aDisplayStyle ?
421 gfxFontEntry::RadicalDisplayStyleVerticalGap :
422 gfxFontEntry::RadicalVerticalGap,
423 oneDevPixel);
424 } else {
425 // Rule 11, App. G, TeXbook
426 aRadicalVerticalGap = aRadicalRuleThickness +
427 (aDisplayStyle ? aFontMetrics->XHeight() : aRadicalRuleThickness) / 4;