Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / layout / mathml / base / src / nsMathMLmpaddedFrame.cpp
blobf261f2290c04c029e06a405ea3f375dd3248d6d7
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
13 * License.
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.
22 * Contributor(s):
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 ***** */
42 #include "nsCOMPtr.h"
43 #include "nsCRT.h" // to get NS_IS_SPACE
44 #include "nsFrame.h"
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
70 nsIFrame*
71 NS_NewMathMLmpaddedFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
73 return new (aPresShell) nsMathMLmpaddedFrame(aContext);
76 nsMathMLmpaddedFrame::~nsMathMLmpaddedFrame()
80 NS_IMETHODIMP
81 nsMathMLmpaddedFrame::InheritAutomaticData(nsIFrame* aParent)
83 // let the base class get the default from our parent
84 nsMathMLContainerFrame::InheritAutomaticData(aParent);
86 mPresentationData.flags |= NS_MATHML_STRETCH_ALL_CHILDREN_VERTICALLY;
88 return NS_OK;
91 void
92 nsMathMLmpaddedFrame::ProcessAttributes()
95 parse the attributes
97 width = [+|-] unsigned-number (% [pseudo-unit] | pseudo-unit | h-unit | namedspace)
98 height= [+|-] unsigned-number (% [pseudo-unit] | pseudo-unit | v-unit)
99 depth = [+|-] unsigned-number (% [pseudo-unit] | pseudo-unit | v-unit)
100 lspace= [+|-] unsigned-number (% [pseudo-unit] | pseudo-unit | h-unit)
103 nsAutoString value;
105 /* The REC says:
106 There is one exceptional element, <mpadded>, whose attributes cannot be
107 set with <mstyle>. When the attributes width, height and depth are specified
108 on an <mstyle> element, they apply only to the <mspace/> element. Similarly,
109 when lspace is set with <mstyle>, it applies only to the <mo> element.
112 // See if attributes are local, don't access mstyle !
114 // width
115 mWidthSign = NS_MATHML_SIGN_INVALID;
116 GetAttribute(mContent, nsnull, nsGkAtoms::width, value);
117 if (!value.IsEmpty()) {
118 ParseAttribute(value, mWidthSign, mWidth, mWidthPseudoUnit);
121 // height
122 mHeightSign = NS_MATHML_SIGN_INVALID;
123 GetAttribute(mContent, nsnull, nsGkAtoms::height, value);
124 if (!value.IsEmpty()) {
125 ParseAttribute(value, mHeightSign, mHeight, mHeightPseudoUnit);
128 // depth
129 mDepthSign = NS_MATHML_SIGN_INVALID;
130 GetAttribute(mContent, nsnull, nsGkAtoms::depth_, value);
131 if (!value.IsEmpty()) {
132 ParseAttribute(value, mDepthSign, mDepth, mDepthPseudoUnit);
135 // lspace
136 mLeftSpaceSign = NS_MATHML_SIGN_INVALID;
137 GetAttribute(mContent, nsnull, nsGkAtoms::lspace_, value);
138 if (!value.IsEmpty()) {
139 ParseAttribute(value, mLeftSpaceSign, mLeftSpace, mLeftSpacePseudoUnit);
143 // parse an input string in the following format (see bug 148326 for testcases):
144 // [+|-] unsigned-number (% [pseudo-unit] | pseudo-unit | css-unit | namedspace)
145 PRBool
146 nsMathMLmpaddedFrame::ParseAttribute(nsString& aString,
147 PRInt32& aSign,
148 nsCSSValue& aCSSValue,
149 PRInt32& aPseudoUnit)
151 aCSSValue.Reset();
152 aSign = NS_MATHML_SIGN_INVALID;
153 aPseudoUnit = NS_MATHML_PSEUDO_UNIT_UNSPECIFIED;
154 aString.CompressWhitespace(); // aString is not a const in this code
156 PRInt32 stringLength = aString.Length();
157 if (!stringLength)
158 return PR_FALSE;
160 nsAutoString number, unit;
162 //////////////////////
163 // see if the sign is there
165 PRInt32 i = 0;
167 if (aString[0] == '+') {
168 aSign = NS_MATHML_SIGN_PLUS;
169 i++;
171 else if (aString[0] == '-') {
172 aSign = NS_MATHML_SIGN_MINUS;
173 i++;
175 else
176 aSign = NS_MATHML_SIGN_UNSPECIFIED;
178 // skip any space after the sign
179 if (i < stringLength && nsCRT::IsAsciiSpace(aString[i]))
180 i++;
182 // get the number
183 PRBool gotDot = PR_FALSE, gotPercent = PR_FALSE;
184 for (; i < stringLength; i++) {
185 PRUnichar c = aString[i];
186 if (gotDot && c == '.') {
187 // error - two dots encountered
188 aSign = NS_MATHML_SIGN_INVALID;
189 return PR_FALSE;
192 if (c == '.')
193 gotDot = PR_TRUE;
194 else if (!nsCRT::IsAsciiDigit(c)) {
195 break;
197 number.Append(c);
200 // catch error if we didn't enter the loop above... we could simply initialize
201 // floatValue = 1, to cater for cases such as width="height", but that wouldn't
202 // be in line with the spec which requires an explicit number
203 if (number.IsEmpty()) {
204 #ifdef NS_DEBUG
205 printf("mpadded: attribute with bad numeric value: %s\n",
206 NS_LossyConvertUTF16toASCII(aString).get());
207 #endif
208 aSign = NS_MATHML_SIGN_INVALID;
209 return PR_FALSE;
212 PRInt32 errorCode;
213 float floatValue = number.ToFloat(&errorCode);
214 if (errorCode) {
215 aSign = NS_MATHML_SIGN_INVALID;
216 return PR_FALSE;
219 // skip any space after the number
220 if (i < stringLength && nsCRT::IsAsciiSpace(aString[i]))
221 i++;
223 // see if this is a percentage-based value
224 if (i < stringLength && aString[i] == '%') {
225 i++;
226 gotPercent = PR_TRUE;
228 // skip any space after the '%' sign
229 if (i < stringLength && nsCRT::IsAsciiSpace(aString[i]))
230 i++;
233 // the remainder now should be a css-unit, or a pseudo-unit, or a named-space
234 aString.Right(unit, stringLength - i);
236 if (unit.IsEmpty()) {
237 // also cater for the edge case of "0" for which the unit is optional
238 if (gotPercent || !floatValue) {
239 aCSSValue.SetPercentValue(floatValue / 100.0f);
240 aPseudoUnit = NS_MATHML_PSEUDO_UNIT_ITSELF;
241 return PR_TRUE;
244 else {
245 // no explicit CSS unit and no explicit pseudo-unit...
246 // In this case, the MathML REC suggests taking ems for
247 // h-unit (width, lspace) or exs for v-unit (height, depth).
248 // Here, however, we explicitly request authors to specify
249 // the unit. This is more in line with the CSS REC (and
250 // it allows keeping the code simpler...)
254 else if (unit.EqualsLiteral("width")) aPseudoUnit = NS_MATHML_PSEUDO_UNIT_WIDTH;
255 else if (unit.EqualsLiteral("height")) aPseudoUnit = NS_MATHML_PSEUDO_UNIT_HEIGHT;
256 else if (unit.EqualsLiteral("depth")) aPseudoUnit = NS_MATHML_PSEUDO_UNIT_DEPTH;
257 else if (unit.EqualsLiteral("lspace")) aPseudoUnit = NS_MATHML_PSEUDO_UNIT_LSPACE;
258 else if (!gotPercent) { // percentage can only apply to a pseudo-unit
260 // see if the unit is a named-space
261 // XXX nsnull in ParseNamedSpacedValue()? don't access mstyle?
262 if (ParseNamedSpaceValue(nsnull, unit, aCSSValue)) {
263 // re-scale properly, and we know that the unit of the named-space is 'em'
264 floatValue *= aCSSValue.GetFloatValue();
265 aCSSValue.SetFloatValue(floatValue, eCSSUnit_EM);
266 aPseudoUnit = NS_MATHML_PSEUDO_UNIT_NAMEDSPACE;
267 return PR_TRUE;
270 // see if the input was just a CSS value
271 number.Append(unit); // leave the sign out if it was there
272 if (ParseNumericValue(number, aCSSValue))
273 return PR_TRUE;
276 // if we enter here, we have a number that will act as a multiplier on a pseudo-unit
277 if (aPseudoUnit != NS_MATHML_PSEUDO_UNIT_UNSPECIFIED) {
278 if (gotPercent)
279 aCSSValue.SetPercentValue(floatValue / 100.0f);
280 else
281 aCSSValue.SetFloatValue(floatValue, eCSSUnit_Number);
283 return PR_TRUE;
287 #ifdef NS_DEBUG
288 printf("mpadded: attribute with bad numeric value: %s\n",
289 NS_LossyConvertUTF16toASCII(aString).get());
290 #endif
291 // if we reach here, it means we encounter an unexpected input
292 aSign = NS_MATHML_SIGN_INVALID;
293 return PR_FALSE;
296 void
297 nsMathMLmpaddedFrame::UpdateValue(PRInt32 aSign,
298 PRInt32 aPseudoUnit,
299 const nsCSSValue& aCSSValue,
300 nscoord aLeftSpace,
301 const nsBoundingMetrics& aBoundingMetrics,
302 nscoord& aValueToUpdate) const
304 nsCSSUnit unit = aCSSValue.GetUnit();
305 if (NS_MATHML_SIGN_INVALID != aSign && eCSSUnit_Null != unit) {
306 nscoord scaler = 0, amount = 0;
308 if (eCSSUnit_Percent == unit || eCSSUnit_Number == unit) {
309 switch(aPseudoUnit) {
310 case NS_MATHML_PSEUDO_UNIT_WIDTH:
311 scaler = aBoundingMetrics.width;
312 break;
314 case NS_MATHML_PSEUDO_UNIT_HEIGHT:
315 scaler = aBoundingMetrics.ascent;
316 break;
318 case NS_MATHML_PSEUDO_UNIT_DEPTH:
319 scaler = aBoundingMetrics.descent;
320 break;
322 case NS_MATHML_PSEUDO_UNIT_LSPACE:
323 scaler = aLeftSpace;
324 break;
326 default:
327 // if we ever reach here, it would mean something is wrong
328 // somewhere with the setup and/or the caller
329 NS_ASSERTION(0, "Unexpected Pseudo Unit");
330 return;
334 if (eCSSUnit_Number == unit)
335 amount = NSToCoordRound(float(scaler) * aCSSValue.GetFloatValue());
336 else if (eCSSUnit_Percent == unit)
337 amount = NSToCoordRound(float(scaler) * aCSSValue.GetPercentValue());
338 else
339 amount = CalcLength(PresContext(), mStyleContext, aCSSValue);
341 nscoord oldValue = aValueToUpdate;
342 if (NS_MATHML_SIGN_PLUS == aSign)
343 aValueToUpdate += amount;
344 else if (NS_MATHML_SIGN_MINUS == aSign)
345 aValueToUpdate -= amount;
346 else
347 aValueToUpdate = amount;
349 /* The REC says:
350 Dimensions that would be positive if the content was rendered normally
351 cannot be made negative using <mpadded>; a positive dimension is set
352 to 0 if it would otherwise become negative. Dimensions which are
353 initially 0 can be made negative
355 if (0 < oldValue && 0 > aValueToUpdate)
356 aValueToUpdate = 0;
360 NS_IMETHODIMP
361 nsMathMLmpaddedFrame::Reflow(nsPresContext* aPresContext,
362 nsHTMLReflowMetrics& aDesiredSize,
363 const nsHTMLReflowState& aReflowState,
364 nsReflowStatus& aStatus)
366 ProcessAttributes();
368 ///////////////
369 // Let the base class format our content like an inferred mrow
370 nsresult rv = nsMathMLContainerFrame::Reflow(aPresContext, aDesiredSize,
371 aReflowState, aStatus);
372 //NS_ASSERTION(NS_FRAME_IS_COMPLETE(aStatus), "bad status");
373 return rv;
376 /* virtual */ nsresult
377 nsMathMLmpaddedFrame::Place(nsIRenderingContext& aRenderingContext,
378 PRBool aPlaceOrigin,
379 nsHTMLReflowMetrics& aDesiredSize)
381 nsresult rv =
382 nsMathMLContainerFrame::Place(aRenderingContext, PR_FALSE, aDesiredSize);
383 if (NS_MATHML_HAS_ERROR(mPresentationData.flags) || NS_FAILED(rv)) {
384 DidReflowChildren(GetFirstChild(nsnull));
385 return rv;
388 nscoord height = mBoundingMetrics.ascent;
389 nscoord depth = mBoundingMetrics.descent;
390 // In MathML2 (http://www.w3.org/TR/MathML2/chapter3.html#presm.mpadded),
391 // lspace is "the amount of space between the left edge of a bounding box
392 // and the start of the rendering of its contents' bounding box" and the
393 // default is zero.
395 // In MathML3 draft
396 // http://www.w3.org/TR/2007/WD-MathML3-20070427/chapter3.html#id.3.3.6.2,
397 // lspace is "the amount of space between the left edge of the bounding box
398 // and the positioning poin [sic] of the mpadded element" and the default is
399 // "same as content".
401 // In both cases, "MathML renderers should ensure that, except for the
402 // effects of the attributes, relative spacing between the contents of
403 // mpadded and surrounding MathML elements is not modified by replacing an
404 // mpadded element with an mrow element with the same content."
405 nscoord lspace = 0;
406 // In MATHML3, "width" will be the bounding box width and "advancewidth" will
407 // refer "to the horizontal distance between the positioning point of the
408 // mpadded and the positioning point for the following content". MathML2
409 // doesn't make the distinction.
410 nscoord width = mBoundingMetrics.width;
412 PRInt32 pseudoUnit;
414 // update width
415 pseudoUnit = (mWidthPseudoUnit == NS_MATHML_PSEUDO_UNIT_ITSELF)
416 ? NS_MATHML_PSEUDO_UNIT_WIDTH : mWidthPseudoUnit;
417 UpdateValue(mWidthSign, pseudoUnit, mWidth,
418 lspace, mBoundingMetrics, width);
420 // update "height" (this is the ascent in the terminology of the REC)
421 pseudoUnit = (mHeightPseudoUnit == NS_MATHML_PSEUDO_UNIT_ITSELF)
422 ? NS_MATHML_PSEUDO_UNIT_HEIGHT : mHeightPseudoUnit;
423 UpdateValue(mHeightSign, pseudoUnit, mHeight,
424 lspace, mBoundingMetrics, height);
426 // update "depth" (this is the descent in the terminology of the REC)
427 pseudoUnit = (mDepthPseudoUnit == NS_MATHML_PSEUDO_UNIT_ITSELF)
428 ? NS_MATHML_PSEUDO_UNIT_DEPTH : mDepthPseudoUnit;
429 UpdateValue(mDepthSign, pseudoUnit, mDepth,
430 lspace, mBoundingMetrics, depth);
432 // update lspace -- should be *last* because lspace is overwritten!!
433 pseudoUnit = (mLeftSpacePseudoUnit == NS_MATHML_PSEUDO_UNIT_ITSELF)
434 ? NS_MATHML_PSEUDO_UNIT_LSPACE : mLeftSpacePseudoUnit;
435 UpdateValue(mLeftSpaceSign, pseudoUnit, mLeftSpace,
436 lspace, mBoundingMetrics, lspace);
438 // do the padding now that we have everything
439 // The idea here is to maintain the invariant that <mpadded>...</mpadded> (i.e.,
440 // with no attributes) looks the same as <mrow>...</mrow>. But when there are
441 // attributes, tweak our metrics and move children to achieve the desired visual
442 // effects.
444 if (mLeftSpaceSign != NS_MATHML_SIGN_INVALID) { // there was padding on the left
445 // dismiss the left italic correction now (so that our parent won't correct us)
446 mBoundingMetrics.leftBearing = 0;
449 if (mLeftSpaceSign != NS_MATHML_SIGN_INVALID ||
450 mWidthSign != NS_MATHML_SIGN_INVALID) { // there was padding on the right
451 // dismiss the right italic correction now (so that our parent won't correct us)
452 mBoundingMetrics.width = PR_MAX(0, lspace + width);
453 mBoundingMetrics.rightBearing = mBoundingMetrics.width;
456 nscoord dy = height - mBoundingMetrics.ascent;
457 nscoord dx = lspace;
459 mBoundingMetrics.ascent = height;
460 mBoundingMetrics.descent = depth;
462 aDesiredSize.ascent += dy;
463 aDesiredSize.width = mBoundingMetrics.width;
464 aDesiredSize.height += dy + depth - mBoundingMetrics.descent;
465 aDesiredSize.mBoundingMetrics = mBoundingMetrics;
467 mReference.x = 0;
468 mReference.y = aDesiredSize.ascent;
470 if (aPlaceOrigin) {
471 // Finish reflowing child frames, positioning their origins.
472 PositionRowChildFrames(dx, aDesiredSize.ascent);
475 return NS_OK;