|
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/. */ |
|
5 |
|
6 #include "nsMathMLFrame.h" |
|
7 #include "nsNameSpaceManager.h" |
|
8 #include "nsMathMLChar.h" |
|
9 #include "nsCSSPseudoElements.h" |
|
10 #include "nsMathMLElement.h" |
|
11 |
|
12 // used to map attributes into CSS rules |
|
13 #include "nsStyleSet.h" |
|
14 #include "nsAutoPtr.h" |
|
15 #include "nsDisplayList.h" |
|
16 #include "nsRenderingContext.h" |
|
17 |
|
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); |
|
24 |
|
25 // if it has a prescribed base, fetch the type from there |
|
26 if (mPresentationData.baseFrame) |
|
27 return GetMathMLFrameTypeFor(mPresentationData.baseFrame); |
|
28 |
|
29 // everything else is treated as ordinary (mapped to 'Ord' in TeX) |
|
30 return eMathMLFrameType_Ordinary; |
|
31 } |
|
32 |
|
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; |
|
41 |
|
42 mPresentationData.flags = 0; |
|
43 mPresentationData.baseFrame = nullptr; |
|
44 |
|
45 // by default, just inherit the display of our parent |
|
46 nsPresentationData parentData; |
|
47 GetPresentationDataFrom(aParent, parentData); |
|
48 |
|
49 #if defined(DEBUG) && defined(SHOW_BOUNDING_BOX) |
|
50 mPresentationData.flags |= NS_MATHML_SHOW_BOUNDING_METRICS; |
|
51 #endif |
|
52 |
|
53 return NS_OK; |
|
54 } |
|
55 |
|
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"); |
|
62 |
|
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 } |
|
73 |
|
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); |
|
90 |
|
91 aMathMLChar->SetStyleContext(newStyleContext); |
|
92 } |
|
93 |
|
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; |
|
104 |
|
105 if (aFrame && aFrame->IsFrameOfType(nsIFrame::eMathML)) { |
|
106 nsIMathMLFrame* mathMLFrame = do_QueryFrame(aFrame); |
|
107 if (mathMLFrame) { |
|
108 mathMLFrame->GetEmbellishData(aEmbellishData); |
|
109 } |
|
110 } |
|
111 } |
|
112 |
|
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; |
|
123 |
|
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; |
|
143 |
|
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 } |
|
152 |
|
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"); |
|
163 |
|
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 } |
|
173 |
|
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"); |
|
184 |
|
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 } |
|
194 |
|
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"); |
|
201 |
|
202 if (aCSSValue.IsFixedLengthUnit()) { |
|
203 return aCSSValue.GetFixedLength(aPresContext); |
|
204 } |
|
205 if (aCSSValue.IsPixelLengthUnit()) { |
|
206 return aCSSValue.GetPixelLength(); |
|
207 } |
|
208 |
|
209 nsCSSUnit unit = aCSSValue.GetUnit(); |
|
210 |
|
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 } |
|
222 |
|
223 // MathML doesn't specify other CSS units such as rem or ch |
|
224 NS_ERROR("Unsupported unit"); |
|
225 return 0; |
|
226 } |
|
227 |
|
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; |
|
236 |
|
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 } |
|
243 |
|
244 nsCSSUnit unit = cssValue.GetUnit(); |
|
245 |
|
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 } |
|
253 |
|
254 // Absolute units. |
|
255 *aLengthValue = CalcLength(aPresContext, aStyleContext, cssValue); |
|
256 } |
|
257 |
|
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 // |
|
262 |
|
263 struct |
|
264 nsCSSMapping { |
|
265 int32_t compatibility; |
|
266 const nsIAtom* attrAtom; |
|
267 const char* cssProperty; |
|
268 }; |
|
269 |
|
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 |
|
283 |
|
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 }; |
|
290 |
|
291 void nsDisplayMathMLBoundingMetrics::Paint(nsDisplayListBuilder* aBuilder, |
|
292 nsRenderingContext* aCtx) |
|
293 { |
|
294 aCtx->SetColor(NS_RGB(0,0,255)); |
|
295 aCtx->DrawRect(mRect + ToReferenceFrame()); |
|
296 } |
|
297 |
|
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; |
|
305 |
|
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; |
|
310 |
|
311 return aLists.Content()->AppendNewToTop(new (aBuilder) |
|
312 nsDisplayMathMLBoundingMetrics(aBuilder, this, nsRect(x,y,w,h))); |
|
313 } |
|
314 #endif |
|
315 |
|
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 |
|
328 |
|
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 }; |
|
335 |
|
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 } |
|
343 |
|
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; |
|
350 |
|
351 aLists.Content()->AppendNewToTop(new (aBuilder) |
|
352 nsDisplayMathMLBar(aBuilder, aFrame, aRect)); |
|
353 } |