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/. */
8 #include "nsPresContext.h"
9 #include "nsStyleContext.h"
10 #include "nsStyleConsts.h"
11 #include "nsContentUtils.h"
12 #include "nsCSSFrameConstructor.h"
13 #include "nsMathMLTokenFrame.h"
16 NS_NewMathMLTokenFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
)
18 return new (aPresShell
) nsMathMLTokenFrame(aContext
);
21 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLTokenFrame
)
23 nsMathMLTokenFrame::~nsMathMLTokenFrame()
28 nsMathMLTokenFrame::InheritAutomaticData(nsIFrame
* aParent
)
30 // let the base class get the default from our parent
31 nsMathMLContainerFrame::InheritAutomaticData(aParent
);
33 if (mContent
->Tag() != nsGkAtoms::mspace_
) {
34 // see if the directionality attribute is there
35 nsMathMLFrame::FindAttrDirectionality(mContent
, mPresentationData
);
44 nsMathMLTokenFrame::GetMathMLFrameType()
46 // treat everything other than <mi> as ordinary...
47 if (mContent
->Tag() != nsGkAtoms::mi_
) {
48 return eMathMLFrameType_Ordinary
;
51 // for <mi>, distinguish between italic and upright...
53 // mathvariant overrides fontstyle
54 // http://www.w3.org/TR/2003/REC-MathML2-20031021/chapter3.html#presm.deprecatt
55 mContent
->GetAttr(kNameSpaceID_None
,
56 nsGkAtoms::_moz_math_fontstyle_
, style
) ||
57 GetAttribute(mContent
, mPresentationData
.mstyle
, nsGkAtoms::mathvariant_
,
59 GetAttribute(mContent
, mPresentationData
.mstyle
, nsGkAtoms::fontstyle_
,
62 if (style
.EqualsLiteral("italic") || style
.EqualsLiteral("bold-italic") ||
63 style
.EqualsLiteral("script") || style
.EqualsLiteral("bold-script") ||
64 style
.EqualsLiteral("sans-serif-italic") ||
65 style
.EqualsLiteral("sans-serif-bold-italic")) {
66 return eMathMLFrameType_ItalicIdentifier
;
68 else if(style
.EqualsLiteral("invariant")) {
70 nsContentUtils::GetNodeTextContent(mContent
, false, data
);
71 eMATHVARIANT variant
= nsMathMLOperators::LookupInvariantChar(data
);
74 case eMATHVARIANT_italic
:
75 case eMATHVARIANT_bold_italic
:
76 case eMATHVARIANT_script
:
77 case eMATHVARIANT_bold_script
:
78 case eMATHVARIANT_sans_serif_italic
:
79 case eMATHVARIANT_sans_serif_bold_italic
:
80 return eMathMLFrameType_ItalicIdentifier
;
82 ; // fall through to upright
85 return eMathMLFrameType_UprightIdentifier
;
89 CompressWhitespace(nsIContent
* aContent
)
91 uint32_t numKids
= aContent
->GetChildCount();
92 for (uint32_t kid
= 0; kid
< numKids
; kid
++) {
93 nsIContent
* cont
= aContent
->GetChildAt(kid
);
94 if (cont
&& cont
->IsNodeOfType(nsINode::eTEXT
)) {
96 cont
->AppendTextTo(text
);
97 text
.CompressWhitespace();
98 cont
->SetText(text
, false); // not meant to be used if notify is needed
104 nsMathMLTokenFrame::Init(nsIContent
* aContent
,
106 nsIFrame
* aPrevInFlow
)
108 // leading and trailing whitespace doesn't count -- bug 15402
109 // brute force removal for people who do <mi> a </mi> instead of <mi>a</mi>
110 // XXX the best fix is to skip these in nsTextFrame
111 CompressWhitespace(aContent
);
113 // let the base class do its Init()
114 return nsMathMLContainerFrame::Init(aContent
, aParent
, aPrevInFlow
);
118 nsMathMLTokenFrame::SetInitialChildList(ChildListID aListID
,
119 nsFrameList
& aChildList
)
121 // First, let the base class do its work
122 nsresult rv
= nsMathMLContainerFrame::SetInitialChildList(aListID
, aChildList
);
132 nsMathMLTokenFrame::Reflow(nsPresContext
* aPresContext
,
133 nsHTMLReflowMetrics
& aDesiredSize
,
134 const nsHTMLReflowState
& aReflowState
,
135 nsReflowStatus
& aStatus
)
139 // initializations needed for empty markup like <mtag></mtag>
140 aDesiredSize
.width
= aDesiredSize
.height
= 0;
141 aDesiredSize
.ascent
= 0;
142 aDesiredSize
.mBoundingMetrics
= nsBoundingMetrics();
144 nsSize
availSize(aReflowState
.ComputedWidth(), NS_UNCONSTRAINEDSIZE
);
145 nsIFrame
* childFrame
= GetFirstPrincipalChild();
147 // ask our children to compute their bounding metrics
148 nsHTMLReflowMetrics
childDesiredSize(aDesiredSize
.mFlags
149 | NS_REFLOW_CALC_BOUNDING_METRICS
);
150 nsHTMLReflowState
childReflowState(aPresContext
, aReflowState
,
151 childFrame
, availSize
);
152 rv
= ReflowChild(childFrame
, aPresContext
, childDesiredSize
,
153 childReflowState
, aStatus
);
154 //NS_ASSERTION(NS_FRAME_IS_COMPLETE(aStatus), "bad status");
156 // Call DidReflow() for the child frames we successfully did reflow.
157 DidReflowChildren(GetFirstPrincipalChild(), childFrame
);
161 SaveReflowAndBoundingMetricsFor(childFrame
, childDesiredSize
,
162 childDesiredSize
.mBoundingMetrics
);
164 childFrame
= childFrame
->GetNextSibling();
168 // place and size children
169 FinalizeReflow(*aReflowState
.rendContext
, aDesiredSize
);
171 aStatus
= NS_FRAME_COMPLETE
;
172 NS_FRAME_SET_TRUNCATION(aStatus
, aReflowState
, aDesiredSize
);
176 // For token elements, mBoundingMetrics is computed at the ReflowToken
177 // pass, it is not computed here because our children may be text frames
178 // that do not implement the GetBoundingMetrics() interface.
179 /* virtual */ nsresult
180 nsMathMLTokenFrame::Place(nsRenderingContext
& aRenderingContext
,
182 nsHTMLReflowMetrics
& aDesiredSize
)
184 mBoundingMetrics
= nsBoundingMetrics();
185 for (nsIFrame
* childFrame
= GetFirstPrincipalChild(); childFrame
;
186 childFrame
= childFrame
->GetNextSibling()) {
187 nsHTMLReflowMetrics childSize
;
188 GetReflowAndBoundingMetricsFor(childFrame
, childSize
,
189 childSize
.mBoundingMetrics
, nullptr);
190 // compute and cache the bounding metrics
191 mBoundingMetrics
+= childSize
.mBoundingMetrics
;
194 nsRefPtr
<nsFontMetrics
> fm
;
195 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm
));
196 nscoord ascent
= fm
->MaxAscent();
197 nscoord descent
= fm
->MaxDescent();
199 aDesiredSize
.mBoundingMetrics
= mBoundingMetrics
;
200 aDesiredSize
.width
= mBoundingMetrics
.width
;
201 aDesiredSize
.ascent
= NS_MAX(mBoundingMetrics
.ascent
, ascent
);
202 aDesiredSize
.height
= aDesiredSize
.ascent
+
203 NS_MAX(mBoundingMetrics
.descent
, descent
);
207 for (nsIFrame
* childFrame
= GetFirstPrincipalChild(); childFrame
;
208 childFrame
= childFrame
->GetNextSibling()) {
209 nsHTMLReflowMetrics childSize
;
210 GetReflowAndBoundingMetricsFor(childFrame
, childSize
,
211 childSize
.mBoundingMetrics
);
213 // place and size the child; (dx,0) makes the caret happy - bug 188146
214 dy
= childSize
.height
== 0 ? 0 : aDesiredSize
.ascent
- childSize
.ascent
;
215 FinishReflowChild(childFrame
, PresContext(), nullptr, childSize
, dx
, dy
, 0);
216 dx
+= childSize
.width
;
220 SetReference(nsPoint(0, aDesiredSize
.ascent
));
226 nsMathMLTokenFrame::MarkIntrinsicWidthsDirty()
228 // this could be called due to changes in the nsTextFrame beneath us
229 // when something changed in the text content. So re-process our text
232 nsMathMLContainerFrame::MarkIntrinsicWidthsDirty();
236 nsMathMLTokenFrame::AttributeChanged(int32_t aNameSpaceID
,
240 if (nsGkAtoms::lquote_
== aAttribute
||
241 nsGkAtoms::rquote_
== aAttribute
) {
245 return nsMathMLContainerFrame::
246 AttributeChanged(aNameSpaceID
, aAttribute
, aModType
);
250 nsMathMLTokenFrame::ProcessTextData()
252 // see if the style changes from normal to italic or vice-versa
256 // explicitly request a re-resolve to pick up the change of style
257 PresContext()->PresShell()->FrameConstructor()->
258 PostRestyleEvent(mContent
->AsElement(), eRestyle_Subtree
, NS_STYLE_HINT_NONE
);
261 ///////////////////////////////////////////////////////////////////////////
262 // For <mi>, if the content is not a single character, turn the font to
263 // normal (this function will also query attributes from the mstyle hierarchy)
264 // Returns true if there is a style change.
266 // http://www.w3.org/TR/2003/REC-MathML2-20031021/chapter3.html#presm.commatt
268 // "It is important to note that only certain combinations of
269 // character data and mathvariant attribute values make sense.
271 // By design, the only cases that have an unambiguous
272 // interpretation are exactly the ones that correspond to SMP Math
273 // Alphanumeric Symbol characters, which are enumerated in Section
274 // 6.2.3 Mathematical Alphanumeric Symbols Characters. In all other
275 // cases, it is suggested that renderers ignore the value of the
276 // mathvariant attribute if it is present."
278 // There are no corresponding characters for mathvariant=normal, suggesting
279 // that this value should be ignored, but this (from the same section of
280 // Chapter 3) implies that font-style should not be inherited, but set to
281 // normal for mathvariant=normal:
283 // "In particular, inheritance of the mathvariant attribute does not follow
284 // the CSS model. The default value for this attribute is "normal"
285 // (non-slanted) for all tokens except mi. ... (The deprecated fontslant
286 // attribute also behaves this way.)"
289 nsMathMLTokenFrame::SetTextStyle()
291 if (mContent
->Tag() != nsGkAtoms::mi_
)
294 if (!mFrames
.FirstChild())
297 // Get the text content that we enclose and its length
299 nsContentUtils::GetNodeTextContent(mContent
, false, data
);
300 int32_t length
= data
.Length();
304 nsAutoString fontstyle
;
305 bool isSingleCharacter
=
307 (length
== 2 && NS_IS_HIGH_SURROGATE(data
[0]));
308 if (isSingleCharacter
&&
309 nsMathMLOperators::LookupInvariantChar(data
) != eMATHVARIANT_NONE
) {
310 // bug 65951 - a non-stylable character has its own intrinsic appearance
311 fontstyle
.AssignLiteral("invariant");
314 // Attributes override the default behavior.
316 if (!(GetAttribute(mContent
, mPresentationData
.mstyle
,
317 nsGkAtoms::mathvariant_
, value
) ||
318 GetAttribute(mContent
, mPresentationData
.mstyle
,
319 nsGkAtoms::fontstyle_
, value
))) {
320 if (!isSingleCharacter
) {
321 fontstyle
.AssignLiteral("normal");
323 else if (length
== 1 && // BMP
325 TransformVariantChar(data
[0], eMATHVARIANT_italic
).
327 // Transformation exists. Try to make the BMP character look like the
328 // styled character using the style system until bug 114365 is resolved.
329 fontstyle
.AssignLiteral("italic");
331 // else single character but there is no corresponding Math Alphanumeric
332 // Symbol character: "ignore the value of the [default] mathvariant
337 // set the _moz-math-font-style attribute without notifying that we want a reflow
338 if (fontstyle
.IsEmpty()) {
339 if (mContent
->HasAttr(kNameSpaceID_None
, nsGkAtoms::_moz_math_fontstyle_
)) {
340 mContent
->UnsetAttr(kNameSpaceID_None
, nsGkAtoms::_moz_math_fontstyle_
,
345 else if (!mContent
->AttrValueIs(kNameSpaceID_None
,
346 nsGkAtoms::_moz_math_fontstyle_
,
347 fontstyle
, eCaseMatters
)) {
348 mContent
->SetAttr(kNameSpaceID_None
, nsGkAtoms::_moz_math_fontstyle_
,
356 ///////////////////////////////////////////////////////////////////////////
357 // For <ms>, it is assumed that the mathml.css file contains two rules:
358 // ms:before { content: open-quote; }
359 // ms:after { content: close-quote; }
360 // With these two rules, the frame construction code will
361 // create inline frames that contain text frames which themselves
362 // contain the text content of the quotes.
363 // So the main idea in this code is to see if there are lquote and
364 // rquote attributes. If these are there, we ovewrite the default
365 // quotes in the text frames.
366 // XXX this is somewhat bogus, we probably should map lquote and rquote
367 // to 'content' style rules
369 // But what if the mathml.css file wasn't loaded?
370 // We also check that we are not relying on null pointers...
373 SetQuote(nsIFrame
* aFrame
, nsString
& aValue
, bool aNotify
)
378 nsIFrame
* textFrame
= aFrame
->GetFirstPrincipalChild();
382 nsIContent
* quoteContent
= textFrame
->GetContent();
383 if (!quoteContent
->IsNodeOfType(nsINode::eTEXT
))
386 quoteContent
->SetText(aValue
, aNotify
);
390 nsMathMLTokenFrame::SetQuotes(bool aNotify
)
392 if (mContent
->Tag() != nsGkAtoms::ms_
)
397 if (GetAttribute(mContent
, mPresentationData
.mstyle
,
398 nsGkAtoms::lquote_
, value
)) {
399 SetQuote(nsLayoutUtils::GetBeforeFrame(this), value
, aNotify
);
402 if (GetAttribute(mContent
, mPresentationData
.mstyle
,
403 nsGkAtoms::rquote_
, value
)) {
404 SetQuote(nsLayoutUtils::GetAfterFrame(this), value
, aNotify
);