Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / layout / mathml / base / src / nsMathMLmfracFrame.cpp
blobfc421ae1bdc38208ca5f606a5acd185950ad801c
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 * Shyjan Mahamud <mahamud@cs.cmu.edu>
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 "nsFrame.h"
44 #include "nsPresContext.h"
45 #include "nsStyleContext.h"
46 #include "nsStyleConsts.h"
47 #include "nsIRenderingContext.h"
48 #include "nsIFontMetrics.h"
50 #include "nsMathMLmfencedFrame.h"
51 #include "nsMathMLmfracFrame.h"
54 // <mfrac> -- form a fraction from two subexpressions - implementation
57 // various fraction line thicknesses (multiplicative values of the default rule thickness)
59 #define THIN_FRACTION_LINE 0.5f
60 #define THIN_FRACTION_LINE_MINIMUM_PIXELS 1 // minimum of 1 pixel
62 #define MEDIUM_FRACTION_LINE 1.5f
63 #define MEDIUM_FRACTION_LINE_MINIMUM_PIXELS 2 // minimum of 2 pixels
65 #define THICK_FRACTION_LINE 2.0f
66 #define THICK_FRACTION_LINE_MINIMUM_PIXELS 4 // minimum of 4 pixels
68 // additional style context to be used by our MathMLChar.
69 #define NS_SLASH_CHAR_STYLE_CONTEXT_INDEX 0
71 static const PRUnichar kSlashChar = PRUnichar('/');
73 nsIFrame*
74 NS_NewMathMLmfracFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
76 return new (aPresShell) nsMathMLmfracFrame(aContext);
79 nsMathMLmfracFrame::~nsMathMLmfracFrame()
81 if (mSlashChar) {
82 delete mSlashChar;
83 mSlashChar = nsnull;
87 PRBool
88 nsMathMLmfracFrame::IsBevelled()
90 nsAutoString value;
91 GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::bevelled_,
92 value);
93 return value.EqualsLiteral("true");
96 NS_IMETHODIMP
97 nsMathMLmfracFrame::Init(nsIContent* aContent,
98 nsIFrame* aParent,
99 nsIFrame* aPrevInFlow)
101 nsresult rv = nsMathMLContainerFrame::Init(aContent, aParent, aPrevInFlow);
103 if (IsBevelled()) {
104 // enable the bevelled rendering
105 mSlashChar = new nsMathMLChar();
106 if (mSlashChar) {
107 nsPresContext* presContext = PresContext();
109 nsAutoString slashChar; slashChar.Assign(kSlashChar);
110 mSlashChar->SetData(presContext, slashChar);
111 ResolveMathMLCharStyle(presContext, mContent, mStyleContext, mSlashChar, PR_TRUE);
115 return rv;
118 eMathMLFrameType
119 nsMathMLmfracFrame::GetMathMLFrameType()
121 // frac is "inner" in TeXBook, Appendix G, rule 15e. See also page 170.
122 return eMathMLFrameType_Inner;
125 NS_IMETHODIMP
126 nsMathMLmfracFrame::TransmitAutomaticData()
128 // 1. The REC says:
129 // The <mfrac> element sets displaystyle to "false", or if it was already
130 // false increments scriptlevel by 1, within numerator and denominator.
131 // 2. The TeXbook (Ch 17. p.141) says the numerator inherits the compression
132 // while the denominator is compressed
133 PRBool increment = !NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags);
134 SetIncrementScriptLevel(0, increment);
135 SetIncrementScriptLevel(1, increment);
137 UpdatePresentationDataFromChildAt(0, -1,
138 ~NS_MATHML_DISPLAYSTYLE,
139 NS_MATHML_DISPLAYSTYLE);
140 UpdatePresentationDataFromChildAt(1, 1,
141 NS_MATHML_COMPRESSED,
142 NS_MATHML_COMPRESSED);
144 // if our numerator is an embellished operator, let its state bubble to us
145 GetEmbellishDataFrom(mFrames.FirstChild(), mEmbellishData);
146 if (NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData.flags)) {
147 // even when embellished, we need to record that <mfrac> won't fire
148 // Stretch() on its embellished child
149 mEmbellishData.direction = NS_STRETCH_DIRECTION_UNSUPPORTED;
152 return NS_OK;
155 nscoord
156 nsMathMLmfracFrame::CalcLineThickness(nsPresContext* aPresContext,
157 nsStyleContext* aStyleContext,
158 nsString& aThicknessAttribute,
159 nscoord onePixel,
160 nscoord aDefaultRuleThickness)
162 nscoord defaultThickness = aDefaultRuleThickness;
163 nscoord lineThickness = aDefaultRuleThickness;
164 nscoord minimumThickness = onePixel;
166 if (!aThicknessAttribute.IsEmpty()) {
167 if (aThicknessAttribute.EqualsLiteral("thin")) {
168 lineThickness = NSToCoordFloor(defaultThickness * THIN_FRACTION_LINE);
169 minimumThickness = onePixel * THIN_FRACTION_LINE_MINIMUM_PIXELS;
170 // should visually decrease by at least one pixel, if default is not a pixel
171 if (defaultThickness > onePixel && lineThickness > defaultThickness - onePixel)
172 lineThickness = defaultThickness - onePixel;
174 else if (aThicknessAttribute.EqualsLiteral("medium")) {
175 lineThickness = NSToCoordRound(defaultThickness * MEDIUM_FRACTION_LINE);
176 minimumThickness = onePixel * MEDIUM_FRACTION_LINE_MINIMUM_PIXELS;
177 // should visually increase by at least one pixel
178 if (lineThickness < defaultThickness + onePixel)
179 lineThickness = defaultThickness + onePixel;
181 else if (aThicknessAttribute.EqualsLiteral("thick")) {
182 lineThickness = NSToCoordCeil(defaultThickness * THICK_FRACTION_LINE);
183 minimumThickness = onePixel * THICK_FRACTION_LINE_MINIMUM_PIXELS;
184 // should visually increase by at least two pixels
185 if (lineThickness < defaultThickness + 2*onePixel)
186 lineThickness = defaultThickness + 2*onePixel;
188 else { // see if it is a plain number, or a percentage, or a h/v-unit like 1ex, 2px, 1em
189 nsCSSValue cssValue;
190 if (ParseNumericValue(aThicknessAttribute, cssValue)) {
191 nsCSSUnit unit = cssValue.GetUnit();
192 if (eCSSUnit_Number == unit)
193 lineThickness = nscoord(float(defaultThickness) * cssValue.GetFloatValue());
194 else if (eCSSUnit_Percent == unit)
195 lineThickness = nscoord(float(defaultThickness) * cssValue.GetPercentValue());
196 else if (eCSSUnit_Null != unit)
197 lineThickness = CalcLength(aPresContext, aStyleContext, cssValue);
202 // use minimum if the lineThickness is a non-zero value less than minimun
203 if (lineThickness && lineThickness < minimumThickness)
204 lineThickness = minimumThickness;
206 return lineThickness;
209 NS_IMETHODIMP
210 nsMathMLmfracFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
211 const nsRect& aDirtyRect,
212 const nsDisplayListSet& aLists)
214 /////////////
215 // paint the numerator and denominator
216 nsresult rv = nsMathMLContainerFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
217 NS_ENSURE_SUCCESS(rv, rv);
219 /////////////
220 // paint the fraction line
221 if (mSlashChar) {
222 // bevelled rendering
223 rv = mSlashChar->Display(aBuilder, this, aLists);
224 } else {
225 rv = DisplayBar(aBuilder, this, mLineRect, aLists);
228 return rv;
231 NS_IMETHODIMP
232 nsMathMLmfracFrame::Reflow(nsPresContext* aPresContext,
233 nsHTMLReflowMetrics& aDesiredSize,
234 const nsHTMLReflowState& aReflowState,
235 nsReflowStatus& aStatus)
237 if (mSlashChar) {
238 // bevelled rendering
239 return nsMathMLmfencedFrame::doReflow(aPresContext, aReflowState,
240 aDesiredSize, aStatus, this,
241 nsnull, nsnull, mSlashChar, 1);
244 // default rendering
245 return nsMathMLContainerFrame::Reflow(aPresContext, aDesiredSize,
246 aReflowState, aStatus);
249 /* virtual */ nscoord
250 nsMathMLmfracFrame::GetIntrinsicWidth(nsIRenderingContext* aRenderingContext)
252 if (mSlashChar) {
253 // bevelled rendering
254 return nsMathMLmfencedFrame::doGetIntrinsicWidth(aRenderingContext, this,
255 nsnull, nsnull,
256 mSlashChar, 1);
259 // default rendering
260 return nsMathMLContainerFrame::GetIntrinsicWidth(aRenderingContext);
263 nscoord
264 nsMathMLmfracFrame::FixInterFrameSpacing(nsHTMLReflowMetrics& aDesiredSize)
266 nscoord gap = nsMathMLContainerFrame::FixInterFrameSpacing(aDesiredSize);
267 if (!gap) return 0;
269 if (mSlashChar) {
270 nsRect rect;
271 mSlashChar->GetRect(rect);
272 rect.MoveBy(gap, 0);
273 mSlashChar->SetRect(rect);
275 else {
276 mLineRect.MoveBy(gap, 0);
278 return gap;
281 /* virtual */ nsresult
282 nsMathMLmfracFrame::Place(nsIRenderingContext& aRenderingContext,
283 PRBool aPlaceOrigin,
284 nsHTMLReflowMetrics& aDesiredSize)
286 ////////////////////////////////////
287 // Get the children's desired sizes
288 nsBoundingMetrics bmNum, bmDen;
289 nsHTMLReflowMetrics sizeNum;
290 nsHTMLReflowMetrics sizeDen;
291 nsIFrame* frameDen = nsnull;
292 nsIFrame* frameNum = mFrames.FirstChild();
293 if (frameNum)
294 frameDen = frameNum->GetNextSibling();
295 if (!frameNum || !frameDen || frameDen->GetNextSibling()) {
296 // report an error, encourage people to get their markups in order
297 return ReflowError(aRenderingContext, aDesiredSize);
299 GetReflowAndBoundingMetricsFor(frameNum, sizeNum, bmNum);
300 GetReflowAndBoundingMetricsFor(frameDen, sizeDen, bmDen);
302 //////////////////
303 // Get shifts
305 nsPresContext* presContext = PresContext();
306 nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
308 aRenderingContext.SetFont(GetStyleFont()->mFont, nsnull,
309 presContext->GetUserFontSet());
310 nsCOMPtr<nsIFontMetrics> fm;
311 aRenderingContext.GetFontMetrics(*getter_AddRefs(fm));
313 nscoord defaultRuleThickness, axisHeight;
314 GetRuleThickness(aRenderingContext, fm, defaultRuleThickness);
315 GetAxisHeight(aRenderingContext, fm, axisHeight);
317 // by default, leave at least one-pixel padding at either end, or use
318 // lspace & rspace that may come from <mo> if we are an embellished container
319 // (we fetch values from the core since they may use units that depend
320 // on style data, and style changes could have occurred in the core since
321 // our last visit there)
322 nsEmbellishData coreData;
323 GetEmbellishDataFrom(mEmbellishData.coreFrame, coreData);
324 nscoord leftSpace = PR_MAX(onePixel, coreData.leftSpace);
325 nscoord rightSpace = PR_MAX(onePixel, coreData.rightSpace);
327 // see if the linethickness attribute is there
328 nsAutoString value;
329 GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::linethickness_, value);
330 mLineRect.height = CalcLineThickness(presContext, mStyleContext, value,
331 onePixel, defaultRuleThickness);
332 nscoord numShift = 0;
333 nscoord denShift = 0;
335 // Rule 15b, App. G, TeXbook
336 nscoord numShift1, numShift2, numShift3;
337 nscoord denShift1, denShift2;
339 GetNumeratorShifts(fm, numShift1, numShift2, numShift3);
340 GetDenominatorShifts(fm, denShift1, denShift2);
341 if (NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags)) {
342 // C > T
343 numShift = numShift1;
344 denShift = denShift1;
346 else {
347 numShift = (0 < mLineRect.height) ? numShift2 : numShift3;
348 denShift = denShift2;
351 nscoord minClearance = 0;
352 nscoord actualClearance = 0;
354 nscoord actualRuleThickness = mLineRect.height;
356 if (0 == actualRuleThickness) {
357 // Rule 15c, App. G, TeXbook
359 // min clearance between numerator and denominator
360 minClearance = (NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags)) ?
361 7 * defaultRuleThickness : 3 * defaultRuleThickness;
362 actualClearance =
363 (numShift - bmNum.descent) - (bmDen.ascent - denShift);
364 // actualClearance should be >= minClearance
365 if (actualClearance < minClearance) {
366 nscoord halfGap = (minClearance - actualClearance)/2;
367 numShift += halfGap;
368 denShift += halfGap;
371 else {
372 // Rule 15d, App. G, TeXbook
374 // min clearance between numerator or denominator and middle of bar
376 // TeX has a different interpretation of the thickness.
377 // Try $a \above10pt b$ to see. Here is what TeX does:
378 // minClearance = (NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags)) ?
379 // 3 * actualRuleThickness : actualRuleThickness;
381 // we slightly depart from TeX here. We use the defaultRuleThickness instead
382 // of the value coming from the linethickness attribute, i.e., we recover what
383 // TeX does if the user hasn't set linethickness. But when the linethickness
384 // is set, we avoid the wide gap problem.
385 minClearance = (NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags)) ?
386 3 * defaultRuleThickness : defaultRuleThickness + onePixel;
388 // adjust numShift to maintain minClearance if needed
389 actualClearance =
390 (numShift - bmNum.descent) - (axisHeight + actualRuleThickness/2);
391 if (actualClearance < minClearance) {
392 numShift += (minClearance - actualClearance);
394 // adjust denShift to maintain minClearance if needed
395 actualClearance =
396 (axisHeight - actualRuleThickness/2) - (bmDen.ascent - denShift);
397 if (actualClearance < minClearance) {
398 denShift += (minClearance - actualClearance);
402 //////////////////
403 // Place Children
405 // XXX Need revisiting the width. TeX uses the exact width
406 // e.g. in $$\huge\frac{\displaystyle\int}{i}$$
407 nscoord width = PR_MAX(bmNum.width, bmDen.width);
408 nscoord dxNum = leftSpace + (width - sizeNum.width)/2;
409 nscoord dxDen = leftSpace + (width - sizeDen.width)/2;
410 width += leftSpace + rightSpace;
412 // see if the numalign attribute is there
413 GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::numalign_,
414 value);
415 if (value.EqualsLiteral("left"))
416 dxNum = leftSpace;
417 else if (value.EqualsLiteral("right"))
418 dxNum = width - rightSpace - sizeNum.width;
420 // see if the denomalign attribute is there
421 GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::denomalign_,
422 value);
423 if (value.EqualsLiteral("left"))
424 dxDen = leftSpace;
425 else if (value.EqualsLiteral("right"))
426 dxDen = width - rightSpace - sizeDen.width;
428 mBoundingMetrics.rightBearing =
429 PR_MAX(dxNum + bmNum.rightBearing, dxDen + bmDen.rightBearing);
430 if (mBoundingMetrics.rightBearing < width - rightSpace)
431 mBoundingMetrics.rightBearing = width - rightSpace;
432 mBoundingMetrics.leftBearing =
433 PR_MIN(dxNum + bmNum.leftBearing, dxDen + bmDen.leftBearing);
434 if (mBoundingMetrics.leftBearing > leftSpace)
435 mBoundingMetrics.leftBearing = leftSpace;
436 mBoundingMetrics.ascent = bmNum.ascent + numShift;
437 mBoundingMetrics.descent = bmDen.descent + denShift;
438 mBoundingMetrics.width = width;
440 aDesiredSize.ascent = sizeNum.ascent + numShift;
441 aDesiredSize.height = aDesiredSize.ascent +
442 sizeDen.height - sizeDen.ascent + denShift;
443 aDesiredSize.width = mBoundingMetrics.width;
444 aDesiredSize.mBoundingMetrics = mBoundingMetrics;
446 mReference.x = 0;
447 mReference.y = aDesiredSize.ascent;
449 if (aPlaceOrigin) {
450 nscoord dy;
451 // place numerator
452 dy = 0;
453 FinishReflowChild(frameNum, presContext, nsnull, sizeNum, dxNum, dy, 0);
454 // place denominator
455 dy = aDesiredSize.height - sizeDen.height;
456 FinishReflowChild(frameDen, presContext, nsnull, sizeDen, dxDen, dy, 0);
457 // place the fraction bar - dy is top of bar
458 dy = aDesiredSize.ascent - (axisHeight + actualRuleThickness/2);
459 mLineRect.SetRect(leftSpace, dy, width - (leftSpace + rightSpace), actualRuleThickness);
462 return NS_OK;
465 NS_IMETHODIMP
466 nsMathMLmfracFrame::AttributeChanged(PRInt32 aNameSpaceID,
467 nsIAtom* aAttribute,
468 PRInt32 aModType)
470 if (nsGkAtoms::bevelled_ == aAttribute) {
471 if (!IsBevelled()) {
472 // disable the bevelled rendering
473 if (mSlashChar) {
474 delete mSlashChar;
475 mSlashChar = nsnull;
478 else {
479 // enable the bevelled rendering
480 if (!mSlashChar) {
481 mSlashChar = new nsMathMLChar();
482 if (mSlashChar) {
483 nsPresContext* presContext = PresContext();
484 nsAutoString slashChar; slashChar.Assign(kSlashChar);
485 mSlashChar->SetData(presContext, slashChar);
486 ResolveMathMLCharStyle(presContext, mContent, mStyleContext, mSlashChar, PR_TRUE);
491 return nsMathMLContainerFrame::
492 AttributeChanged(aNameSpaceID, aAttribute, aModType);
495 NS_IMETHODIMP
496 nsMathMLmfracFrame::UpdatePresentationDataFromChildAt(PRInt32 aFirstIndex,
497 PRInt32 aLastIndex,
498 PRUint32 aFlagsValues,
499 PRUint32 aFlagsToUpdate)
501 // The REC says "The <mfrac> element sets displaystyle to "false" within
502 // numerator and denominator"
503 #if 0
504 // At one point I thought that it meant that the displaystyle state of
505 // the numerator and denominator cannot be modified by an ancestor, i.e.,
506 // to change the displaystlye, one has to use displaystyle="true" with mstyle:
507 // <mfrac> <mstyle>numerator</mstyle> <mstyle>denominator</mstyle> </mfrac>
509 // Commenting out for now until it is clear what the intention really is.
510 // See also the variants for <mover>, <munder>, <munderover>
512 aFlagsToUpdate &= ~NS_MATHML_DISPLAYSTYLE;
513 aFlagsValues &= ~NS_MATHML_DISPLAYSTYLE;
514 #endif
515 return nsMathMLContainerFrame::
516 UpdatePresentationDataFromChildAt(aFirstIndex, aLastIndex,
517 aFlagsValues, aFlagsToUpdate);
520 // ----------------------
521 // the Style System will use these to pass the proper style context to our MathMLChar
522 nsStyleContext*
523 nsMathMLmfracFrame::GetAdditionalStyleContext(PRInt32 aIndex) const
525 if (!mSlashChar) {
526 return nsnull;
528 switch (aIndex) {
529 case NS_SLASH_CHAR_STYLE_CONTEXT_INDEX:
530 return mSlashChar->GetStyleContext();
531 break;
532 default:
533 return nsnull;
537 void
538 nsMathMLmfracFrame::SetAdditionalStyleContext(PRInt32 aIndex,
539 nsStyleContext* aStyleContext)
541 if (!mSlashChar) {
542 return;
544 switch (aIndex) {
545 case NS_SLASH_CHAR_STYLE_CONTEXT_INDEX:
546 mSlashChar->SetStyleContext(aStyleContext);
547 break;