Fri, 16 Jan 2015 18:13:44 +0100
Integrate suggestion from review to improve consistency with existing code.
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"
7 #include "nsNameSpaceManager.h"
8 #include "nsMathMLChar.h"
9 #include "nsCSSPseudoElements.h"
10 #include "nsMathMLElement.h"
12 // used to map attributes into CSS rules
13 #include "nsStyleSet.h"
14 #include "nsAutoPtr.h"
15 #include "nsDisplayList.h"
16 #include "nsRenderingContext.h"
18 eMathMLFrameType
19 nsMathMLFrame::GetMathMLFrameType()
20 {
21 // see if it is an embellished operator (mapped to 'Op' in TeX)
22 if (mEmbellishData.coreFrame)
23 return GetMathMLFrameTypeFor(mEmbellishData.coreFrame);
25 // if it has a prescribed base, fetch the type from there
26 if (mPresentationData.baseFrame)
27 return GetMathMLFrameTypeFor(mPresentationData.baseFrame);
29 // everything else is treated as ordinary (mapped to 'Ord' in TeX)
30 return eMathMLFrameType_Ordinary;
31 }
33 NS_IMETHODIMP
34 nsMathMLFrame::InheritAutomaticData(nsIFrame* aParent)
35 {
36 mEmbellishData.flags = 0;
37 mEmbellishData.coreFrame = nullptr;
38 mEmbellishData.direction = NS_STRETCH_DIRECTION_UNSUPPORTED;
39 mEmbellishData.leadingSpace = 0;
40 mEmbellishData.trailingSpace = 0;
42 mPresentationData.flags = 0;
43 mPresentationData.baseFrame = nullptr;
45 // by default, just inherit the display of our parent
46 nsPresentationData parentData;
47 GetPresentationDataFrom(aParent, parentData);
49 #if defined(DEBUG) && defined(SHOW_BOUNDING_BOX)
50 mPresentationData.flags |= NS_MATHML_SHOW_BOUNDING_METRICS;
51 #endif
53 return NS_OK;
54 }
56 NS_IMETHODIMP
57 nsMathMLFrame::UpdatePresentationData(uint32_t aFlagsValues,
58 uint32_t aWhichFlags)
59 {
60 NS_ASSERTION(NS_MATHML_IS_COMPRESSED(aWhichFlags),
61 "aWhichFlags should only be compression flag");
63 if (NS_MATHML_IS_COMPRESSED(aWhichFlags)) {
64 // updating the compression flag is allowed
65 if (NS_MATHML_IS_COMPRESSED(aFlagsValues)) {
66 // 'compressed' means 'prime' style in App. G, TeXbook
67 mPresentationData.flags |= NS_MATHML_COMPRESSED;
68 }
69 // no else. the flag is sticky. it retains its value once it is set
70 }
71 return NS_OK;
72 }
74 // Helper to give a style context suitable for doing the stretching of
75 // a MathMLChar. Frame classes that use this should ensure that the
76 // extra leaf style contexts given to the MathMLChars are accessible to
77 // the Style System via the Get/Set AdditionalStyleContext() APIs.
78 /* static */ void
79 nsMathMLFrame::ResolveMathMLCharStyle(nsPresContext* aPresContext,
80 nsIContent* aContent,
81 nsStyleContext* aParentStyleContext,
82 nsMathMLChar* aMathMLChar)
83 {
84 nsCSSPseudoElements::Type pseudoType =
85 nsCSSPseudoElements::ePseudo_mozMathAnonymous; // savings
86 nsRefPtr<nsStyleContext> newStyleContext;
87 newStyleContext = aPresContext->StyleSet()->
88 ResolvePseudoElementStyle(aContent->AsElement(), pseudoType,
89 aParentStyleContext, nullptr);
91 aMathMLChar->SetStyleContext(newStyleContext);
92 }
94 /* static */ void
95 nsMathMLFrame::GetEmbellishDataFrom(nsIFrame* aFrame,
96 nsEmbellishData& aEmbellishData)
97 {
98 // initialize OUT params
99 aEmbellishData.flags = 0;
100 aEmbellishData.coreFrame = nullptr;
101 aEmbellishData.direction = NS_STRETCH_DIRECTION_UNSUPPORTED;
102 aEmbellishData.leadingSpace = 0;
103 aEmbellishData.trailingSpace = 0;
105 if (aFrame && aFrame->IsFrameOfType(nsIFrame::eMathML)) {
106 nsIMathMLFrame* mathMLFrame = do_QueryFrame(aFrame);
107 if (mathMLFrame) {
108 mathMLFrame->GetEmbellishData(aEmbellishData);
109 }
110 }
111 }
113 // helper to get the presentation data of a frame, by possibly walking up
114 // the frame hierarchy if we happen to be surrounded by non-MathML frames.
115 /* static */ void
116 nsMathMLFrame::GetPresentationDataFrom(nsIFrame* aFrame,
117 nsPresentationData& aPresentationData,
118 bool aClimbTree)
119 {
120 // initialize OUT params
121 aPresentationData.flags = 0;
122 aPresentationData.baseFrame = nullptr;
124 nsIFrame* frame = aFrame;
125 while (frame) {
126 if (frame->IsFrameOfType(nsIFrame::eMathML)) {
127 nsIMathMLFrame* mathMLFrame = do_QueryFrame(frame);
128 if (mathMLFrame) {
129 mathMLFrame->GetPresentationData(aPresentationData);
130 break;
131 }
132 }
133 // stop if the caller doesn't want to lookup beyond the frame
134 if (!aClimbTree) {
135 break;
136 }
137 // stop if we reach the root <math> tag
138 nsIContent* content = frame->GetContent();
139 NS_ASSERTION(content || !frame->GetParent(), // no assert for the root
140 "dangling frame without a content node");
141 if (!content)
142 break;
144 if (content->Tag() == nsGkAtoms::math) {
145 break;
146 }
147 frame = frame->GetParent();
148 }
149 NS_WARN_IF_FALSE(frame && frame->GetContent(),
150 "bad MathML markup - could not find the top <math> element");
151 }
153 /* static */ void
154 nsMathMLFrame::GetRuleThickness(nsRenderingContext& aRenderingContext,
155 nsFontMetrics* aFontMetrics,
156 nscoord& aRuleThickness)
157 {
158 // get the bounding metrics of the overbar char, the rendering context
159 // is assumed to have been set with the font of the current style context
160 NS_ASSERTION(aRenderingContext.FontMetrics()->Font().
161 Equals(aFontMetrics->Font()),
162 "unexpected state");
164 nscoord xHeight = aFontMetrics->XHeight();
165 char16_t overBar = 0x00AF;
166 nsBoundingMetrics bm = aRenderingContext.GetBoundingMetrics(&overBar, 1);
167 aRuleThickness = bm.ascent + bm.descent;
168 if (aRuleThickness <= 0 || aRuleThickness >= xHeight) {
169 // fall-back to the other version
170 GetRuleThickness(aFontMetrics, aRuleThickness);
171 }
172 }
174 /* static */ void
175 nsMathMLFrame::GetAxisHeight(nsRenderingContext& aRenderingContext,
176 nsFontMetrics* aFontMetrics,
177 nscoord& aAxisHeight)
178 {
179 // get the bounding metrics of the minus sign, the rendering context
180 // is assumed to have been set with the font of the current style context
181 NS_ASSERTION(aRenderingContext.FontMetrics()->Font().
182 Equals(aFontMetrics->Font()),
183 "unexpected state");
185 nscoord xHeight = aFontMetrics->XHeight();
186 char16_t minus = 0x2212; // not '-', but official Unicode minus sign
187 nsBoundingMetrics bm = aRenderingContext.GetBoundingMetrics(&minus, 1);
188 aAxisHeight = bm.ascent - (bm.ascent + bm.descent)/2;
189 if (aAxisHeight <= 0 || aAxisHeight >= xHeight) {
190 // fall-back to the other version
191 GetAxisHeight(aFontMetrics, aAxisHeight);
192 }
193 }
195 /* static */ nscoord
196 nsMathMLFrame::CalcLength(nsPresContext* aPresContext,
197 nsStyleContext* aStyleContext,
198 const nsCSSValue& aCSSValue)
199 {
200 NS_ASSERTION(aCSSValue.IsLengthUnit(), "not a length unit");
202 if (aCSSValue.IsFixedLengthUnit()) {
203 return aCSSValue.GetFixedLength(aPresContext);
204 }
205 if (aCSSValue.IsPixelLengthUnit()) {
206 return aCSSValue.GetPixelLength();
207 }
209 nsCSSUnit unit = aCSSValue.GetUnit();
211 if (eCSSUnit_EM == unit) {
212 const nsStyleFont* font = aStyleContext->StyleFont();
213 return NSToCoordRound(aCSSValue.GetFloatValue() * (float)font->mFont.size);
214 }
215 else if (eCSSUnit_XHeight == unit) {
216 nsRefPtr<nsFontMetrics> fm;
217 nsLayoutUtils::GetFontMetricsForStyleContext(aStyleContext,
218 getter_AddRefs(fm));
219 nscoord xHeight = fm->XHeight();
220 return NSToCoordRound(aCSSValue.GetFloatValue() * (float)xHeight);
221 }
223 // MathML doesn't specify other CSS units such as rem or ch
224 NS_ERROR("Unsupported unit");
225 return 0;
226 }
228 /* static */ void
229 nsMathMLFrame::ParseNumericValue(const nsString& aString,
230 nscoord* aLengthValue,
231 uint32_t aFlags,
232 nsPresContext* aPresContext,
233 nsStyleContext* aStyleContext)
234 {
235 nsCSSValue cssValue;
237 if (!nsMathMLElement::ParseNumericValue(aString, cssValue, aFlags,
238 aPresContext->Document())) {
239 // Invalid attribute value. aLengthValue remains unchanged, so the default
240 // length value is used.
241 return;
242 }
244 nsCSSUnit unit = cssValue.GetUnit();
246 if (unit == eCSSUnit_Percent || unit == eCSSUnit_Number) {
247 // Relative units. A multiple of the default length value is used.
248 *aLengthValue = NSToCoordRound(*aLengthValue * (unit == eCSSUnit_Percent ?
249 cssValue.GetPercentValue() :
250 cssValue.GetFloatValue()));
251 return;
252 }
254 // Absolute units.
255 *aLengthValue = CalcLength(aPresContext, aStyleContext, cssValue);
256 }
258 // ================
259 // Utils to map attributes into CSS rules (work-around to bug 69409 which
260 // is not scheduled to be fixed anytime soon)
261 //
263 struct
264 nsCSSMapping {
265 int32_t compatibility;
266 const nsIAtom* attrAtom;
267 const char* cssProperty;
268 };
270 #if defined(DEBUG) && defined(SHOW_BOUNDING_BOX)
271 class nsDisplayMathMLBoundingMetrics : public nsDisplayItem {
272 public:
273 nsDisplayMathMLBoundingMetrics(nsDisplayListBuilder* aBuilder,
274 nsIFrame* aFrame, const nsRect& aRect)
275 : nsDisplayItem(aBuilder, aFrame), mRect(aRect) {
276 MOZ_COUNT_CTOR(nsDisplayMathMLBoundingMetrics);
277 }
278 #ifdef NS_BUILD_REFCNT_LOGGING
279 virtual ~nsDisplayMathMLBoundingMetrics() {
280 MOZ_COUNT_DTOR(nsDisplayMathMLBoundingMetrics);
281 }
282 #endif
284 virtual void Paint(nsDisplayListBuilder* aBuilder,
285 nsRenderingContext* aCtx) MOZ_OVERRIDE;
286 NS_DISPLAY_DECL_NAME("MathMLBoundingMetrics", TYPE_MATHML_BOUNDING_METRICS)
287 private:
288 nsRect mRect;
289 };
291 void nsDisplayMathMLBoundingMetrics::Paint(nsDisplayListBuilder* aBuilder,
292 nsRenderingContext* aCtx)
293 {
294 aCtx->SetColor(NS_RGB(0,0,255));
295 aCtx->DrawRect(mRect + ToReferenceFrame());
296 }
298 nsresult
299 nsMathMLFrame::DisplayBoundingMetrics(nsDisplayListBuilder* aBuilder,
300 nsIFrame* aFrame, const nsPoint& aPt,
301 const nsBoundingMetrics& aMetrics,
302 const nsDisplayListSet& aLists) {
303 if (!NS_MATHML_PAINT_BOUNDING_METRICS(mPresentationData.flags))
304 return NS_OK;
306 nscoord x = aPt.x + aMetrics.leftBearing;
307 nscoord y = aPt.y - aMetrics.ascent;
308 nscoord w = aMetrics.rightBearing - aMetrics.leftBearing;
309 nscoord h = aMetrics.ascent + aMetrics.descent;
311 return aLists.Content()->AppendNewToTop(new (aBuilder)
312 nsDisplayMathMLBoundingMetrics(aBuilder, this, nsRect(x,y,w,h)));
313 }
314 #endif
316 class nsDisplayMathMLBar : public nsDisplayItem {
317 public:
318 nsDisplayMathMLBar(nsDisplayListBuilder* aBuilder,
319 nsIFrame* aFrame, const nsRect& aRect)
320 : nsDisplayItem(aBuilder, aFrame), mRect(aRect) {
321 MOZ_COUNT_CTOR(nsDisplayMathMLBar);
322 }
323 #ifdef NS_BUILD_REFCNT_LOGGING
324 virtual ~nsDisplayMathMLBar() {
325 MOZ_COUNT_DTOR(nsDisplayMathMLBar);
326 }
327 #endif
329 virtual void Paint(nsDisplayListBuilder* aBuilder,
330 nsRenderingContext* aCtx) MOZ_OVERRIDE;
331 NS_DISPLAY_DECL_NAME("MathMLBar", TYPE_MATHML_BAR)
332 private:
333 nsRect mRect;
334 };
336 void nsDisplayMathMLBar::Paint(nsDisplayListBuilder* aBuilder,
337 nsRenderingContext* aCtx)
338 {
339 // paint the bar with the current text color
340 aCtx->SetColor(mFrame->GetVisitedDependentColor(eCSSProperty_color));
341 aCtx->FillRect(mRect + ToReferenceFrame());
342 }
344 void
345 nsMathMLFrame::DisplayBar(nsDisplayListBuilder* aBuilder,
346 nsIFrame* aFrame, const nsRect& aRect,
347 const nsDisplayListSet& aLists) {
348 if (!aFrame->StyleVisibility()->IsVisible() || aRect.IsEmpty())
349 return;
351 aLists.Content()->AppendNewToTop(new (aBuilder)
352 nsDisplayMathMLBar(aBuilder, aFrame, aRect));
353 }