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>
24 * David J. Fiddes <D.J.Fiddes@hw.ac.uk>
25 * Karl Tomlinson <karlt+@karlt.net>, Mozilla Corporation
27 * Alternatively, the contents of this file may be used under the terms of
28 * either of the GNU General Public License Version 2 or later (the "GPL"),
29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
43 #include "nsCRT.h" // to get NS_IS_SPACE
45 #include "nsPresContext.h"
46 #include "nsStyleContext.h"
47 #include "nsStyleConsts.h"
48 #include "nsIRenderingContext.h"
49 #include "nsIFontMetrics.h"
51 #include "nsMathMLmpaddedFrame.h"
54 // <mpadded> -- adjust space around content - implementation
57 #define NS_MATHML_SIGN_INVALID -1 // if the attribute is not there
58 #define NS_MATHML_SIGN_UNSPECIFIED 0
59 #define NS_MATHML_SIGN_MINUS 1
60 #define NS_MATHML_SIGN_PLUS 2
62 #define NS_MATHML_PSEUDO_UNIT_UNSPECIFIED 0
63 #define NS_MATHML_PSEUDO_UNIT_ITSELF 1 // special
64 #define NS_MATHML_PSEUDO_UNIT_WIDTH 2
65 #define NS_MATHML_PSEUDO_UNIT_HEIGHT 3
66 #define NS_MATHML_PSEUDO_UNIT_DEPTH 4
67 #define NS_MATHML_PSEUDO_UNIT_LSPACE 5
68 #define NS_MATHML_PSEUDO_UNIT_NAMEDSPACE 6
71 NS_NewMathMLmpaddedFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
)
73 return new (aPresShell
) nsMathMLmpaddedFrame(aContext
);
76 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmpaddedFrame
)
78 nsMathMLmpaddedFrame::~nsMathMLmpaddedFrame()
83 nsMathMLmpaddedFrame::InheritAutomaticData(nsIFrame
* aParent
)
85 // let the base class get the default from our parent
86 nsMathMLContainerFrame::InheritAutomaticData(aParent
);
88 mPresentationData
.flags
|= NS_MATHML_STRETCH_ALL_CHILDREN_VERTICALLY
;
94 nsMathMLmpaddedFrame::ProcessAttributes()
99 width = [+|-] unsigned-number (% [pseudo-unit] | pseudo-unit | h-unit | namedspace)
100 height= [+|-] unsigned-number (% [pseudo-unit] | pseudo-unit | v-unit)
101 depth = [+|-] unsigned-number (% [pseudo-unit] | pseudo-unit | v-unit)
102 lspace= [+|-] unsigned-number (% [pseudo-unit] | pseudo-unit | h-unit)
108 There is one exceptional element, <mpadded>, whose attributes cannot be
109 set with <mstyle>. When the attributes width, height and depth are specified
110 on an <mstyle> element, they apply only to the <mspace/> element. Similarly,
111 when lspace is set with <mstyle>, it applies only to the <mo> element.
114 // See if attributes are local, don't access mstyle !
117 mWidthSign
= NS_MATHML_SIGN_INVALID
;
118 GetAttribute(mContent
, nsnull
, nsGkAtoms::width
, value
);
119 if (!value
.IsEmpty()) {
120 ParseAttribute(value
, mWidthSign
, mWidth
, mWidthPseudoUnit
);
124 mHeightSign
= NS_MATHML_SIGN_INVALID
;
125 GetAttribute(mContent
, nsnull
, nsGkAtoms::height
, value
);
126 if (!value
.IsEmpty()) {
127 ParseAttribute(value
, mHeightSign
, mHeight
, mHeightPseudoUnit
);
131 mDepthSign
= NS_MATHML_SIGN_INVALID
;
132 GetAttribute(mContent
, nsnull
, nsGkAtoms::depth_
, value
);
133 if (!value
.IsEmpty()) {
134 ParseAttribute(value
, mDepthSign
, mDepth
, mDepthPseudoUnit
);
138 mLeftSpaceSign
= NS_MATHML_SIGN_INVALID
;
139 GetAttribute(mContent
, nsnull
, nsGkAtoms::lspace_
, value
);
140 if (!value
.IsEmpty()) {
141 ParseAttribute(value
, mLeftSpaceSign
, mLeftSpace
, mLeftSpacePseudoUnit
);
145 // parse an input string in the following format (see bug 148326 for testcases):
146 // [+|-] unsigned-number (% [pseudo-unit] | pseudo-unit | css-unit | namedspace)
148 nsMathMLmpaddedFrame::ParseAttribute(nsString
& aString
,
150 nsCSSValue
& aCSSValue
,
151 PRInt32
& aPseudoUnit
)
154 aSign
= NS_MATHML_SIGN_INVALID
;
155 aPseudoUnit
= NS_MATHML_PSEUDO_UNIT_UNSPECIFIED
;
156 aString
.CompressWhitespace(); // aString is not a const in this code
158 PRInt32 stringLength
= aString
.Length();
162 nsAutoString number
, unit
;
164 //////////////////////
165 // see if the sign is there
169 if (aString
[0] == '+') {
170 aSign
= NS_MATHML_SIGN_PLUS
;
173 else if (aString
[0] == '-') {
174 aSign
= NS_MATHML_SIGN_MINUS
;
178 aSign
= NS_MATHML_SIGN_UNSPECIFIED
;
180 // skip any space after the sign
181 if (i
< stringLength
&& nsCRT::IsAsciiSpace(aString
[i
]))
185 PRBool gotDot
= PR_FALSE
, gotPercent
= PR_FALSE
;
186 for (; i
< stringLength
; i
++) {
187 PRUnichar c
= aString
[i
];
188 if (gotDot
&& c
== '.') {
189 // error - two dots encountered
190 aSign
= NS_MATHML_SIGN_INVALID
;
196 else if (!nsCRT::IsAsciiDigit(c
)) {
202 // catch error if we didn't enter the loop above... we could simply initialize
203 // floatValue = 1, to cater for cases such as width="height", but that wouldn't
204 // be in line with the spec which requires an explicit number
205 if (number
.IsEmpty()) {
207 printf("mpadded: attribute with bad numeric value: %s\n",
208 NS_LossyConvertUTF16toASCII(aString
).get());
210 aSign
= NS_MATHML_SIGN_INVALID
;
215 float floatValue
= number
.ToFloat(&errorCode
);
217 aSign
= NS_MATHML_SIGN_INVALID
;
221 // skip any space after the number
222 if (i
< stringLength
&& nsCRT::IsAsciiSpace(aString
[i
]))
225 // see if this is a percentage-based value
226 if (i
< stringLength
&& aString
[i
] == '%') {
228 gotPercent
= PR_TRUE
;
230 // skip any space after the '%' sign
231 if (i
< stringLength
&& nsCRT::IsAsciiSpace(aString
[i
]))
235 // the remainder now should be a css-unit, or a pseudo-unit, or a named-space
236 aString
.Right(unit
, stringLength
- i
);
238 if (unit
.IsEmpty()) {
239 // also cater for the edge case of "0" for which the unit is optional
240 if (gotPercent
|| !floatValue
) {
241 aCSSValue
.SetPercentValue(floatValue
/ 100.0f
);
242 aPseudoUnit
= NS_MATHML_PSEUDO_UNIT_ITSELF
;
247 // no explicit CSS unit and no explicit pseudo-unit...
248 // In this case, the MathML REC suggests taking ems for
249 // h-unit (width, lspace) or exs for v-unit (height, depth).
250 // Here, however, we explicitly request authors to specify
251 // the unit. This is more in line with the CSS REC (and
252 // it allows keeping the code simpler...)
256 else if (unit
.EqualsLiteral("width")) aPseudoUnit
= NS_MATHML_PSEUDO_UNIT_WIDTH
;
257 else if (unit
.EqualsLiteral("height")) aPseudoUnit
= NS_MATHML_PSEUDO_UNIT_HEIGHT
;
258 else if (unit
.EqualsLiteral("depth")) aPseudoUnit
= NS_MATHML_PSEUDO_UNIT_DEPTH
;
259 else if (unit
.EqualsLiteral("lspace")) aPseudoUnit
= NS_MATHML_PSEUDO_UNIT_LSPACE
;
260 else if (!gotPercent
) { // percentage can only apply to a pseudo-unit
262 // see if the unit is a named-space
263 // XXX nsnull in ParseNamedSpacedValue()? don't access mstyle?
264 if (ParseNamedSpaceValue(nsnull
, unit
, aCSSValue
)) {
265 // re-scale properly, and we know that the unit of the named-space is 'em'
266 floatValue
*= aCSSValue
.GetFloatValue();
267 aCSSValue
.SetFloatValue(floatValue
, eCSSUnit_EM
);
268 aPseudoUnit
= NS_MATHML_PSEUDO_UNIT_NAMEDSPACE
;
272 // see if the input was just a CSS value
273 number
.Append(unit
); // leave the sign out if it was there
274 if (ParseNumericValue(number
, aCSSValue
))
278 // if we enter here, we have a number that will act as a multiplier on a pseudo-unit
279 if (aPseudoUnit
!= NS_MATHML_PSEUDO_UNIT_UNSPECIFIED
) {
281 aCSSValue
.SetPercentValue(floatValue
/ 100.0f
);
283 aCSSValue
.SetFloatValue(floatValue
, eCSSUnit_Number
);
290 printf("mpadded: attribute with bad numeric value: %s\n",
291 NS_LossyConvertUTF16toASCII(aString
).get());
293 // if we reach here, it means we encounter an unexpected input
294 aSign
= NS_MATHML_SIGN_INVALID
;
299 nsMathMLmpaddedFrame::UpdateValue(PRInt32 aSign
,
301 const nsCSSValue
& aCSSValue
,
303 const nsBoundingMetrics
& aBoundingMetrics
,
304 nscoord
& aValueToUpdate
) const
306 nsCSSUnit unit
= aCSSValue
.GetUnit();
307 if (NS_MATHML_SIGN_INVALID
!= aSign
&& eCSSUnit_Null
!= unit
) {
308 nscoord scaler
= 0, amount
= 0;
310 if (eCSSUnit_Percent
== unit
|| eCSSUnit_Number
== unit
) {
311 switch(aPseudoUnit
) {
312 case NS_MATHML_PSEUDO_UNIT_WIDTH
:
313 scaler
= aBoundingMetrics
.width
;
316 case NS_MATHML_PSEUDO_UNIT_HEIGHT
:
317 scaler
= aBoundingMetrics
.ascent
;
320 case NS_MATHML_PSEUDO_UNIT_DEPTH
:
321 scaler
= aBoundingMetrics
.descent
;
324 case NS_MATHML_PSEUDO_UNIT_LSPACE
:
329 // if we ever reach here, it would mean something is wrong
330 // somewhere with the setup and/or the caller
331 NS_ERROR("Unexpected Pseudo Unit");
336 if (eCSSUnit_Number
== unit
)
337 amount
= NSToCoordRound(float(scaler
) * aCSSValue
.GetFloatValue());
338 else if (eCSSUnit_Percent
== unit
)
339 amount
= NSToCoordRound(float(scaler
) * aCSSValue
.GetPercentValue());
341 amount
= CalcLength(PresContext(), mStyleContext
, aCSSValue
);
343 nscoord oldValue
= aValueToUpdate
;
344 if (NS_MATHML_SIGN_PLUS
== aSign
)
345 aValueToUpdate
+= amount
;
346 else if (NS_MATHML_SIGN_MINUS
== aSign
)
347 aValueToUpdate
-= amount
;
349 aValueToUpdate
= amount
;
352 Dimensions that would be positive if the content was rendered normally
353 cannot be made negative using <mpadded>; a positive dimension is set
354 to 0 if it would otherwise become negative. Dimensions which are
355 initially 0 can be made negative
357 if (0 < oldValue
&& 0 > aValueToUpdate
)
363 nsMathMLmpaddedFrame::Reflow(nsPresContext
* aPresContext
,
364 nsHTMLReflowMetrics
& aDesiredSize
,
365 const nsHTMLReflowState
& aReflowState
,
366 nsReflowStatus
& aStatus
)
371 // Let the base class format our content like an inferred mrow
372 nsresult rv
= nsMathMLContainerFrame::Reflow(aPresContext
, aDesiredSize
,
373 aReflowState
, aStatus
);
374 //NS_ASSERTION(NS_FRAME_IS_COMPLETE(aStatus), "bad status");
378 /* virtual */ nsresult
379 nsMathMLmpaddedFrame::Place(nsIRenderingContext
& aRenderingContext
,
381 nsHTMLReflowMetrics
& aDesiredSize
)
384 nsMathMLContainerFrame::Place(aRenderingContext
, PR_FALSE
, aDesiredSize
);
385 if (NS_MATHML_HAS_ERROR(mPresentationData
.flags
) || NS_FAILED(rv
)) {
386 DidReflowChildren(GetFirstChild(nsnull
));
390 nscoord height
= mBoundingMetrics
.ascent
;
391 nscoord depth
= mBoundingMetrics
.descent
;
392 // In MathML2 (http://www.w3.org/TR/MathML2/chapter3.html#presm.mpadded),
393 // lspace is "the amount of space between the left edge of a bounding box
394 // and the start of the rendering of its contents' bounding box" and the
398 // http://www.w3.org/TR/2007/WD-MathML3-20070427/chapter3.html#id.3.3.6.2,
399 // lspace is "the amount of space between the left edge of the bounding box
400 // and the positioning poin [sic] of the mpadded element" and the default is
401 // "same as content".
403 // In both cases, "MathML renderers should ensure that, except for the
404 // effects of the attributes, relative spacing between the contents of
405 // mpadded and surrounding MathML elements is not modified by replacing an
406 // mpadded element with an mrow element with the same content."
408 // In MATHML3, "width" will be the bounding box width and "advancewidth" will
409 // refer "to the horizontal distance between the positioning point of the
410 // mpadded and the positioning point for the following content". MathML2
411 // doesn't make the distinction.
412 nscoord width
= mBoundingMetrics
.width
;
417 pseudoUnit
= (mWidthPseudoUnit
== NS_MATHML_PSEUDO_UNIT_ITSELF
)
418 ? NS_MATHML_PSEUDO_UNIT_WIDTH
: mWidthPseudoUnit
;
419 UpdateValue(mWidthSign
, pseudoUnit
, mWidth
,
420 lspace
, mBoundingMetrics
, width
);
422 // update "height" (this is the ascent in the terminology of the REC)
423 pseudoUnit
= (mHeightPseudoUnit
== NS_MATHML_PSEUDO_UNIT_ITSELF
)
424 ? NS_MATHML_PSEUDO_UNIT_HEIGHT
: mHeightPseudoUnit
;
425 UpdateValue(mHeightSign
, pseudoUnit
, mHeight
,
426 lspace
, mBoundingMetrics
, height
);
428 // update "depth" (this is the descent in the terminology of the REC)
429 pseudoUnit
= (mDepthPseudoUnit
== NS_MATHML_PSEUDO_UNIT_ITSELF
)
430 ? NS_MATHML_PSEUDO_UNIT_DEPTH
: mDepthPseudoUnit
;
431 UpdateValue(mDepthSign
, pseudoUnit
, mDepth
,
432 lspace
, mBoundingMetrics
, depth
);
434 // update lspace -- should be *last* because lspace is overwritten!!
435 pseudoUnit
= (mLeftSpacePseudoUnit
== NS_MATHML_PSEUDO_UNIT_ITSELF
)
436 ? NS_MATHML_PSEUDO_UNIT_LSPACE
: mLeftSpacePseudoUnit
;
437 UpdateValue(mLeftSpaceSign
, pseudoUnit
, mLeftSpace
,
438 lspace
, mBoundingMetrics
, lspace
);
440 // do the padding now that we have everything
441 // The idea here is to maintain the invariant that <mpadded>...</mpadded> (i.e.,
442 // with no attributes) looks the same as <mrow>...</mrow>. But when there are
443 // attributes, tweak our metrics and move children to achieve the desired visual
446 if (mLeftSpaceSign
!= NS_MATHML_SIGN_INVALID
) { // there was padding on the left
447 // dismiss the left italic correction now (so that our parent won't correct us)
448 mBoundingMetrics
.leftBearing
= 0;
451 if (mLeftSpaceSign
!= NS_MATHML_SIGN_INVALID
||
452 mWidthSign
!= NS_MATHML_SIGN_INVALID
) { // there was padding on the right
453 // dismiss the right italic correction now (so that our parent won't correct us)
454 mBoundingMetrics
.width
= NS_MAX(0, lspace
+ width
);
455 mBoundingMetrics
.rightBearing
= mBoundingMetrics
.width
;
458 nscoord dy
= height
- mBoundingMetrics
.ascent
;
461 mBoundingMetrics
.ascent
= height
;
462 mBoundingMetrics
.descent
= depth
;
464 aDesiredSize
.ascent
+= dy
;
465 aDesiredSize
.width
= mBoundingMetrics
.width
;
466 aDesiredSize
.height
+= dy
+ depth
- mBoundingMetrics
.descent
;
467 aDesiredSize
.mBoundingMetrics
= mBoundingMetrics
;
470 mReference
.y
= aDesiredSize
.ascent
;
473 // Finish reflowing child frames, positioning their origins.
474 PositionRowChildFrames(dx
, aDesiredSize
.ascent
);