1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is Mozilla MathML Project.
17 * The Initial Developer of the Original Code is
18 * The University Of Queensland.
19 * Portions created by the Initial Developer are Copyright (C) 1999
20 * the Initial Developer. All Rights Reserved.
23 * Roger B. Sidje <rbs@maths.uq.edu.au>
25 * Alternatively, the contents of this file may be used under the terms of
26 * either of the GNU General Public License Version 2 or later (the "GPL"),
27 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
39 #include "nsINameSpaceManager.h"
40 #include "nsMathMLFrame.h"
41 #include "nsMathMLChar.h"
42 #include "nsCSSAnonBoxes.h"
44 // used to map attributes into CSS rules
45 #include "nsIDocument.h"
46 #include "nsStyleSet.h"
47 #include "nsIStyleSheet.h"
48 #include "nsICSSStyleSheet.h"
49 #include "nsIDOMCSSStyleSheet.h"
50 #include "nsICSSRule.h"
51 #include "nsICSSStyleRule.h"
52 #include "nsStyleChangeList.h"
53 #include "nsFrameManager.h"
54 #include "nsNetUtil.h"
56 #include "nsContentCID.h"
57 #include "nsAutoPtr.h"
58 #include "nsStyleSet.h"
59 #include "nsStyleUtil.h"
60 #include "nsDisplayList.h"
61 #include "nsAttrName.h"
63 static NS_DEFINE_CID(kCSSStyleSheetCID
, NS_CSS_STYLESHEET_CID
);
66 NS_IMPL_QUERY_INTERFACE1(nsMathMLFrame
, nsIMathMLFrame
)
69 nsMathMLFrame::GetMathMLFrameType()
71 // see if it is an embellished operator (mapped to 'Op' in TeX)
72 if (mEmbellishData
.coreFrame
)
73 return GetMathMLFrameTypeFor(mEmbellishData
.coreFrame
);
75 // if it has a prescribed base, fetch the type from there
76 if (mPresentationData
.baseFrame
)
77 return GetMathMLFrameTypeFor(mPresentationData
.baseFrame
);
79 // everything else is treated as ordinary (mapped to 'Ord' in TeX)
80 return eMathMLFrameType_Ordinary
;
83 // snippet of code used by <mstyle> and <mtable>, which are the only
84 // two tags where the displaystyle attribute is allowed by the spec.
86 nsMathMLFrame::FindAttrDisplaystyle(nsIContent
* aContent
,
87 nsPresentationData
& aPresentationData
)
89 NS_ASSERTION(aContent
->Tag() == nsGkAtoms::mstyle_
||
90 aContent
->Tag() == nsGkAtoms::mtable_
, "bad caller");
91 static nsIContent::AttrValuesArray strings
[] =
92 {&nsGkAtoms::_false
, &nsGkAtoms::_true
, nsnull
};
93 // see if the explicit displaystyle attribute is there
94 switch (aContent
->FindAttrValueIn(kNameSpaceID_None
,
95 nsGkAtoms::displaystyle_
, strings
, eCaseMatters
)) {
97 aPresentationData
.flags
&= ~NS_MATHML_DISPLAYSTYLE
;
98 aPresentationData
.flags
|= NS_MATHML_EXPLICIT_DISPLAYSTYLE
;
101 aPresentationData
.flags
|= NS_MATHML_DISPLAYSTYLE
;
102 aPresentationData
.flags
|= NS_MATHML_EXPLICIT_DISPLAYSTYLE
;
105 // no reset if the attr isn't found. so be sure to call it on inherited flags
109 nsMathMLFrame::InheritAutomaticData(nsIFrame
* aParent
)
111 mEmbellishData
.flags
= 0;
112 mEmbellishData
.coreFrame
= nsnull
;
113 mEmbellishData
.direction
= NS_STRETCH_DIRECTION_UNSUPPORTED
;
114 mEmbellishData
.leftSpace
= 0;
115 mEmbellishData
.rightSpace
= 0;
117 mPresentationData
.flags
= 0;
118 mPresentationData
.baseFrame
= nsnull
;
119 mPresentationData
.mstyle
= nsnull
;
121 // by default, just inherit the display of our parent
122 nsPresentationData parentData
;
123 GetPresentationDataFrom(aParent
, parentData
);
124 mPresentationData
.mstyle
= parentData
.mstyle
;
125 if (NS_MATHML_IS_DISPLAYSTYLE(parentData
.flags
)) {
126 mPresentationData
.flags
|= NS_MATHML_DISPLAYSTYLE
;
129 #if defined(NS_DEBUG) && defined(SHOW_BOUNDING_BOX)
130 mPresentationData
.flags
|= NS_MATHML_SHOW_BOUNDING_METRICS
;
137 nsMathMLFrame::UpdatePresentationData(PRUint32 aFlagsValues
,
138 PRUint32 aWhichFlags
)
140 // update flags that are relevant to this call
141 if (NS_MATHML_IS_DISPLAYSTYLE(aWhichFlags
)) {
142 // updating the displaystyle flag is allowed
143 if (NS_MATHML_IS_DISPLAYSTYLE(aFlagsValues
)) {
144 mPresentationData
.flags
|= NS_MATHML_DISPLAYSTYLE
;
147 mPresentationData
.flags
&= ~NS_MATHML_DISPLAYSTYLE
;
150 if (NS_MATHML_IS_COMPRESSED(aWhichFlags
)) {
151 // updating the compression flag is allowed
152 if (NS_MATHML_IS_COMPRESSED(aFlagsValues
)) {
153 // 'compressed' means 'prime' style in App. G, TeXbook
154 mPresentationData
.flags
|= NS_MATHML_COMPRESSED
;
156 // no else. the flag is sticky. it retains its value once it is set
161 // Helper to give a style context suitable for doing the stretching of
162 // a MathMLChar. Frame classes that use this should ensure that the
163 // extra leaf style contexts given to the MathMLChars are accessible to
164 // the Style System via the Get/Set AdditionalStyleContext() APIs.
166 nsMathMLFrame::ResolveMathMLCharStyle(nsPresContext
* aPresContext
,
167 nsIContent
* aContent
,
168 nsStyleContext
* aParentStyleContext
,
169 nsMathMLChar
* aMathMLChar
,
170 PRBool aIsMutableChar
)
172 nsIAtom
* pseudoStyle
= (aIsMutableChar
) ?
173 nsCSSAnonBoxes::mozMathStretchy
:
174 nsCSSAnonBoxes::mozMathAnonymous
; // savings
175 nsRefPtr
<nsStyleContext
> newStyleContext
;
176 newStyleContext
= aPresContext
->StyleSet()->
177 ResolvePseudoStyleFor(aContent
, pseudoStyle
, aParentStyleContext
);
180 aMathMLChar
->SetStyleContext(newStyleContext
);
184 nsMathMLFrame::GetEmbellishDataFrom(nsIFrame
* aFrame
,
185 nsEmbellishData
& aEmbellishData
)
187 // initialize OUT params
188 aEmbellishData
.flags
= 0;
189 aEmbellishData
.coreFrame
= nsnull
;
190 aEmbellishData
.direction
= NS_STRETCH_DIRECTION_UNSUPPORTED
;
191 aEmbellishData
.leftSpace
= 0;
192 aEmbellishData
.rightSpace
= 0;
194 if (aFrame
&& aFrame
->IsFrameOfType(nsIFrame::eMathML
)) {
195 nsIMathMLFrame
* mathMLFrame
;
196 CallQueryInterface(aFrame
, &mathMLFrame
);
198 mathMLFrame
->GetEmbellishData(aEmbellishData
);
203 // helper to get the presentation data of a frame, by possibly walking up
204 // the frame hierarchy if we happen to be surrounded by non-MathML frames.
206 nsMathMLFrame::GetPresentationDataFrom(nsIFrame
* aFrame
,
207 nsPresentationData
& aPresentationData
,
210 // initialize OUT params
211 aPresentationData
.flags
= 0;
212 aPresentationData
.baseFrame
= nsnull
;
213 aPresentationData
.mstyle
= nsnull
;
215 nsIFrame
* frame
= aFrame
;
217 if (frame
->IsFrameOfType(nsIFrame::eMathML
)) {
218 nsIMathMLFrame
* mathMLFrame
;
219 CallQueryInterface(frame
, &mathMLFrame
);
221 mathMLFrame
->GetPresentationData(aPresentationData
);
225 // stop if the caller doesn't want to lookup beyond the frame
229 // stop if we reach the root <math> tag
230 nsIContent
* content
= frame
->GetContent();
231 NS_ASSERTION(content
|| !frame
->GetParent(), // no assert for the root
232 "dangling frame without a content node");
236 if (content
->Tag() == nsGkAtoms::math
) {
237 const nsStyleDisplay
* display
= frame
->GetStyleDisplay();
238 if (display
->mDisplay
== NS_STYLE_DISPLAY_BLOCK
) {
239 aPresentationData
.flags
|= NS_MATHML_DISPLAYSTYLE
;
243 frame
= frame
->GetParent();
245 NS_WARN_IF_FALSE(frame
&& frame
->GetContent(),
246 "bad MathML markup - could not find the top <math> element");
249 // helper to get an attribute from the content or the surrounding <mstyle> hierarchy
251 nsMathMLFrame::GetAttribute(nsIContent
* aContent
,
252 nsIFrame
* aMathMLmstyleFrame
,
253 nsIAtom
* aAttributeAtom
,
256 // see if we can get the attribute from the content
257 if (aContent
&& aContent
->GetAttr(kNameSpaceID_None
, aAttributeAtom
,
262 // see if we can get the attribute from the mstyle frame
263 if (!aMathMLmstyleFrame
) {
267 nsIFrame
* mstyleParent
= aMathMLmstyleFrame
->GetParent();
269 nsPresentationData mstyleParentData
;
270 mstyleParentData
.mstyle
= nsnull
;
273 nsIMathMLFrame
* mathMLFrame
;
274 CallQueryInterface(mstyleParent
, &mathMLFrame
);
276 mathMLFrame
->GetPresentationData(mstyleParentData
);
280 // recurse all the way up into the <mstyle> hierarchy
281 return GetAttribute(aMathMLmstyleFrame
->GetContent(),
282 mstyleParentData
.mstyle
, aAttributeAtom
, aValue
);
286 nsMathMLFrame::GetRuleThickness(nsIRenderingContext
& aRenderingContext
,
287 nsIFontMetrics
* aFontMetrics
,
288 nscoord
& aRuleThickness
)
290 // get the bounding metrics of the overbar char, the rendering context
291 // is assumed to have been set with the font of the current style context
293 nsCOMPtr
<nsIFontMetrics
> currFontMetrics
;
294 aRenderingContext
.GetFontMetrics(*getter_AddRefs(currFontMetrics
));
295 NS_ASSERTION(currFontMetrics
->Font().Equals(aFontMetrics
->Font()),
299 aFontMetrics
->GetXHeight(xHeight
);
300 PRUnichar overBar
= 0x00AF;
301 nsBoundingMetrics bm
;
302 nsresult rv
= aRenderingContext
.GetBoundingMetrics(&overBar
, PRUint32(1), bm
);
303 if (NS_SUCCEEDED(rv
)) {
304 aRuleThickness
= bm
.ascent
+ bm
.descent
;
306 if (NS_FAILED(rv
) || aRuleThickness
<= 0 || aRuleThickness
>= xHeight
) {
307 // fall-back to the other version
308 GetRuleThickness(aFontMetrics
, aRuleThickness
);
312 nscoord oldRuleThickness
;
313 GetRuleThickness(aFontMetrics
, oldRuleThickness
);
315 PRUnichar sqrt
= 0xE063; // a sqrt glyph from TeX's CMEX font
316 rv
= aRenderingContext
.GetBoundingMetrics(&sqrt
, PRUint32(1), bm
);
317 nscoord sqrtrule
= bm
.ascent
; // according to TeX, the ascent should be the rule
319 printf("xheight:%4d rule:%4d oldrule:%4d sqrtrule:%4d\n",
320 xHeight
, aRuleThickness
, oldRuleThickness
, sqrtrule
);
325 nsMathMLFrame::GetAxisHeight(nsIRenderingContext
& aRenderingContext
,
326 nsIFontMetrics
* aFontMetrics
,
327 nscoord
& aAxisHeight
)
329 // get the bounding metrics of the minus sign, the rendering context
330 // is assumed to have been set with the font of the current style context
332 nsCOMPtr
<nsIFontMetrics
> currFontMetrics
;
333 aRenderingContext
.GetFontMetrics(*getter_AddRefs(currFontMetrics
));
334 NS_ASSERTION(currFontMetrics
->Font().Equals(aFontMetrics
->Font()),
338 aFontMetrics
->GetXHeight(xHeight
);
339 PRUnichar minus
= 0x2212; // not '-', but official Unicode minus sign
340 nsBoundingMetrics bm
;
341 nsresult rv
= aRenderingContext
.GetBoundingMetrics(&minus
, PRUint32(1), bm
);
342 if (NS_SUCCEEDED(rv
)) {
343 aAxisHeight
= bm
.ascent
- (bm
.ascent
+ bm
.descent
)/2;
345 if (NS_FAILED(rv
) || aAxisHeight
<= 0 || aAxisHeight
>= xHeight
) {
346 // fall-back to the other version
347 GetAxisHeight(aFontMetrics
, aAxisHeight
);
352 nsMathMLFrame::CalcLength(nsPresContext
* aPresContext
,
353 nsStyleContext
* aStyleContext
,
354 const nsCSSValue
& aCSSValue
)
356 NS_ASSERTION(aCSSValue
.IsLengthUnit(), "not a length unit");
358 if (aCSSValue
.IsFixedLengthUnit()) {
359 return aPresContext
->TwipsToAppUnits(aCSSValue
.GetLengthTwips());
362 nsCSSUnit unit
= aCSSValue
.GetUnit();
364 if (eCSSUnit_Pixel
== unit
) {
365 return nsPresContext::CSSPixelsToAppUnits(aCSSValue
.GetFloatValue());
367 else if (eCSSUnit_EM
== unit
) {
368 const nsStyleFont
* font
= aStyleContext
->GetStyleFont();
369 return NSToCoordRound(aCSSValue
.GetFloatValue() * (float)font
->mFont
.size
);
371 else if (eCSSUnit_XHeight
== unit
) {
373 const nsStyleFont
* font
= aStyleContext
->GetStyleFont();
374 nsCOMPtr
<nsIFontMetrics
> fm
= aPresContext
->GetMetricsFor(font
->mFont
);
375 fm
->GetXHeight(xHeight
);
376 return NSToCoordRound(aCSSValue
.GetFloatValue() * (float)xHeight
);
383 nsMathMLFrame::ParseNamedSpaceValue(nsIFrame
* aMathMLmstyleFrame
,
385 nsCSSValue
& aCSSValue
)
388 aString
.CompressWhitespace(); // aString is not a const in this code...
389 if (!aString
.Length()) return PR_FALSE
;
391 // See if it is one of the 'namedspace' (ranging 1/18em...7/18em)
393 nsIAtom
* namedspaceAtom
= nsnull
;
394 if (aString
.EqualsLiteral("veryverythinmathspace")) {
396 namedspaceAtom
= nsGkAtoms::veryverythinmathspace_
;
398 else if (aString
.EqualsLiteral("verythinmathspace")) {
400 namedspaceAtom
= nsGkAtoms::verythinmathspace_
;
402 else if (aString
.EqualsLiteral("thinmathspace")) {
404 namedspaceAtom
= nsGkAtoms::thinmathspace_
;
406 else if (aString
.EqualsLiteral("mediummathspace")) {
408 namedspaceAtom
= nsGkAtoms::mediummathspace_
;
410 else if (aString
.EqualsLiteral("thickmathspace")) {
412 namedspaceAtom
= nsGkAtoms::thickmathspace_
;
414 else if (aString
.EqualsLiteral("verythickmathspace")) {
416 namedspaceAtom
= nsGkAtoms::verythickmathspace_
;
418 else if (aString
.EqualsLiteral("veryverythickmathspace")) {
420 namedspaceAtom
= nsGkAtoms::veryverythickmathspace_
;
424 if (aMathMLmstyleFrame
) {
425 // see if there is a <mstyle> that has overriden the default value
426 // GetAttribute() will recurse all the way up into the <mstyle> hierarchy
428 GetAttribute(nsnull
, aMathMLmstyleFrame
, namedspaceAtom
, value
);
429 if (!value
.IsEmpty()) {
430 if (ParseNumericValue(value
, aCSSValue
) &&
431 aCSSValue
.IsLengthUnit()) {
437 // fall back to the default value
438 aCSSValue
.SetFloatValue(float(i
)/float(18), eCSSUnit_EM
);
446 // Utils to map attributes into CSS rules (work-around to bug 69409 which
447 // is not scheduled to be fixed anytime soon)
450 static const PRInt32 kMathMLversion1
= 1;
451 static const PRInt32 kMathMLversion2
= 2;
455 PRInt32 compatibility
;
456 const nsIAtom
* attrAtom
;
457 const char* cssProperty
;
460 #if defined(NS_DEBUG) && defined(SHOW_BOUNDING_BOX)
461 class nsDisplayMathMLBoundingMetrics
: public nsDisplayItem
{
463 nsDisplayMathMLBoundingMetrics(nsIFrame
* aFrame
, const nsRect
& aRect
)
464 : nsDisplayItem(aFrame
), mRect(aRect
) {
465 MOZ_COUNT_CTOR(nsDisplayMathMLBoundingMetrics
);
467 #ifdef NS_BUILD_REFCNT_LOGGING
468 virtual ~nsDisplayMathMLBoundingMetrics() {
469 MOZ_COUNT_DTOR(nsDisplayMathMLBoundingMetrics
);
473 virtual void Paint(nsDisplayListBuilder
* aBuilder
, nsIRenderingContext
* aCtx
,
474 const nsRect
& aDirtyRect
);
475 NS_DISPLAY_DECL_NAME("MathMLBoundingMetrics")
480 void nsDisplayMathMLBoundingMetrics::Paint(nsDisplayListBuilder
* aBuilder
,
481 nsIRenderingContext
* aCtx
, const nsRect
& aDirtyRect
)
483 aCtx
->SetColor(NS_RGB(0,0,255));
484 aCtx
->DrawRect(mRect
+ aBuilder
->ToReferenceFrame(mFrame
));
488 nsMathMLFrame::DisplayBoundingMetrics(nsDisplayListBuilder
* aBuilder
,
489 nsIFrame
* aFrame
, const nsPoint
& aPt
,
490 const nsBoundingMetrics
& aMetrics
,
491 const nsDisplayListSet
& aLists
) {
492 if (!NS_MATHML_PAINT_BOUNDING_METRICS(mPresentationData
.flags
))
495 nscoord x
= aPt
.x
+ aMetrics
.leftBearing
;
496 nscoord y
= aPt
.y
- aMetrics
.ascent
;
497 nscoord w
= aMetrics
.rightBearing
- aMetrics
.leftBearing
;
498 nscoord h
= aMetrics
.ascent
+ aMetrics
.descent
;
500 return aLists
.Content()->AppendNewToTop(new (aBuilder
)
501 nsDisplayMathMLBoundingMetrics(this, nsRect(x
,y
,w
,h
)));
505 class nsDisplayMathMLBar
: public nsDisplayItem
{
507 nsDisplayMathMLBar(nsIFrame
* aFrame
, const nsRect
& aRect
)
508 : nsDisplayItem(aFrame
), mRect(aRect
) {
509 MOZ_COUNT_CTOR(nsDisplayMathMLBar
);
511 #ifdef NS_BUILD_REFCNT_LOGGING
512 virtual ~nsDisplayMathMLBar() {
513 MOZ_COUNT_DTOR(nsDisplayMathMLBar
);
517 virtual void Paint(nsDisplayListBuilder
* aBuilder
, nsIRenderingContext
* aCtx
,
518 const nsRect
& aDirtyRect
);
519 NS_DISPLAY_DECL_NAME("MathMLBar")
524 void nsDisplayMathMLBar::Paint(nsDisplayListBuilder
* aBuilder
,
525 nsIRenderingContext
* aCtx
, const nsRect
& aDirtyRect
)
527 // paint the bar with the current text color
528 aCtx
->SetColor(mFrame
->GetStyleColor()->mColor
);
529 aCtx
->FillRect(mRect
+ aBuilder
->ToReferenceFrame(mFrame
));
533 nsMathMLFrame::DisplayBar(nsDisplayListBuilder
* aBuilder
,
534 nsIFrame
* aFrame
, const nsRect
& aRect
,
535 const nsDisplayListSet
& aLists
) {
536 if (!aFrame
->GetStyleVisibility()->IsVisible() || aRect
.IsEmpty())
539 return aLists
.Content()->AppendNewToTop(new (aBuilder
)
540 nsDisplayMathMLBar(aFrame
, aRect
));