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 * Pierre Phaneuf <pp@ludusdesign.com>
26 * Karl Tomlinson <karlt+@karlt.net>, Mozilla Corporation
28 * Alternatively, the contents of this file may be used under the terms of
29 * either of the GNU General Public License Version 2 or later (the "GPL"),
30 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 * in which case the provisions of the GPL or the LGPL are applicable instead
32 * of those above. If you wish to allow use of your version of this file only
33 * under the terms of either the GPL or the LGPL, and not to allow others to
34 * use your version of this file under the terms of the MPL, indicate your
35 * decision by deleting the provisions above and replace them with the notice
36 * and other provisions required by the GPL or the LGPL. If you do not delete
37 * the provisions above, a recipient may use your version of this file under
38 * the terms of any one of the MPL, the GPL or the LGPL.
40 * ***** END LICENSE BLOCK ***** */
43 #include "nsHTMLParts.h"
45 #include "nsPresContext.h"
46 #include "nsIPresShell.h"
47 #include "nsCSSAnonBoxes.h"
48 #include "nsStyleContext.h"
49 #include "nsStyleConsts.h"
50 #include "nsINameSpaceManager.h"
51 #include "nsIRenderingContext.h"
52 #include "nsIFontMetrics.h"
54 #include "nsIDOMText.h"
55 #include "nsIDOMMutationEvent.h"
56 #include "nsFrameManager.h"
57 #include "nsStyleChangeList.h"
59 #include "nsGkAtoms.h"
60 #include "nsMathMLParts.h"
61 #include "nsMathMLContainerFrame.h"
62 #include "nsAutoPtr.h"
63 #include "nsStyleSet.h"
64 #include "nsDisplayList.h"
65 #include "nsCSSFrameConstructor.h"
66 #include "nsIReflowCallback.h"
68 NS_DEFINE_CID(kInlineFrameCID
, NS_INLINE_FRAME_CID
);
71 // nsMathMLContainerFrame implementation
75 // =============================================================================
77 NS_IMPL_ADDREF_INHERITED(nsMathMLContainerFrame
, nsMathMLFrame
)
78 NS_IMPL_RELEASE_INHERITED(nsMathMLContainerFrame
, nsMathMLFrame
)
79 NS_IMPL_QUERY_INTERFACE_INHERITED1(nsMathMLContainerFrame
, nsHTMLContainerFrame
, nsMathMLFrame
)
81 // =============================================================================
84 // provide a feedback to the user when a frame with bad markup can not be rendered
86 nsMathMLContainerFrame::ReflowError(nsIRenderingContext
& aRenderingContext
,
87 nsHTMLReflowMetrics
& aDesiredSize
)
91 // clear all other flags and record that there is an error with this frame
92 mEmbellishData
.flags
= 0;
93 mPresentationData
.flags
= NS_MATHML_ERROR
;
97 nsLayoutUtils::SetFontFromStyle(&aRenderingContext
, GetStyleContext());
100 nsAutoString errorMsg
; errorMsg
.AssignLiteral("invalid-markup");
101 rv
= aRenderingContext
.GetBoundingMetrics(errorMsg
.get(),
102 PRUint32(errorMsg
.Length()),
105 NS_WARNING("GetBoundingMetrics failed");
106 aDesiredSize
.width
= aDesiredSize
.height
= 0;
107 aDesiredSize
.ascent
= 0;
112 nsCOMPtr
<nsIFontMetrics
> fm
;
113 aRenderingContext
.GetFontMetrics(*getter_AddRefs(fm
));
114 fm
->GetMaxAscent(aDesiredSize
.ascent
);
116 fm
->GetMaxDescent(descent
);
117 aDesiredSize
.height
= aDesiredSize
.ascent
+ descent
;
118 aDesiredSize
.width
= mBoundingMetrics
.width
;
120 // Also return our bounding metrics
121 aDesiredSize
.mBoundingMetrics
= mBoundingMetrics
;
126 class nsDisplayMathMLError
: public nsDisplayItem
{
128 nsDisplayMathMLError(nsIFrame
* aFrame
)
129 : nsDisplayItem(aFrame
) {
130 MOZ_COUNT_CTOR(nsDisplayMathMLError
);
132 #ifdef NS_BUILD_REFCNT_LOGGING
133 virtual ~nsDisplayMathMLError() {
134 MOZ_COUNT_DTOR(nsDisplayMathMLError
);
138 virtual void Paint(nsDisplayListBuilder
* aBuilder
, nsIRenderingContext
* aCtx
,
139 const nsRect
& aDirtyRect
);
140 NS_DISPLAY_DECL_NAME("MathMLError")
143 void nsDisplayMathMLError::Paint(nsDisplayListBuilder
* aBuilder
,
144 nsIRenderingContext
* aCtx
, const nsRect
& aDirtyRect
)
146 // Set color and font ...
147 nsLayoutUtils::SetFontFromStyle(aCtx
, mFrame
->GetStyleContext());
149 nsPoint pt
= aBuilder
->ToReferenceFrame(mFrame
);
150 aCtx
->SetColor(NS_RGB(255,0,0));
151 aCtx
->FillRect(nsRect(pt
, mFrame
->GetSize()));
152 aCtx
->SetColor(NS_RGB(255,255,255));
155 nsCOMPtr
<nsIFontMetrics
> fm
;
156 aCtx
->GetFontMetrics(*getter_AddRefs(fm
));
157 fm
->GetMaxAscent(ascent
);
159 nsAutoString errorMsg
; errorMsg
.AssignLiteral("invalid-markup");
160 aCtx
->DrawString(errorMsg
.get(), PRUint32(errorMsg
.Length()), pt
.x
, pt
.y
+ascent
);
164 * nsIMathMLFrame - support methods for stretchy elements
165 * =============================================================================
169 IsForeignChild(const nsIFrame
* aFrame
)
171 // This counts nsMathMLmathBlockFrame as a foreign child, because it
173 return !(aFrame
->IsFrameOfType(nsIFrame::eMathML
)) ||
174 aFrame
->GetType() == nsGkAtoms::blockFrame
;
178 DeleteHTMLReflowMetrics(void *aObject
, nsIAtom
*aPropertyName
,
179 void *aPropertyValue
, void *aData
)
181 delete static_cast<nsHTMLReflowMetrics
*>(aPropertyValue
);
185 nsMathMLContainerFrame::SaveReflowAndBoundingMetricsFor(nsIFrame
* aFrame
,
186 const nsHTMLReflowMetrics
& aReflowMetrics
,
187 const nsBoundingMetrics
& aBoundingMetrics
)
189 nsHTMLReflowMetrics
*metrics
= new nsHTMLReflowMetrics(aReflowMetrics
);
190 metrics
->mBoundingMetrics
= aBoundingMetrics
;
191 aFrame
->SetProperty(nsGkAtoms::HTMLReflowMetricsProperty
, metrics
,
192 DeleteHTMLReflowMetrics
);
195 // helper method to facilitate getting the reflow and bounding metrics
197 nsMathMLContainerFrame::GetReflowAndBoundingMetricsFor(nsIFrame
* aFrame
,
198 nsHTMLReflowMetrics
& aReflowMetrics
,
199 nsBoundingMetrics
& aBoundingMetrics
,
200 eMathMLFrameType
* aMathMLFrameType
)
202 NS_PRECONDITION(aFrame
, "null arg");
204 nsHTMLReflowMetrics
*metrics
= static_cast<nsHTMLReflowMetrics
*>
205 (aFrame
->GetProperty(nsGkAtoms::HTMLReflowMetricsProperty
));
207 // IMPORTANT: This function is only meant to be called in Place() methods
208 // where it is assumed that SaveReflowAndBoundingMetricsFor has recorded the
210 NS_ASSERTION(metrics
, "Didn't SaveReflowAndBoundingMetricsFor frame!");
212 aReflowMetrics
= *metrics
;
213 aBoundingMetrics
= metrics
->mBoundingMetrics
;
216 if (aMathMLFrameType
) {
217 if (!IsForeignChild(aFrame
)) {
218 nsIMathMLFrame
* mathMLFrame
;
219 CallQueryInterface(aFrame
, &mathMLFrame
);
221 *aMathMLFrameType
= mathMLFrame
->GetMathMLFrameType();
225 *aMathMLFrameType
= eMathMLFrameType_UNKNOWN
;
231 nsMathMLContainerFrame::ClearSavedChildMetrics()
233 nsIFrame
* childFrame
= mFrames
.FirstChild();
235 childFrame
->DeleteProperty(nsGkAtoms::HTMLReflowMetricsProperty
);
236 childFrame
= childFrame
->GetNextSibling();
240 // helper to get the preferred size that a container frame should use to fire
241 // the stretch on its stretchy child frames.
243 nsMathMLContainerFrame::GetPreferredStretchSize(nsIRenderingContext
& aRenderingContext
,
245 nsStretchDirection aStretchDirection
,
246 nsBoundingMetrics
& aPreferredStretchSize
)
248 if (aOptions
& STRETCH_CONSIDER_ACTUAL_SIZE
) {
249 // when our actual size is ok, just use it
250 aPreferredStretchSize
= mBoundingMetrics
;
252 else if (aOptions
& STRETCH_CONSIDER_EMBELLISHMENTS
) {
253 // compute our up-to-date size using Place()
254 nsHTMLReflowMetrics metrics
;
255 Place(aRenderingContext
, PR_FALSE
, metrics
);
256 aPreferredStretchSize
= metrics
.mBoundingMetrics
;
259 // compute a size that doesn't include embellishements
260 NS_ASSERTION(NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData
.flags
) ||
261 NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData
.flags
) ||
262 NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData
.flags
),
263 "invalid call to GetPreferredStretchSize");
264 PRBool firstTime
= PR_TRUE
;
265 nsBoundingMetrics bm
, bmChild
;
266 // XXXrbs need overloaded FirstChild() and clean integration of <maction> throughout
267 nsIFrame
* childFrame
= GetFirstChild(nsnull
);
269 // initializations in case this child happens not to be a MathML frame
270 nsIMathMLFrame
* mathMLFrame
;
271 childFrame
->QueryInterface(NS_GET_IID(nsIMathMLFrame
), (void**)&mathMLFrame
);
273 nsEmbellishData embellishData
;
274 nsPresentationData presentationData
;
275 mathMLFrame
->GetEmbellishData(embellishData
);
276 mathMLFrame
->GetPresentationData(presentationData
);
277 if (NS_MATHML_IS_EMBELLISH_OPERATOR(embellishData
.flags
) &&
278 embellishData
.direction
== aStretchDirection
&&
279 presentationData
.baseFrame
) {
280 // embellishements are not included, only consider the inner first child itself
281 // XXXkt Does that mean the core descendent frame should be used
282 // instead of the base child?
283 nsIMathMLFrame
* mathMLchildFrame
;
284 presentationData
.baseFrame
->QueryInterface(NS_GET_IID(nsIMathMLFrame
), (void**)&mathMLchildFrame
);
285 if (mathMLchildFrame
) {
286 mathMLFrame
= mathMLchildFrame
;
289 mathMLFrame
->GetBoundingMetrics(bmChild
);
292 nsHTMLReflowMetrics unused
;
293 GetReflowAndBoundingMetricsFor(childFrame
, unused
, bmChild
);
297 firstTime
= PR_FALSE
;
299 if (!NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData
.flags
) &&
300 !NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData
.flags
)) {
301 // we may get here for cases such as <msup><mo>...</mo> ... </msup>
306 if (NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData
.flags
)) {
307 // if we get here, it means this is container that will stack its children
308 // vertically and fire an horizontal stretch on each them. This is the case
309 // for \munder, \mover, \munderover. We just sum-up the size vertically.
310 bm
.descent
+= bmChild
.ascent
+ bmChild
.descent
;
311 // Sometimes non-spacing marks (when width is zero) are positioned
312 // to the left of the origin, but it is the distance between left
313 // and right bearing that is important rather than the offsets from
315 if (bmChild
.width
== 0) {
316 bmChild
.rightBearing
-= bmChild
.leftBearing
;
317 bmChild
.leftBearing
= 0;
319 if (bm
.leftBearing
> bmChild
.leftBearing
)
320 bm
.leftBearing
= bmChild
.leftBearing
;
321 if (bm
.rightBearing
< bmChild
.rightBearing
)
322 bm
.rightBearing
= bmChild
.rightBearing
;
324 else if (NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData
.flags
)) {
325 // just sum-up the sizes horizontally.
329 NS_ERROR("unexpected case in GetPreferredStretchSize");
333 childFrame
= childFrame
->GetNextSibling();
335 aPreferredStretchSize
= bm
;
340 nsMathMLContainerFrame::Stretch(nsIRenderingContext
& aRenderingContext
,
341 nsStretchDirection aStretchDirection
,
342 nsBoundingMetrics
& aContainerSize
,
343 nsHTMLReflowMetrics
& aDesiredStretchSize
)
345 if (NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData
.flags
)) {
347 if (NS_MATHML_STRETCH_WAS_DONE(mPresentationData
.flags
)) {
348 NS_WARNING("it is wrong to fire stretch more than once on a frame");
351 mPresentationData
.flags
|= NS_MATHML_STRETCH_DONE
;
353 if (NS_MATHML_HAS_ERROR(mPresentationData
.flags
)) {
354 NS_WARNING("it is wrong to fire stretch on a erroneous frame");
358 // Pass the stretch to the base child ...
360 nsIFrame
* baseFrame
= mPresentationData
.baseFrame
;
362 nsIMathMLFrame
* mathMLFrame
;
363 baseFrame
->QueryInterface(NS_GET_IID(nsIMathMLFrame
), (void**)&mathMLFrame
);
364 NS_ASSERTION(mathMLFrame
, "Something is wrong somewhere");
367 NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData
.flags
) ||
368 NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData
.flags
);
370 // And the trick is that the child's rect.x is still holding the descent,
371 // and rect.y is still holding the ascent ...
372 nsHTMLReflowMetrics
childSize(aDesiredStretchSize
);
373 GetReflowAndBoundingMetricsFor(baseFrame
, childSize
, childSize
.mBoundingMetrics
);
375 // See if we should downsize and confine the stretch to us...
376 // XXX there may be other cases where we can downsize the stretch,
377 // e.g., the first ∑ might appear big in the following situation
378 // <math xmlns='http://www.w3.org/1998/Math/MathML'>
381 // <msub><mo>∑</mo><mfrac><mi>a</mi><mi>b</mi></mfrac></msub>
382 // <msub><mo>∑</mo><mfrac><mi>a</mi><mi>b</mi></mfrac></msub>
386 nsBoundingMetrics containerSize
= aContainerSize
;
387 if (aStretchDirection
!= NS_STRETCH_DIRECTION_DEFAULT
&&
388 aStretchDirection
!= mEmbellishData
.direction
) {
389 if (mEmbellishData
.direction
== NS_STRETCH_DIRECTION_UNSUPPORTED
) {
390 containerSize
= childSize
.mBoundingMetrics
;
393 GetPreferredStretchSize(aRenderingContext
,
394 stretchAll
? STRETCH_CONSIDER_EMBELLISHMENTS
: 0,
395 mEmbellishData
.direction
, containerSize
);
399 // do the stretching...
400 mathMLFrame
->Stretch(aRenderingContext
,
401 mEmbellishData
.direction
, containerSize
, childSize
);
402 // store the updated metrics
403 SaveReflowAndBoundingMetricsFor(baseFrame
, childSize
,
404 childSize
.mBoundingMetrics
);
406 // Remember the siblings which were _deferred_.
407 // Now that this embellished child may have changed, we need to
408 // fire the stretch on its siblings using our updated size
412 nsStretchDirection stretchDir
=
413 NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData
.flags
) ?
414 NS_STRETCH_DIRECTION_VERTICAL
: NS_STRETCH_DIRECTION_HORIZONTAL
;
416 GetPreferredStretchSize(aRenderingContext
, STRETCH_CONSIDER_EMBELLISHMENTS
,
417 stretchDir
, containerSize
);
419 nsIFrame
* childFrame
= mFrames
.FirstChild();
421 if (childFrame
!= mPresentationData
.baseFrame
) {
422 childFrame
->QueryInterface(NS_GET_IID(nsIMathMLFrame
), (void**)&mathMLFrame
);
424 // retrieve the metrics that was stored at the previous pass
425 GetReflowAndBoundingMetricsFor(childFrame
,
426 childSize
, childSize
.mBoundingMetrics
);
427 // do the stretching...
428 mathMLFrame
->Stretch(aRenderingContext
, stretchDir
,
429 containerSize
, childSize
);
430 // store the updated metrics
431 SaveReflowAndBoundingMetricsFor(childFrame
, childSize
,
432 childSize
.mBoundingMetrics
);
435 childFrame
= childFrame
->GetNextSibling();
439 // re-position all our children
440 nsresult rv
= Place(aRenderingContext
, PR_TRUE
, aDesiredStretchSize
);
441 if (NS_MATHML_HAS_ERROR(mPresentationData
.flags
) || NS_FAILED(rv
)) {
442 // Make sure the child frames get their DidReflow() calls.
443 DidReflowChildren(mFrames
.FirstChild());
446 // If our parent is not embellished, it means we are the outermost embellished
447 // container and so we put the spacing, otherwise we don't include the spacing,
448 // the outermost embellished container will take care of it.
450 nsEmbellishData parentData
;
451 GetEmbellishDataFrom(mParent
, parentData
);
452 // ensure that we are the embellished child, not just a sibling
453 // (need to test coreFrame since <mfrac> resets other things)
454 if (parentData
.coreFrame
!= mEmbellishData
.coreFrame
) {
455 // (we fetch values from the core since they may use units that depend
456 // on style data, and style changes could have occurred in the core since
457 // our last visit there)
458 nsEmbellishData coreData
;
459 GetEmbellishDataFrom(mEmbellishData
.coreFrame
, coreData
);
461 mBoundingMetrics
.width
+= coreData
.leftSpace
+ coreData
.rightSpace
;
462 aDesiredStretchSize
.width
= mBoundingMetrics
.width
;
463 aDesiredStretchSize
.mBoundingMetrics
.width
= mBoundingMetrics
.width
;
465 nscoord dx
= coreData
.leftSpace
;
467 mBoundingMetrics
.leftBearing
+= dx
;
468 mBoundingMetrics
.rightBearing
+= dx
;
469 aDesiredStretchSize
.mBoundingMetrics
.leftBearing
+= dx
;
470 aDesiredStretchSize
.mBoundingMetrics
.rightBearing
+= dx
;
472 nsIFrame
* childFrame
= mFrames
.FirstChild();
474 childFrame
->SetPosition(childFrame
->GetPosition()
476 childFrame
= childFrame
->GetNextSibling();
481 // Finished with these:
482 ClearSavedChildMetrics();
483 // Set our overflow area
484 GatherAndStoreOverflow(&aDesiredStretchSize
);
492 nsMathMLContainerFrame::FinalizeReflow(nsIRenderingContext
& aRenderingContext
,
493 nsHTMLReflowMetrics
& aDesiredSize
)
495 // During reflow, we use rect.x and rect.y as placeholders for the child's ascent
496 // and descent in expectation of a stretch command. Hence we need to ensure that
497 // a stretch command will actually be fired later on, after exiting from our
498 // reflow. If the stretch is not fired, the rect.x, and rect.y will remain
499 // with inappropriate data causing children to be improperly positioned.
500 // This helper method checks to see if our parent will fire a stretch command
501 // targeted at us. If not, we go ahead and fire an involutive stretch on
502 // ourselves. This will clear all the rect.x and rect.y, and return our
506 // First, complete the post-reflow hook.
507 // We use the information in our children rectangles to position them.
508 // If placeOrigin==false, then Place() will not touch rect.x, and rect.y.
509 // They will still be holding the ascent and descent for each child.
511 // The first clause caters for any non-embellished container.
512 // The second clause is for a container which won't fire stretch even though it is
513 // embellished, e.g., as in <mfrac><mo>...</mo> ... </mfrac>, the test is convoluted
514 // because it excludes the particular case of the core <mo>...</mo> itself.
515 // (<mo> needs to fire stretch on its MathMLChar in any case to initialize it)
516 PRBool placeOrigin
= !NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData
.flags
) ||
517 (mEmbellishData
.coreFrame
!= this && !mPresentationData
.baseFrame
&&
518 mEmbellishData
.direction
== NS_STRETCH_DIRECTION_UNSUPPORTED
);
519 nsresult rv
= Place(aRenderingContext
, placeOrigin
, aDesiredSize
);
521 // Place() will call FinishReflowChild() when placeOrigin is true but if
522 // it returns before reaching FinishReflowChild() due to errors we need
523 // to fulfill the reflow protocol by calling DidReflow for the child frames
524 // that still needs it here (or we may crash - bug 366012).
525 // If placeOrigin is false we should reach Place() with aPlaceOrigin == true
526 // through Stretch() eventually.
527 if (NS_MATHML_HAS_ERROR(mPresentationData
.flags
) || NS_FAILED(rv
)) {
528 DidReflowChildren(GetFirstChild(nsnull
));
532 PRBool parentWillFireStretch
= PR_FALSE
;
534 // This means the rect.x and rect.y of our children were not set!!
535 // Don't go without checking to see if our parent will later fire a Stretch() command
536 // targeted at us. The Stretch() will cause the rect.x and rect.y to clear...
537 nsIMathMLFrame
* mathMLFrame
;
538 mParent
->QueryInterface(NS_GET_IID(nsIMathMLFrame
), (void**)&mathMLFrame
);
540 nsEmbellishData embellishData
;
541 nsPresentationData presentationData
;
542 mathMLFrame
->GetEmbellishData(embellishData
);
543 mathMLFrame
->GetPresentationData(presentationData
);
544 if (NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(presentationData
.flags
) ||
545 NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(presentationData
.flags
) ||
546 (NS_MATHML_IS_EMBELLISH_OPERATOR(embellishData
.flags
)
547 && presentationData
.baseFrame
== this))
549 parentWillFireStretch
= PR_TRUE
;
552 if (!parentWillFireStretch
) {
553 // There is nobody who will fire the stretch for us, we do it ourselves!
556 /* NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData.flags) || */
557 NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData
.flags
);
559 nsBoundingMetrics defaultSize
;
560 if (mEmbellishData
.coreFrame
== this /* case of a bare <mo>...</mo> itself */
561 || stretchAll
) { /* or <mover><mo>...</mo>...</mover>, or friends */
562 // use our current size as computed earlier by Place()
563 defaultSize
= aDesiredSize
.mBoundingMetrics
;
565 else { /* case of <msup><mo>...</mo>...</msup> or friends */
566 // compute a size that doesn't include embellishments
567 GetPreferredStretchSize(aRenderingContext
, 0, mEmbellishData
.direction
,
570 Stretch(aRenderingContext
, NS_STRETCH_DIRECTION_DEFAULT
, defaultSize
,
574 // The Place() call above didn't request FinishReflowChild(),
575 // so let's check that we eventually did through Stretch().
576 nsIFrame
* childFrame
= GetFirstChild(nsnull
);
577 for ( ; childFrame
; childFrame
= childFrame
->GetNextSibling()) {
578 NS_ASSERTION(!(childFrame
->GetStateBits() & NS_FRAME_IN_REFLOW
),
579 "DidReflow() was never called");
586 // see if we should fix the spacing
587 FixInterFrameSpacing(aDesiredSize
);
589 // Also return our bounding metrics
590 aDesiredSize
.mBoundingMetrics
= mBoundingMetrics
;
592 if (!parentWillFireStretch
) {
593 // Not expecting a stretch.
594 // Finished with these:
595 ClearSavedChildMetrics();
596 // Set our overflow area.
597 GatherAndStoreOverflow(&aDesiredSize
);
605 * nsIMathMLFrame - support methods for scripting elements (nested frames
606 * within msub, msup, msubsup, munder, mover, munderover, mmultiscripts,
607 * mfrac, mroot, mtable).
608 * =============================================================================
611 // helper to let the update of presentation data pass through
612 // a subtree that may contain non-mathml container frames
614 nsMathMLContainerFrame::PropagatePresentationDataFor(nsIFrame
* aFrame
,
615 PRUint32 aFlagsValues
,
616 PRUint32 aFlagsToUpdate
)
618 if (!aFrame
|| !aFlagsToUpdate
)
620 nsIMathMLFrame
* mathMLFrame
;
621 aFrame
->QueryInterface(NS_GET_IID(nsIMathMLFrame
), (void**)&mathMLFrame
);
624 mathMLFrame
->UpdatePresentationData(aFlagsValues
,
626 // propagate using the base method to make sure that the control
627 // is passed on to MathML frames that may be overloading the method
628 mathMLFrame
->UpdatePresentationDataFromChildAt(0, -1,
629 aFlagsValues
, aFlagsToUpdate
);
632 // propagate down the subtrees
633 nsIFrame
* childFrame
= aFrame
->GetFirstChild(nsnull
);
635 PropagatePresentationDataFor(childFrame
,
636 aFlagsValues
, aFlagsToUpdate
);
637 childFrame
= childFrame
->GetNextSibling();
643 nsMathMLContainerFrame::PropagatePresentationDataFromChildAt(nsIFrame
* aParentFrame
,
644 PRInt32 aFirstChildIndex
,
645 PRInt32 aLastChildIndex
,
646 PRUint32 aFlagsValues
,
647 PRUint32 aFlagsToUpdate
)
649 if (!aParentFrame
|| !aFlagsToUpdate
)
652 nsIFrame
* childFrame
= aParentFrame
->GetFirstChild(nsnull
);
654 if ((index
>= aFirstChildIndex
) &&
655 ((aLastChildIndex
<= 0) || ((aLastChildIndex
> 0) &&
656 (index
<= aLastChildIndex
)))) {
657 PropagatePresentationDataFor(childFrame
,
658 aFlagsValues
, aFlagsToUpdate
);
661 childFrame
= childFrame
->GetNextSibling();
665 /* //////////////////
667 * =============================================================================
672 nsMathMLContainerFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
673 const nsRect
& aDirtyRect
,
674 const nsDisplayListSet
& aLists
)
676 // report an error if something wrong was found in this frame
677 if (NS_MATHML_HAS_ERROR(mPresentationData
.flags
)) {
678 if (!IsVisibleForPainting(aBuilder
))
681 return aLists
.Content()->AppendNewToTop(new (aBuilder
) nsDisplayMathMLError(this));
684 nsresult rv
= DisplayBorderBackgroundOutline(aBuilder
, aLists
);
685 NS_ENSURE_SUCCESS(rv
, rv
);
687 rv
= DisplayTextDecorationsAndChildren(aBuilder
, aDirtyRect
, aLists
);
688 NS_ENSURE_SUCCESS(rv
, rv
);
690 #if defined(NS_DEBUG) && defined(SHOW_BOUNDING_BOX)
693 // if you want to see your bounding box, make sure to properly fill
694 // your mBoundingMetrics and mReference point, and set
695 // mPresentationData.flags |= NS_MATHML_SHOW_BOUNDING_METRICS
696 // in the Init() of your sub-class
697 rv
= DisplayBoundingMetrics(aBuilder
, this, mReference
, mBoundingMetrics
, aLists
);
702 // Note that this method re-builds the automatic data in the children -- not
703 // in aParentFrame itself (except for those particular operations that the
704 // parent frame may do in its TransmitAutomaticData()).
706 nsMathMLContainerFrame::RebuildAutomaticDataForChildren(nsIFrame
* aParentFrame
)
708 // 1. As we descend the tree, make each child frame inherit data from
710 // 2. As we ascend the tree, transmit any specific change that we want
712 nsIFrame
* childFrame
= aParentFrame
->GetFirstChild(nsnull
);
714 nsIMathMLFrame
* childMathMLFrame
;
715 childFrame
->QueryInterface(NS_GET_IID(nsIMathMLFrame
), (void**)&childMathMLFrame
);
716 if (childMathMLFrame
) {
717 childMathMLFrame
->InheritAutomaticData(aParentFrame
);
719 RebuildAutomaticDataForChildren(childFrame
);
720 childFrame
= childFrame
->GetNextSibling();
722 nsIMathMLFrame
* mathMLFrame
;
723 aParentFrame
->QueryInterface(NS_GET_IID(nsIMathMLFrame
), (void**)&mathMLFrame
);
725 mathMLFrame
->TransmitAutomaticData();
729 /* static */ nsresult
730 nsMathMLContainerFrame::ReLayoutChildren(nsIFrame
* aParentFrame
,
736 // walk-up to the first frame that is a MathML frame, stop if we reach <math>
737 nsIFrame
* frame
= aParentFrame
;
739 nsIFrame
* parent
= frame
->GetParent();
740 if (!parent
|| !parent
->GetContent())
743 // stop if it is a MathML frame
744 nsIMathMLFrame
* mathMLFrame
;
745 frame
->QueryInterface(NS_GET_IID(nsIMathMLFrame
), (void**)&mathMLFrame
);
749 // stop if we reach the root <math> tag
750 nsIContent
* content
= frame
->GetContent();
751 NS_ASSERTION(content
, "dangling frame without a content node");
754 // XXXldb This should check namespaces too.
755 if (content
->Tag() == nsGkAtoms::math
)
758 // mark the frame dirty, and continue to climb up. It's important that
759 // we're NOT doing this to the frame we plan to pass to FrameNeedsReflow()
760 frame
->AddStateBits(NS_FRAME_IS_DIRTY
| NS_FRAME_HAS_DIRTY_CHILDREN
);
765 // re-sync the presentation data and embellishment data of our children
766 RebuildAutomaticDataForChildren(frame
);
768 // Ask our parent frame to reflow us
769 nsIFrame
* parent
= frame
->GetParent();
770 NS_ASSERTION(parent
, "No parent to pass the reflow request up to");
774 return frame
->PresContext()->PresShell()->
775 FrameNeedsReflow(frame
, nsIPresShell::eStyleChange
, aBits
);
778 // There are precise rules governing children of a MathML frame,
779 // and properties such as the scriptlevel depends on those rules.
780 // Hence for things to work, callers must use Append/Insert/etc wisely.
783 nsMathMLContainerFrame::ChildListChanged(PRInt32 aModType
)
785 // If this is an embellished frame we need to rebuild the
786 // embellished hierarchy by walking-up to the parent of the
787 // outermost embellished container.
788 nsIFrame
* frame
= this;
789 if (mEmbellishData
.coreFrame
) {
790 nsIFrame
* parent
= mParent
;
791 nsEmbellishData embellishData
;
792 for ( ; parent
; frame
= parent
, parent
= parent
->GetParent()) {
793 GetEmbellishDataFrom(parent
, embellishData
);
794 if (embellishData
.coreFrame
!= mEmbellishData
.coreFrame
)
797 // Important: do not do this to the frame we plan to pass to
799 frame
->AddStateBits(NS_FRAME_IS_DIRTY
| NS_FRAME_HAS_DIRTY_CHILDREN
);
802 return ReLayoutChildren(frame
, NS_FRAME_IS_DIRTY
);
806 nsMathMLContainerFrame::AppendFrames(nsIAtom
* aListName
,
807 nsIFrame
* aFrameList
)
810 return NS_ERROR_INVALID_ARG
;
813 mFrames
.AppendFrames(this, aFrameList
);
814 return ChildListChanged(nsIDOMMutationEvent::ADDITION
);
820 nsMathMLContainerFrame::InsertFrames(nsIAtom
* aListName
,
821 nsIFrame
* aPrevFrame
,
822 nsIFrame
* aFrameList
)
825 return NS_ERROR_INVALID_ARG
;
828 // Insert frames after aPrevFrame
829 mFrames
.InsertFrames(this, aPrevFrame
, aFrameList
);
830 return ChildListChanged(nsIDOMMutationEvent::ADDITION
);
836 nsMathMLContainerFrame::RemoveFrame(nsIAtom
* aListName
,
840 return NS_ERROR_INVALID_ARG
;
842 // remove the child frame
843 mFrames
.DestroyFrame(aOldFrame
);
844 return ChildListChanged(nsIDOMMutationEvent::REMOVAL
);
848 nsMathMLContainerFrame::AttributeChanged(PRInt32 aNameSpaceID
,
852 // XXX Since they are numerous MathML attributes that affect layout, and
853 // we can't check all of them here, play safe by requesting a reflow.
854 // XXXldb This should only do work for attributes that cause changes!
855 return PresContext()->PresShell()->
856 FrameNeedsReflow(this, nsIPresShell::eStyleChange
,
861 nsMathMLContainerFrame::GatherAndStoreOverflow(nsHTMLReflowMetrics
* aMetrics
)
863 // nsIFrame::FinishAndStoreOverflow likes the overflow area to include the
865 nsRect
frameRect(0, 0, aMetrics
->width
, aMetrics
->height
);
867 // Text-shadow overflows.
868 if (PresContext()->CompatibilityMode() != eCompatibility_NavQuirks
) {
869 nsRect shadowRect
= nsLayoutUtils::GetTextShadowRectsUnion(frameRect
, this);
870 frameRect
.UnionRect(frameRect
, shadowRect
);
873 // All non-child-frame content such as nsMathMLChars (and most child-frame
874 // content) is included in mBoundingMetrics.
875 nsRect
boundingBox(mBoundingMetrics
.leftBearing
,
876 aMetrics
->ascent
- mBoundingMetrics
.ascent
,
877 mBoundingMetrics
.rightBearing
- mBoundingMetrics
.leftBearing
,
878 mBoundingMetrics
.ascent
+ mBoundingMetrics
.descent
);
880 aMetrics
->mOverflowArea
.UnionRect(frameRect
, boundingBox
);
882 // mBoundingMetrics does not necessarily include content of <mpadded>
883 // elements whose mBoundingMetrics may not be representative of the true
884 // bounds, and doesn't include the CSS2 outline rectangles of children, so
885 // make such to include child overflow areas.
886 nsIFrame
* childFrame
= mFrames
.FirstChild();
888 ConsiderChildOverflow(aMetrics
->mOverflowArea
, childFrame
);
889 childFrame
= childFrame
->GetNextSibling();
892 FinishAndStoreOverflow(aMetrics
);
896 nsMathMLContainerFrame::ReflowChild(nsIFrame
* aChildFrame
,
897 nsPresContext
* aPresContext
,
898 nsHTMLReflowMetrics
& aDesiredSize
,
899 const nsHTMLReflowState
& aReflowState
,
900 nsReflowStatus
& aStatus
)
902 // Having foreign/hybrid children, e.g., from html markups, is not defined by
903 // the MathML spec. But it can happen in practice, e.g., <html:img> allows us
904 // to do some cool demos... or we may have a child that is an nsInlineFrame
905 // from a generated content such as :before { content: open-quote } or
906 // :after { content: close-quote }. Unfortunately, the other frames out-there
907 // may expect their own invariants that are not met when we mix things.
908 // Hence we do not claim their support, but we will nevertheless attempt to keep
909 // them in the flow, if we can get their desired size. We observed that most
910 // frames may be reflowed generically, but nsInlineFrames need extra care.
913 nsInlineFrame
* inlineFrame
;
914 aChildFrame
->QueryInterface(kInlineFrameCID
, (void**)&inlineFrame
);
915 NS_ASSERTION(!inlineFrame
, "Inline frames should be wrapped in blocks");
918 nsresult rv
= nsHTMLContainerFrame::
919 ReflowChild(aChildFrame
, aPresContext
, aDesiredSize
, aReflowState
,
920 0, 0, NS_FRAME_NO_MOVE_FRAME
, aStatus
);
925 if (aDesiredSize
.ascent
== nsHTMLReflowMetrics::ASK_FOR_BASELINE
) {
926 // This will be suitable for inline frames, which are wrapped in a block.
927 if(!nsLayoutUtils::GetLastLineBaseline(aChildFrame
,
928 &aDesiredSize
.ascent
)) {
929 // We don't expect any other block children so just place the frame on
930 // the baseline instead of going through DidReflow() and
931 // GetBaseline(). This is what nsFrame::GetBaseline() will do anyway.
932 aDesiredSize
.ascent
= aDesiredSize
.height
;
935 if (IsForeignChild(aChildFrame
)) {
936 // use ComputeTightBounds API as aDesiredSize.mBoundingMetrics is not set.
937 nsRect r
= aChildFrame
->ComputeTightBounds(aReflowState
.rendContext
->ThebesContext());
938 aDesiredSize
.mBoundingMetrics
.leftBearing
= r
.x
;
939 aDesiredSize
.mBoundingMetrics
.rightBearing
= r
.XMost();
940 aDesiredSize
.mBoundingMetrics
.ascent
= aDesiredSize
.ascent
- r
.y
;
941 aDesiredSize
.mBoundingMetrics
.descent
= r
.YMost() - aDesiredSize
.ascent
;
942 aDesiredSize
.mBoundingMetrics
.width
= aDesiredSize
.width
;
948 nsMathMLContainerFrame::Reflow(nsPresContext
* aPresContext
,
949 nsHTMLReflowMetrics
& aDesiredSize
,
950 const nsHTMLReflowState
& aReflowState
,
951 nsReflowStatus
& aStatus
)
953 aDesiredSize
.width
= aDesiredSize
.height
= 0;
954 aDesiredSize
.ascent
= 0;
955 aDesiredSize
.mBoundingMetrics
.Clear();
959 // Asking each child to cache its bounding metrics
961 nsReflowStatus childStatus
;
962 nsSize
availSize(aReflowState
.ComputedWidth(), NS_UNCONSTRAINEDSIZE
);
963 nsIFrame
* childFrame
= mFrames
.FirstChild();
965 nsHTMLReflowMetrics
childDesiredSize(aDesiredSize
.mFlags
);
966 nsHTMLReflowState
childReflowState(aPresContext
, aReflowState
,
967 childFrame
, availSize
);
968 nsresult rv
= ReflowChild(childFrame
, aPresContext
, childDesiredSize
,
969 childReflowState
, childStatus
);
970 //NS_ASSERTION(NS_FRAME_IS_COMPLETE(childStatus), "bad status");
972 // Call DidReflow() for the child frames we successfully did reflow.
973 DidReflowChildren(mFrames
.FirstChild(), childFrame
);
977 SaveReflowAndBoundingMetricsFor(childFrame
, childDesiredSize
,
978 childDesiredSize
.mBoundingMetrics
);
979 childFrame
= childFrame
->GetNextSibling();
983 // If we are a container which is entitled to stretch its children, then we
984 // ask our stretchy children to stretch themselves
986 // The stretching of siblings of an embellished child is _deferred_ until
987 // after finishing the stretching of the embellished child - bug 117652
989 if (!NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData
.flags
) &&
990 (NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData
.flags
) ||
991 NS_MATHML_WILL_STRETCH_ALL_CHILDREN_HORIZONTALLY(mPresentationData
.flags
))) {
993 // get the stretchy direction
994 nsStretchDirection stretchDir
=
995 NS_MATHML_WILL_STRETCH_ALL_CHILDREN_VERTICALLY(mPresentationData
.flags
)
996 ? NS_STRETCH_DIRECTION_VERTICAL
997 : NS_STRETCH_DIRECTION_HORIZONTAL
;
999 // what size should we use to stretch our stretchy children
1000 // We don't use STRETCH_CONSIDER_ACTUAL_SIZE -- because our size is not known yet
1001 // We don't use STRETCH_CONSIDER_EMBELLISHMENTS -- because we don't want to
1002 // include them in the caculations of the size of stretchy elements
1003 nsBoundingMetrics containerSize
;
1004 GetPreferredStretchSize(*aReflowState
.rendContext
, 0, stretchDir
,
1007 // fire the stretch on each child
1008 childFrame
= mFrames
.FirstChild();
1009 while (childFrame
) {
1010 nsIMathMLFrame
* mathMLFrame
;
1011 childFrame
->QueryInterface(NS_GET_IID(nsIMathMLFrame
), (void**)&mathMLFrame
);
1013 // retrieve the metrics that was stored at the previous pass
1014 nsHTMLReflowMetrics childDesiredSize
;
1015 GetReflowAndBoundingMetricsFor(childFrame
,
1016 childDesiredSize
, childDesiredSize
.mBoundingMetrics
);
1018 mathMLFrame
->Stretch(*aReflowState
.rendContext
, stretchDir
,
1019 containerSize
, childDesiredSize
);
1020 // store the updated metrics
1021 SaveReflowAndBoundingMetricsFor(childFrame
, childDesiredSize
,
1022 childDesiredSize
.mBoundingMetrics
);
1024 childFrame
= childFrame
->GetNextSibling();
1029 // Place children now by re-adjusting the origins to align the baselines
1030 FinalizeReflow(*aReflowState
.rendContext
, aDesiredSize
);
1032 aStatus
= NS_FRAME_COMPLETE
;
1033 NS_FRAME_SET_TRUNCATION(aStatus
, aReflowState
, aDesiredSize
);
1037 /* virtual */ nscoord
1038 nsMathMLContainerFrame::GetMinWidth(nsIRenderingContext
*aRenderingContext
)
1041 DISPLAY_MIN_WIDTH(this, result
);
1042 result
= GetIntrinsicWidth(aRenderingContext
);
1046 /* virtual */ nscoord
1047 nsMathMLContainerFrame::GetPrefWidth(nsIRenderingContext
*aRenderingContext
)
1050 DISPLAY_MIN_WIDTH(this, result
);
1051 result
= GetIntrinsicWidth(aRenderingContext
);
1055 /* virtual */ nscoord
1056 nsMathMLContainerFrame::GetIntrinsicWidth(nsIRenderingContext
* aRenderingContext
)
1059 nsIFrame
* childFrame
= mFrames
.FirstChild();
1060 while (childFrame
) {
1061 // XXX This includes margin while Reflow currently doesn't consider
1062 // margin, so we may end up with too much space, but, with stretchy
1063 // characters, this is an approximation anyway.
1065 nsLayoutUtils::IntrinsicForContainer(aRenderingContext
, childFrame
,
1066 nsLayoutUtils::PREF_WIDTH
);
1068 nsHTMLReflowMetrics childDesiredSize
;
1069 childDesiredSize
.width
= width
;
1070 childDesiredSize
.mBoundingMetrics
.width
= width
;
1071 // TODO: we need nsIFrame::GetIntrinsicHBounds() for better values here.
1072 childDesiredSize
.mBoundingMetrics
.leftBearing
= 0;
1073 childDesiredSize
.mBoundingMetrics
.rightBearing
= width
;
1075 SaveReflowAndBoundingMetricsFor(childFrame
, childDesiredSize
,
1076 childDesiredSize
.mBoundingMetrics
);
1078 childFrame
= childFrame
->GetNextSibling();
1082 nsHTMLReflowMetrics desiredSize
;
1083 nsresult rv
= MeasureChildFrames(*aRenderingContext
, desiredSize
);
1084 if (NS_FAILED(rv
)) {
1085 ReflowError(*aRenderingContext
, desiredSize
);
1088 ClearSavedChildMetrics();
1090 return desiredSize
.width
;
1093 /* virtual */ nsresult
1094 nsMathMLContainerFrame::MeasureChildFrames(nsIRenderingContext
& aRenderingContext
,
1095 nsHTMLReflowMetrics
& aDesiredSize
)
1097 return Place(aRenderingContext
, PR_FALSE
, aDesiredSize
);
1101 // see spacing table in Chapter 18, TeXBook (p.170)
1102 // Our table isn't quite identical to TeX because operators have
1103 // built-in values for lspace & rspace in the Operator Dictionary.
1104 static PRInt32 kInterFrameSpacingTable
[eMathMLFrameType_COUNT
][eMathMLFrameType_COUNT
] =
1106 // in units of muspace.
1107 // upper half of the byte is set if the
1108 // spacing is not to be used for scriptlevel > 0
1110 /* Ord OpOrd OpInv OpUsr Inner Italic Upright */
1111 /*Ord */ {0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00},
1112 /*OpOrd*/ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
1113 /*OpInv*/ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
1114 /*OpUsr*/ {0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01},
1115 /*Inner*/ {0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01},
1116 /*Italic*/ {0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01},
1117 /*Upright*/ {0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01}
1120 #define GET_INTERSPACE(scriptlevel_, frametype1_, frametype2_, space_) \
1121 /* no space if there is a frame that we know nothing about */ \
1122 if (frametype1_ == eMathMLFrameType_UNKNOWN || \
1123 frametype2_ == eMathMLFrameType_UNKNOWN) \
1126 space_ = kInterFrameSpacingTable[frametype1_][frametype2_]; \
1127 space_ = (scriptlevel_ > 0 && (space_ & 0xF0)) \
1128 ? 0 /* spacing is disabled */ \
1132 // This function computes the inter-space between two frames. However,
1133 // since invisible operators need special treatment, the inter-space may
1134 // be delayed when an invisible operator is encountered. In this case,
1135 // the function will carry the inter-space forward until it is determined
1136 // that it can be applied properly (i.e., until we encounter a visible
1137 // frame where to decide whether to accept or reject the inter-space).
1138 // aFromFrameType: remembers the frame when the carry-forward initiated.
1139 // aCarrySpace: keeps track of the inter-space that is delayed.
1140 // @returns: current inter-space (which is 0 when the true inter-space is
1141 // delayed -- and thus has no effect since the frame is invisible anyway).
1143 GetInterFrameSpacing(PRInt32 aScriptLevel
,
1144 eMathMLFrameType aFirstFrameType
,
1145 eMathMLFrameType aSecondFrameType
,
1146 eMathMLFrameType
* aFromFrameType
, // IN/OUT
1147 PRInt32
* aCarrySpace
) // IN/OUT
1149 eMathMLFrameType firstType
= aFirstFrameType
;
1150 eMathMLFrameType secondType
= aSecondFrameType
;
1153 GET_INTERSPACE(aScriptLevel
, firstType
, secondType
, space
);
1155 // feedback control to avoid the inter-space to be added when not necessary
1156 if (secondType
== eMathMLFrameType_OperatorInvisible
) {
1157 // see if we should start to carry the space forward until we
1158 // encounter a visible frame
1159 if (*aFromFrameType
== eMathMLFrameType_UNKNOWN
) {
1160 *aFromFrameType
= firstType
;
1161 *aCarrySpace
= space
;
1163 // keep carrying *aCarrySpace forward, while returning 0 for this stage
1166 else if (*aFromFrameType
!= eMathMLFrameType_UNKNOWN
) {
1167 // no carry-forward anymore, get the real inter-space between
1168 // the two frames of interest
1170 firstType
= *aFromFrameType
;
1172 // But... the invisible operator that we encountered earlier could
1173 // be sitting between italic and upright identifiers, e.g.,
1175 // 1. <mi>sin</mi> <mo>⁡</mo> <mi>x</mi>
1176 // 2. <mi>x</mi> <mo>&InvisibileTime;</mo> <mi>sin</mi>
1178 // the trick to get the inter-space in either situation
1179 // is to promote "<mi>sin</mi><mo>⁡</mo>" and
1180 // "<mo>&InvisibileTime;</mo><mi>sin</mi>" to user-defined operators...
1181 if (firstType
== eMathMLFrameType_UprightIdentifier
) {
1182 firstType
= eMathMLFrameType_OperatorUserDefined
;
1184 else if (secondType
== eMathMLFrameType_UprightIdentifier
) {
1185 secondType
= eMathMLFrameType_OperatorUserDefined
;
1188 GET_INTERSPACE(aScriptLevel
, firstType
, secondType
, space
);
1190 // Now, we have two values: the computed space and the space that
1191 // has been carried forward until now. Which value do we pick?
1192 // If the second type is an operator (e.g., fence), it already has
1193 // built-in lspace & rspace, so we let them win. Otherwise we pick
1194 // the max between the two values that we have.
1195 if (secondType
!= eMathMLFrameType_OperatorOrdinary
&&
1196 space
< *aCarrySpace
)
1197 space
= *aCarrySpace
;
1199 // reset everything now that the carry-forward is done
1200 *aFromFrameType
= eMathMLFrameType_UNKNOWN
;
1207 static nscoord
GetThinSpace(const nsStyleFont
* aStyleFont
)
1209 return NSToCoordRound(float(aStyleFont
->mFont
.size
)*float(3) / float(18));
1212 class nsMathMLContainerFrame::RowChildFrameIterator
{
1214 explicit RowChildFrameIterator(nsMathMLContainerFrame
* aParentFrame
) :
1215 mParentFrame(aParentFrame
),
1216 mChildFrame(aParentFrame
->mFrames
.FirstChild()),
1219 mFromFrameType(eMathMLFrameType_UNKNOWN
)
1224 InitMetricsForChild();
1225 // Remove left correction in <msqrt> because the sqrt glyph itself is
1227 if (mParentFrame
->GetContent()->Tag() == nsGkAtoms::msqrt_
) {
1232 RowChildFrameIterator
& operator++()
1234 // add child size + italic correction
1235 mX
+= mSize
.mBoundingMetrics
.width
+ mItalicCorrection
;
1237 mChildFrame
= mChildFrame
->GetNextSibling();
1241 eMathMLFrameType prevFrameType
= mChildFrameType
;
1242 InitMetricsForChild();
1244 // add inter frame spacing
1245 const nsStyleFont
* font
= mParentFrame
->GetStyleFont();
1247 GetInterFrameSpacing(font
->mScriptLevel
,
1248 prevFrameType
, mChildFrameType
,
1249 &mFromFrameType
, &mCarrySpace
);
1250 mX
+= space
* GetThinSpace(font
);
1254 nsIFrame
* Frame() const { return mChildFrame
; }
1255 nscoord
X() const { return mX
; }
1256 const nsHTMLReflowMetrics
& ReflowMetrics() const { return mSize
; }
1257 nscoord
Ascent() const { return mSize
.ascent
; }
1258 nscoord
Descent() const { return mSize
.height
- mSize
.ascent
; }
1259 const nsBoundingMetrics
& BoundingMetrics() const {
1260 return mSize
.mBoundingMetrics
;
1264 const nsMathMLContainerFrame
* mParentFrame
;
1265 nsIFrame
* mChildFrame
;
1266 nsHTMLReflowMetrics mSize
;
1269 nscoord mItalicCorrection
;
1270 eMathMLFrameType mChildFrameType
;
1271 PRInt32 mCarrySpace
;
1272 eMathMLFrameType mFromFrameType
;
1274 void InitMetricsForChild()
1276 GetReflowAndBoundingMetricsFor(mChildFrame
, mSize
, mSize
.mBoundingMetrics
,
1278 nscoord leftCorrection
;
1279 GetItalicCorrection(mSize
.mBoundingMetrics
, leftCorrection
,
1281 // add left correction -- this fixes the problem of the italic 'f'
1282 // e.g., <mo>q</mo> <mi>f</mi> <mo>I</mo>
1283 mX
+= leftCorrection
;
1287 /* virtual */ nsresult
1288 nsMathMLContainerFrame::Place(nsIRenderingContext
& aRenderingContext
,
1289 PRBool aPlaceOrigin
,
1290 nsHTMLReflowMetrics
& aDesiredSize
)
1292 // This is needed in case this frame is empty (i.e., no child frames)
1293 mBoundingMetrics
.Clear();
1295 RowChildFrameIterator
child(this);
1296 nscoord ascent
= 0, descent
= 0;
1297 while (child
.Frame()) {
1298 if (descent
< child
.Descent())
1299 descent
= child
.Descent();
1300 if (ascent
< child
.Ascent())
1301 ascent
= child
.Ascent();
1302 // add the child size
1303 mBoundingMetrics
.width
= child
.X();
1304 mBoundingMetrics
+= child
.BoundingMetrics();
1307 // Add the italic correction at the end (including the last child).
1308 // This gives a nice gap between math and non-math frames, and still
1309 // gives the same math inter-spacing in case this frame connects to
1310 // another math frame
1311 mBoundingMetrics
.width
= child
.X();
1313 aDesiredSize
.width
= mBoundingMetrics
.width
;
1314 aDesiredSize
.height
= ascent
+ descent
;
1315 aDesiredSize
.ascent
= ascent
;
1316 aDesiredSize
.mBoundingMetrics
= mBoundingMetrics
;
1319 mReference
.y
= aDesiredSize
.ascent
;
1325 PositionRowChildFrames(0, aDesiredSize
.ascent
);
1332 nsMathMLContainerFrame::PositionRowChildFrames(nscoord aOffsetX
,
1335 RowChildFrameIterator
child(this);
1336 while (child
.Frame()) {
1337 nscoord dx
= aOffsetX
+ child
.X();
1338 nscoord dy
= aBaseline
- child
.Ascent();
1339 FinishReflowChild(child
.Frame(), PresContext(), nsnull
,
1340 child
.ReflowMetrics(), dx
, dy
, 0);
1345 class ForceReflow
: public nsIReflowCallback
{
1347 virtual PRBool
ReflowFinished() {
1350 virtual void ReflowCallbackCanceled() {}
1353 // We only need one of these so we just make it a static global, no need
1354 // to dynamically allocate/destroy it.
1355 static ForceReflow gForceReflow
;
1358 nsMathMLContainerFrame::SetIncrementScriptLevel(PRInt32 aChildIndex
, PRBool aIncrement
)
1360 nsIFrame
* child
= nsFrameList(GetFirstChild(nsnull
)).FrameAt(aChildIndex
);
1363 nsIContent
* content
= child
->GetContent();
1364 if (!content
->IsNodeOfType(nsINode::eMATHML
))
1366 nsMathMLElement
* element
= static_cast<nsMathMLElement
*>(content
);
1368 if (element
->GetIncrementScriptLevel() == aIncrement
)
1371 // XXXroc this does a ContentStatesChanged, is it safe to call here? If
1372 // not we should do it in a post-reflow callback.
1373 element
->SetIncrementScriptLevel(aIncrement
, PR_TRUE
);
1374 PresContext()->PresShell()->PostReflowCallback(&gForceReflow
);
1377 // helpers to fix the inter-spacing when <math> is the only parent
1378 // e.g., it fixes <math> <mi>f</mi> <mo>q</mo> <mi>f</mi> <mo>I</mo> </math>
1381 GetInterFrameSpacingFor(PRInt32 aScriptLevel
,
1382 nsIFrame
* aParentFrame
,
1383 nsIFrame
* aChildFrame
)
1385 nsIFrame
* childFrame
= aParentFrame
->GetFirstChild(nsnull
);
1386 if (!childFrame
|| aChildFrame
== childFrame
)
1389 PRInt32 carrySpace
= 0;
1390 eMathMLFrameType fromFrameType
= eMathMLFrameType_UNKNOWN
;
1391 eMathMLFrameType prevFrameType
= eMathMLFrameType_UNKNOWN
;
1392 eMathMLFrameType childFrameType
= nsMathMLFrame::GetMathMLFrameTypeFor(childFrame
);
1393 childFrame
= childFrame
->GetNextSibling();
1394 while (childFrame
) {
1395 prevFrameType
= childFrameType
;
1396 childFrameType
= nsMathMLFrame::GetMathMLFrameTypeFor(childFrame
);
1397 nscoord space
= GetInterFrameSpacing(aScriptLevel
,
1398 prevFrameType
, childFrameType
, &fromFrameType
, &carrySpace
);
1399 if (aChildFrame
== childFrame
) {
1401 nsStyleContext
* parentContext
= aParentFrame
->GetStyleContext();
1402 nscoord thinSpace
= GetThinSpace(parentContext
->GetStyleFont());
1404 return space
* thinSpace
;
1406 childFrame
= childFrame
->GetNextSibling();
1409 NS_NOTREACHED("child not in the childlist of its parent");
1414 nsMathMLContainerFrame::FixInterFrameSpacing(nsHTMLReflowMetrics
& aDesiredSize
)
1417 nsIContent
* parentContent
= mParent
->GetContent();
1418 if (NS_UNLIKELY(!parentContent
)) {
1421 // XXXldb This should check namespaces too.
1422 nsIAtom
*parentTag
= parentContent
->Tag();
1423 if (parentTag
== nsGkAtoms::math
||
1424 parentTag
== nsGkAtoms::mtd_
) {
1425 gap
= GetInterFrameSpacingFor(GetStyleFont()->mScriptLevel
, mParent
, this);
1426 // add our own italic correction
1427 nscoord leftCorrection
= 0, italicCorrection
= 0;
1428 GetItalicCorrection(mBoundingMetrics
, leftCorrection
, italicCorrection
);
1429 gap
+= leftCorrection
;
1430 // see if we should shift our children to account for the correction
1432 nsIFrame
* childFrame
= mFrames
.FirstChild();
1433 while (childFrame
) {
1434 childFrame
->SetPosition(childFrame
->GetPosition() + nsPoint(gap
, 0));
1435 childFrame
= childFrame
->GetNextSibling();
1437 mBoundingMetrics
.leftBearing
+= gap
;
1438 mBoundingMetrics
.rightBearing
+= gap
;
1439 mBoundingMetrics
.width
+= gap
;
1440 aDesiredSize
.width
+= gap
;
1442 mBoundingMetrics
.width
+= italicCorrection
;
1443 aDesiredSize
.width
+= italicCorrection
;
1449 nsMathMLContainerFrame::DidReflowChildren(nsIFrame
* aFirst
, nsIFrame
* aStop
)
1452 if (NS_UNLIKELY(!aFirst
))
1455 for (nsIFrame
* frame
= aFirst
;
1457 frame
= frame
->GetNextSibling()) {
1458 NS_ASSERTION(frame
, "aStop isn't a sibling");
1459 if (frame
->GetStateBits() & NS_FRAME_IN_REFLOW
) {
1460 // finish off principal descendants, too
1461 nsIFrame
* grandchild
= frame
->GetFirstChild(nsnull
);
1463 DidReflowChildren(grandchild
, nsnull
);
1465 frame
->DidReflow(frame
->PresContext(), nsnull
,
1466 NS_FRAME_REFLOW_FINISHED
);
1471 //==========================
1474 NS_NewMathMLmathBlockFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
,
1477 nsMathMLmathBlockFrame
* it
= new (aPresShell
) nsMathMLmathBlockFrame(aContext
);
1479 it
->SetFlags(aFlags
);
1485 NS_NewMathMLmathInlineFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
)
1487 return new (aPresShell
) nsMathMLmathInlineFrame(aContext
);