1356 lines
52 KiB
C++
1356 lines
52 KiB
C++
/*
|
|
* The contents of this file are subject to the Mozilla Public
|
|
* License Version 1.1 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS
|
|
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
|
* implied. See the License for the specific language governing
|
|
* rights and limitations under the License.
|
|
*
|
|
* The Original Code is Mozilla MathML Project.
|
|
*
|
|
* The Initial Developer of the Original Code is The University Of
|
|
* Queensland. Portions created by The University Of Queensland are
|
|
* Copyright (C) 1999 The University Of Queensland. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* Roger B. Sidje <rbs@maths.uq.edu.au>
|
|
* David J. Fiddes <D.J.Fiddes@hw.ac.uk>
|
|
*/
|
|
|
|
#include "nsCOMPtr.h"
|
|
#include "nsHTMLParts.h"
|
|
#include "nsIHTMLContent.h"
|
|
#include "nsFrame.h"
|
|
#include "nsLineLayout.h"
|
|
#include "nsHTMLIIDs.h"
|
|
#include "nsIPresContext.h"
|
|
#include "nsIPresShell.h"
|
|
#include "nsHTMLAtoms.h"
|
|
#include "nsUnitConversion.h"
|
|
#include "nsIStyleContext.h"
|
|
#include "nsStyleConsts.h"
|
|
#include "nsINameSpaceManager.h"
|
|
#include "nsIRenderingContext.h"
|
|
#include "nsIFontMetrics.h"
|
|
#include "nsStyleUtil.h"
|
|
|
|
#include "nsITextContent.h"
|
|
|
|
#include "nsMathMLAtoms.h"
|
|
#include "nsMathMLParts.h"
|
|
#include "nsMathMLChar.h"
|
|
#include "nsMathMLContainerFrame.h"
|
|
|
|
//
|
|
// nsMathMLContainerFrame implementation
|
|
//
|
|
|
|
// TODO: Proper management of ignorable whitespace
|
|
// and handling of non-math related frames.
|
|
// * Should math markups only enclose other math markups?
|
|
// Why bother... we can leave them in, as hinted in the newsgroup.
|
|
//
|
|
// * Space doesn't count. Handling space is more complicated than it seems.
|
|
// We have to clean inside and outside:
|
|
// <mi> a </mi> <mo> + </mo> <mi> b </mi>
|
|
// ^ ^ ^ ^ ^ ^ ^ ^
|
|
// *Outside* currently handled using IsOnlyWhitespace().
|
|
// For whitespace only frames, their rect is set zero.
|
|
//
|
|
// *Inside* not currently handled... so the user must do <mi>a</mi>
|
|
// What to do?!
|
|
// - Add a nsTextFrame::CompressWhitespace() *if* it is MathML content?!
|
|
// - via CSS property? Support for "none/compress"? (white-space: normal | pre | nowrap)
|
|
|
|
|
|
// nsISupports
|
|
// =============================================================================
|
|
|
|
NS_IMETHODIMP
|
|
nsMathMLContainerFrame::QueryInterface(REFNSIID aIID, void** aInstancePtr)
|
|
{
|
|
if (NULL == aInstancePtr) {
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
*aInstancePtr = nsnull;
|
|
if (aIID.Equals(kIMathMLFrameIID)) {
|
|
*aInstancePtr = (void*)(nsIMathMLFrame*)this;
|
|
NS_ADDREF_THIS(); // not effectual, frames are not Refcounted
|
|
return NS_OK;
|
|
}
|
|
return nsHTMLContainerFrame::QueryInterface(aIID, aInstancePtr);
|
|
}
|
|
|
|
NS_IMETHODIMP_(nsrefcnt)
|
|
nsMathMLContainerFrame::AddRef(void)
|
|
{
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP_(nsrefcnt)
|
|
nsMathMLContainerFrame::Release(void)
|
|
{
|
|
return NS_OK;
|
|
}
|
|
|
|
/* ///////////////
|
|
* MathML specific - Whitespace management ...
|
|
* WHITESPACE: don't forget that whitespace doesn't count in MathML!
|
|
* empty frames shouldn't be created in MathML, oh well that's another story.
|
|
* =============================================================================
|
|
*/
|
|
|
|
PRBool
|
|
nsMathMLContainerFrame::IsOnlyWhitespace(nsIFrame* aFrame)
|
|
{
|
|
NS_PRECONDITION(aFrame, "null arg");
|
|
PRBool rv = PR_FALSE;
|
|
|
|
// by empty frame we mean a leaf frame whose text content is empty...
|
|
nsCOMPtr<nsIContent> aContent;
|
|
aFrame->GetContent(getter_AddRefs(aContent));
|
|
if (!aContent) return PR_TRUE;
|
|
PRInt32 numKids;
|
|
aContent->ChildCount(numKids);
|
|
if (0 == numKids) {
|
|
nsCOMPtr<nsITextContent> tc(do_QueryInterface(aContent));
|
|
if (tc.get()) tc->IsOnlyWhitespace(&rv);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
void
|
|
nsMathMLContainerFrame::ReflowEmptyChild(nsIPresContext* aPresContext,
|
|
nsIFrame* aFrame)
|
|
{
|
|
// nsHTMLReflowMetrics emptySize(nsnull);
|
|
// nsHTMLReflowState emptyReflowState(aPresContext, aReflowState, aFrame, nsSize(0,0));
|
|
// nsresult rv = ReflowChild(aFrame, aPresContext, emptySize, emptyReflowState, aStatus);
|
|
|
|
// 0-size the frame
|
|
aFrame->SetRect(aPresContext, nsRect(0,0,0,0));
|
|
|
|
// 0-size the view, if any
|
|
nsIView* view = nsnull;
|
|
aFrame->GetView(aPresContext, &view);
|
|
if (view) {
|
|
nsCOMPtr<nsIViewManager> vm;
|
|
view->GetViewManager(*getter_AddRefs(vm));
|
|
vm->ResizeView(view, 0,0);
|
|
}
|
|
}
|
|
|
|
/* /////////////
|
|
* nsIMathMLFrame - support methods for precise positioning
|
|
* =============================================================================
|
|
*/
|
|
|
|
NS_IMETHODIMP
|
|
nsMathMLContainerFrame::GetBoundingMetrics(nsBoundingMetrics& aBoundingMetrics)
|
|
{
|
|
aBoundingMetrics = mBoundingMetrics;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMathMLContainerFrame::SetBoundingMetrics(const nsBoundingMetrics& aBoundingMetrics)
|
|
{
|
|
mBoundingMetrics = aBoundingMetrics;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMathMLContainerFrame::GetReference(nsPoint& aReference)
|
|
{
|
|
aReference = mReference;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMathMLContainerFrame::SetReference(const nsPoint& aReference)
|
|
{
|
|
mReference = aReference;
|
|
return NS_OK;
|
|
}
|
|
|
|
// helper method to facilitate getting the reflow and bounding metrics
|
|
void
|
|
nsMathMLContainerFrame::GetReflowAndBoundingMetricsFor(nsIFrame* aFrame,
|
|
nsHTMLReflowMetrics& aReflowMetrics,
|
|
nsBoundingMetrics& aBoundingMetrics)
|
|
{
|
|
NS_PRECONDITION(aFrame, "null arg");
|
|
|
|
// IMPORTANT: This function is only meant to be called in Place() methods
|
|
// where it is assumed that the frame's rect is still acting as place holder
|
|
// for the frame's ascent and descent information
|
|
|
|
nsRect aRect;
|
|
aFrame->GetRect(aRect);
|
|
aReflowMetrics.descent = aRect.x;
|
|
aReflowMetrics.ascent = aRect.y;
|
|
aReflowMetrics.width = aRect.width;
|
|
aReflowMetrics.height = aRect.height;
|
|
|
|
aBoundingMetrics.Clear();
|
|
nsIMathMLFrame* aMathMLFrame = nsnull;
|
|
nsresult rv = aFrame->QueryInterface(nsIMathMLFrame::GetIID(), (void**)&aMathMLFrame);
|
|
if (NS_SUCCEEDED(rv) && aMathMLFrame) {
|
|
aMathMLFrame->GetBoundingMetrics(aBoundingMetrics);
|
|
#if 0
|
|
nsFrame::ListTag(stdout, aFrame);
|
|
printf(" subItalicCorrection:%d supItalicCorrection:%d\n",
|
|
aBoundingMetrics.subItalicCorrection, aBoundingMetrics.supItalicCorrection);
|
|
#endif
|
|
}
|
|
else { // aFrame is not a MathML frame, just return the reflow metrics
|
|
aBoundingMetrics.descent = aReflowMetrics.descent;
|
|
aBoundingMetrics.ascent = aReflowMetrics.ascent;
|
|
aBoundingMetrics.width = aReflowMetrics.width;
|
|
#if 0
|
|
printf("GetBoundingMetrics() failed for: "); /* getchar(); */
|
|
nsFrame::ListTag(stdout, aFrame);
|
|
printf("\n");
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/* /////////////
|
|
* nsIMathMLFrame - support methods for stretchy elements
|
|
* =============================================================================
|
|
*/
|
|
|
|
NS_IMETHODIMP
|
|
nsMathMLContainerFrame::Stretch(nsIPresContext* aPresContext,
|
|
nsIRenderingContext& aRenderingContext,
|
|
nsStretchDirection aStretchDirection,
|
|
nsStretchMetrics& aContainerSize,
|
|
nsStretchMetrics& aDesiredStretchSize)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
if (NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData.flags)) {
|
|
|
|
if (NS_MATHML_STRETCH_WAS_DONE(mEmbellishData.flags)) {
|
|
printf("WARNING *** it is wrong to fire stretch more than once on a frame...\n");
|
|
// NS_ASSERTION(PR_FALSE,"Stretch() was fired more than once on a frame!");
|
|
return NS_OK;
|
|
}
|
|
mEmbellishData.flags |= NS_MATHML_STRETCH_DONE;
|
|
|
|
|
|
// Pass the stretch to the first non-empty child ...
|
|
|
|
nsIFrame* childFrame = mEmbellishData.firstChild;
|
|
NS_ASSERTION(childFrame, "Something is wrong somewhere");
|
|
|
|
if (childFrame) {
|
|
nsIMathMLFrame* aMathMLFrame = nsnull;
|
|
rv = childFrame->QueryInterface(nsIMathMLFrame::GetIID(), (void**)&aMathMLFrame);
|
|
NS_ASSERTION(NS_SUCCEEDED(rv) && aMathMLFrame, "Something is wrong somewhere");
|
|
if (NS_SUCCEEDED(rv) && aMathMLFrame) {
|
|
nsHTMLReflowMetrics aReflowMetrics(nsnull);
|
|
|
|
nsRect rect;
|
|
childFrame->GetRect(rect);
|
|
// And the trick is that rect.x is still holding the descent, and rect.y
|
|
// is still holding the ascent ...
|
|
nsStretchMetrics childSize(rect.x, rect.y, rect.width, rect.height);
|
|
nsStretchMetrics container(aContainerSize);
|
|
|
|
if (aStretchDirection != NS_STRETCH_DIRECTION_DEFAULT && aStretchDirection != mEmbellishData.direction) {
|
|
// change the direction and confine the stretch to us
|
|
#if 0
|
|
GetDesiredStretchSize(aPresContext, aRenderingContext, container);
|
|
#endif
|
|
GetRect(rect);
|
|
container = nsStretchMetrics(rect.x, rect.y, rect.width, rect.height);
|
|
}
|
|
|
|
aMathMLFrame->Stretch(aPresContext, aRenderingContext,
|
|
mEmbellishData.direction, container, childSize);
|
|
childFrame->SetRect(aPresContext,
|
|
nsRect(childSize.descent, childSize.ascent,
|
|
childSize.width, childSize.height));
|
|
|
|
// We now have one child that may have changed, re-position all our children
|
|
Place(aPresContext, aRenderingContext, PR_TRUE, aReflowMetrics);
|
|
|
|
// Prepare the metrics to be returned
|
|
aDesiredStretchSize = nsStretchMetrics(aReflowMetrics);
|
|
aDesiredStretchSize.leftSpace = childSize.leftSpace;
|
|
aDesiredStretchSize.rightSpace = childSize.rightSpace;
|
|
|
|
// If our parent is not embellished, it means we are the outermost embellished
|
|
// container and so we put the spacing, otherwise we don't include the spacing,
|
|
// the outermost embellished container will take care of it.
|
|
|
|
if (!IsEmbellishOperator(mParent)) {
|
|
|
|
nsStyleFont font;
|
|
mStyleContext->GetStyle(eStyleStruct_Font, font);
|
|
nscoord em = NSToCoordRound(float(font.mFont.size));
|
|
|
|
// cache these values
|
|
mEmbellishData.leftSpace = nscoord( em * aDesiredStretchSize.leftSpace );
|
|
mEmbellishData.rightSpace = nscoord( em * aDesiredStretchSize.rightSpace );
|
|
|
|
aDesiredStretchSize.width += nscoord( (aDesiredStretchSize.leftSpace + aDesiredStretchSize.rightSpace) * em );
|
|
nscoord dx = nscoord( aDesiredStretchSize.leftSpace * em );
|
|
if (0 == dx) return NS_OK;
|
|
|
|
nsPoint origin;
|
|
childFrame = mFrames.FirstChild();
|
|
while (childFrame) {
|
|
childFrame->GetOrigin(origin);
|
|
childFrame->MoveTo(aPresContext, origin.x + dx, origin.y);
|
|
childFrame->GetNextSibling(&childFrame);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMathMLContainerFrame::FinalizeReflow(PRInt32 aDirection,
|
|
nsIPresContext* aPresContext,
|
|
nsIRenderingContext& aRenderingContext,
|
|
nsHTMLReflowMetrics& aDesiredSize)
|
|
{
|
|
// During reflow, we use rect.x and rect.y as placeholders for the child's ascent
|
|
// and descent in expectation of a stretch command. Hence we need to ensure that
|
|
// a stretch command will actually be fired later on, after exiting from our
|
|
// reflow. If the stretch is not fired, the rect.x, and rect.y will remain
|
|
// with inappropriate data causing children to be improperly positioned.
|
|
// This helper method checks to see if our parent will fire a stretch command
|
|
// targeted at us. If not, we go ahead and fire an involutive stretch on
|
|
// ourselves. This will clear all the rect.x and rect.y, and return our
|
|
// desired size.
|
|
|
|
|
|
// First, complete the post-reflow hook.
|
|
// We use the information in our children rectangles to position them.
|
|
// If placeOrigin==false, then Place() will not touch rect.x, and rect.y.
|
|
// They will still be holding the ascent and descent for each child.
|
|
PRBool placeOrigin = !NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData.flags);
|
|
Place(aPresContext, aRenderingContext, placeOrigin, aDesiredSize);
|
|
|
|
if (!placeOrigin) {
|
|
// This means the rect.x and rect.y of our children were not set!!
|
|
// Don't go without checking to see if our parent will later fire a Stretch() command
|
|
// targeted at us. The Stretch() will cause the rect.x and rect.y to clear...
|
|
PRBool parentWillFireStretch = PR_FALSE;
|
|
nsEmbellishData parentData;
|
|
nsIMathMLFrame* aMathMLFrame = nsnull;
|
|
nsresult rv = mParent->QueryInterface(nsIMathMLFrame::GetIID(), (void**)&aMathMLFrame);
|
|
if (NS_SUCCEEDED(rv) && aMathMLFrame) {
|
|
aMathMLFrame->GetEmbellishData(parentData);
|
|
if (NS_MATHML_WILL_STRETCH_ALL_CHILDREN(parentData.flags) ||
|
|
(NS_MATHML_WILL_STRETCH_FIRST_CHILD(parentData.flags) &&
|
|
parentData.firstChild == this)) {
|
|
parentWillFireStretch = PR_TRUE;
|
|
}
|
|
}
|
|
if (!parentWillFireStretch) {
|
|
// There is nobody who will fire the stretch for us, we do it ourselves!
|
|
|
|
// BEGIN of GETTING THE STRETCH SIZE
|
|
// What is the size that we should use to stretch our stretchy children ????
|
|
|
|
// 1) With this code, vertical stretching works. But horizontal stretching
|
|
// does not work when the firstChild happens to be the core embellished mo...
|
|
// nsRect rect;
|
|
// nsIFrame* childFrame = mEmbellishData.firstChild;
|
|
// NS_ASSERTION(childFrame, "Something is wrong somewhere");
|
|
// childFrame->GetRect(rect);
|
|
// nsStretchMetrics curSize(rect.x, rect.y, rect.width, rect.height);
|
|
|
|
|
|
// 2) With this code, horizontal stretching works. But vertical stretching
|
|
// is done in some cases where frames could have simply been kept as is.
|
|
nsStretchMetrics curSize(aDesiredSize);
|
|
|
|
|
|
// 3) With this code, we should get appropriate size when it is done !!
|
|
// GetDesiredSize(aDirection, aPresContext, aRenderingContext, curSize);
|
|
|
|
// XXX It is not clear if a direction should be imposed.
|
|
// With the default direction, the MathMLChar will attempt to stretch
|
|
// in its preferred direction.
|
|
|
|
nsStretchMetrics newSize(curSize);
|
|
Stretch(aPresContext, aRenderingContext, NS_STRETCH_DIRECTION_DEFAULT,
|
|
curSize, newSize);
|
|
|
|
aDesiredSize.width = newSize.width;
|
|
aDesiredSize.height = newSize.height;
|
|
aDesiredSize.ascent = newSize.ascent;
|
|
aDesiredSize.descent = newSize.descent;
|
|
}
|
|
}
|
|
if (nsnull != aDesiredSize.maxElementSize) {
|
|
aDesiredSize.maxElementSize->width = aDesiredSize.width;
|
|
aDesiredSize.maxElementSize->height = aDesiredSize.height;
|
|
}
|
|
// Also return our bounding metrics
|
|
aDesiredSize.mBoundingMetrics = mBoundingMetrics;
|
|
return NS_OK;
|
|
}
|
|
|
|
// This is the method used to set the frame as an embellished container.
|
|
// It checks if the first (non-empty) child is embellished. Hence, calls
|
|
// must be bottom-up. The method must only be called from within frames who are
|
|
// entitled to be potential embellished operators as per the MathML REC.
|
|
NS_IMETHODIMP
|
|
nsMathMLContainerFrame::EmbellishOperator()
|
|
{
|
|
// Get the first non-empty child
|
|
nsIFrame* firstChild = mFrames.FirstChild();
|
|
while (firstChild) {
|
|
if (!IsOnlyWhitespace(firstChild)) break;
|
|
firstChild->GetNextSibling(&firstChild);
|
|
}
|
|
if (firstChild && IsEmbellishOperator(firstChild)) {
|
|
// Cache the first child
|
|
mEmbellishData.flags |= NS_MATHML_EMBELLISH_OPERATOR;
|
|
mEmbellishData.firstChild = firstChild;
|
|
// Cache also the inner-most embellished frame at the core of the hierarchy
|
|
nsIMathMLFrame* aMathMLFrame = nsnull;
|
|
nsresult rv = firstChild->QueryInterface(nsIMathMLFrame::GetIID(), (void**)&aMathMLFrame);
|
|
NS_ASSERTION(NS_SUCCEEDED(rv) && aMathMLFrame, "Mystery!");
|
|
nsEmbellishData embellishData;
|
|
aMathMLFrame->GetEmbellishData(embellishData);
|
|
mEmbellishData.core = embellishData.core;
|
|
mEmbellishData.direction = embellishData.direction;
|
|
}
|
|
else {
|
|
mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_OPERATOR;
|
|
mEmbellishData.firstChild = nsnull;
|
|
mEmbellishData.core = nsnull;
|
|
mEmbellishData.direction = NS_STRETCH_DIRECTION_UNSUPPORTED;
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMathMLContainerFrame::GetEmbellishData(nsEmbellishData& aEmbellishData)
|
|
{
|
|
aEmbellishData = mEmbellishData;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMathMLContainerFrame::SetEmbellishData(const nsEmbellishData& aEmbellishData)
|
|
{
|
|
mEmbellishData = aEmbellishData;
|
|
return NS_OK;
|
|
}
|
|
|
|
PRBool
|
|
nsMathMLContainerFrame::IsEmbellishOperator(nsIFrame* aFrame)
|
|
{
|
|
NS_PRECONDITION(aFrame, "null arg");
|
|
if (!aFrame) return PR_FALSE;
|
|
nsIMathMLFrame* aMathMLFrame = nsnull;
|
|
nsresult rv = aFrame->QueryInterface(nsIMathMLFrame::GetIID(), (void**)&aMathMLFrame);
|
|
if (NS_FAILED(rv) || !aMathMLFrame) return PR_FALSE;
|
|
nsEmbellishData aEmbellishData;
|
|
aMathMLFrame->GetEmbellishData(aEmbellishData);
|
|
return NS_MATHML_IS_EMBELLISH_OPERATOR(aEmbellishData.flags);
|
|
}
|
|
|
|
/* /////////////
|
|
* nsIMathMLFrame - support methods for scripting elements (nested frames
|
|
* within msub, msup, msubsup, munder, mover, munderover, mmultiscripts,
|
|
* mfrac, mroot, mtable).
|
|
* =============================================================================
|
|
*/
|
|
|
|
NS_IMETHODIMP
|
|
nsMathMLContainerFrame::GetPresentationData(nsPresentationData& aPresentationData)
|
|
{
|
|
aPresentationData = mPresentationData;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMathMLContainerFrame::SetPresentationData(const nsPresentationData& aPresentationData)
|
|
{
|
|
mPresentationData = aPresentationData;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMathMLContainerFrame::UpdatePresentationData(PRInt32 aScriptLevelIncrement,
|
|
PRBool aDisplayStyle)
|
|
{
|
|
mPresentationData.scriptLevel += aScriptLevelIncrement;
|
|
if (aDisplayStyle)
|
|
mPresentationData.flags |= NS_MATHML_DISPLAYSTYLE;
|
|
else
|
|
mPresentationData.flags &= ~NS_MATHML_DISPLAYSTYLE;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMathMLContainerFrame::UpdatePresentationDataFromChildAt(PRInt32 aIndex,
|
|
PRInt32 aScriptLevelIncrement,
|
|
PRBool aDisplayStyle)
|
|
{
|
|
nsIFrame* childFrame = mFrames.FirstChild();
|
|
while (nsnull != childFrame) {
|
|
if (!IsOnlyWhitespace(childFrame)) {
|
|
if (0 >= aIndex--) {
|
|
nsIMathMLFrame* aMathMLFrame = nsnull;
|
|
nsresult rv = childFrame->QueryInterface(nsIMathMLFrame::GetIID(), (void**)&aMathMLFrame);
|
|
if (NS_SUCCEEDED(rv) && nsnull != aMathMLFrame) {
|
|
// update
|
|
aMathMLFrame->UpdatePresentationData(aScriptLevelIncrement, aDisplayStyle);
|
|
// propagate down the subtrees
|
|
aMathMLFrame->UpdatePresentationDataFromChildAt(0, aScriptLevelIncrement, aDisplayStyle);
|
|
}
|
|
}
|
|
}
|
|
childFrame->GetNextSibling(&childFrame);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
PRInt32
|
|
nsMathMLContainerFrame::FindSmallestFontSizeFor(nsIFrame* aFrame)
|
|
{
|
|
nsStyleFont aFont;
|
|
nsCOMPtr<nsIStyleContext> aStyleContext;
|
|
aFrame->GetStyleContext(getter_AddRefs(aStyleContext));
|
|
aStyleContext->GetStyle(eStyleStruct_Font, aFont);
|
|
// PRInt32 fontSize = NSTwipsToFloorIntPoints(aFont.mFont.size);
|
|
PRInt32 fontSize = aFont.mFont.size;
|
|
|
|
PRInt32 childSize;
|
|
nsIFrame* childFrame;
|
|
aFrame->FirstChild(nsnull, &childFrame);
|
|
while (nsnull != childFrame) {
|
|
if (!IsOnlyWhitespace(childFrame)) {
|
|
childSize = FindSmallestFontSizeFor(childFrame);
|
|
if (fontSize > childSize) fontSize = childSize;
|
|
}
|
|
childFrame->GetNextSibling(&childFrame);
|
|
}
|
|
return fontSize;
|
|
}
|
|
|
|
// helper method to alter the style context
|
|
// This method is used for switching the font to a subscript/superscript font in
|
|
// mfrac, msub, msup, msubsup, munder, mover, munderover, mmultiscripts
|
|
|
|
NS_IMETHODIMP
|
|
nsMathMLContainerFrame::InsertScriptLevelStyleContext(nsIPresContext* aPresContext)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
nsIFrame* nextFrame = mFrames.FirstChild();
|
|
while (nsnull != nextFrame && NS_SUCCEEDED(rv)) {
|
|
nsIFrame* childFrame = nextFrame;
|
|
rv = nextFrame->GetNextSibling(&nextFrame);
|
|
if (!IsOnlyWhitespace(childFrame) && NS_SUCCEEDED(rv)) {
|
|
|
|
// see if the child frame implements the nsIMathMLFrame interface
|
|
nsIMathMLFrame* aMathMLFrame = nsnull;
|
|
rv = childFrame->QueryInterface(nsIMathMLFrame::GetIID(), (void**)&aMathMLFrame);
|
|
if (nsnull != aMathMLFrame && NS_SUCCEEDED(rv)) {
|
|
|
|
// get the scriptlevel of the child
|
|
nsPresentationData childData;
|
|
aMathMLFrame->GetPresentationData(childData);
|
|
|
|
// Iteration to set a style context for the script level font.
|
|
// Wow, here is what is happening: the style system requires that any style context
|
|
// *must* be uniquely associated to a frame. So we insert as many frames as needed
|
|
// to scale-down (or scale-up) the fontsize.
|
|
|
|
PRInt32 gap = childData.scriptLevel - mPresentationData.scriptLevel;
|
|
if (0 != gap) {
|
|
// We are about to change the font-size... We first see if we
|
|
// are in the scope of a <mstyle> that tells us what to do.
|
|
// This is one of the most obscure part to implement in the spec...
|
|
/*
|
|
The REC says:
|
|
|
|
Whenever the scriptlevel is changed, either automatically or by being
|
|
explicitly incremented, decremented, or set, the current font size is
|
|
multiplied by the value of scriptsizemultiplier to the power of the
|
|
change in scriptlevel. For example, if scriptlevel is increased by 2,
|
|
the font size is multiplied by scriptsizemultiplier twice in succession;
|
|
if scriptlevel is explicitly set to 2 when it had been 3, the font size
|
|
is divided by scriptsizemultiplier.
|
|
|
|
The default value of scriptsizemultiplier is less than one (in fact, it
|
|
is approximately the square root of 1/2), resulting in a smaller font size
|
|
with increasing scriptlevel. To prevent scripts from becoming unreadably
|
|
small, the font size is never allowed to go below the value of
|
|
scriptminsize as a result of a change to scriptlevel, though it can be
|
|
set to a lower value using the fontsize attribute on <mstyle> or on
|
|
token elements. If a change to scriptlevel would cause the font size to
|
|
become lower than scriptminsize using the above formula, the font size
|
|
is instead set equal to scriptminsize within the subexpression for which
|
|
scriptlevel was changed.
|
|
|
|
In the syntax for scriptminsize, v-unit represents a unit of vertical
|
|
length. The most common unit for specifying font sizes in typesetting
|
|
is pt (points).
|
|
*/
|
|
|
|
// default scriptsizemultiplier = 0.71
|
|
// default scriptminsize = 8pt
|
|
|
|
// here we only consider scriptminsize, and use the default
|
|
// smaller-font-size algorithm of the style system
|
|
PRInt32 scriptminsize = NSIntPointsToTwips(8);
|
|
|
|
// see if the scriptminsize attribute is on <mstyle> that wraps us
|
|
nsAutoString value;
|
|
nsIFrame* mstyleFrame = mPresentationData.mstyle;
|
|
if (mstyleFrame) {
|
|
nsCOMPtr<nsIContent> mstyleContent;
|
|
mstyleFrame->GetContent(getter_AddRefs(mstyleContent));
|
|
if (NS_CONTENT_ATTR_HAS_VALUE == mstyleContent->GetAttribute(kNameSpaceID_None,
|
|
nsMathMLAtoms::scriptminsize_, value))
|
|
{
|
|
PRInt32 errorCode;
|
|
PRInt32 userValue = value.ToInteger(&errorCode);
|
|
if (NS_SUCCEEDED(errorCode)) {
|
|
// assume unit is point
|
|
// XXX need consistent, default unit throughout the code
|
|
scriptminsize = NSIntPointsToTwips(userValue);
|
|
}
|
|
else {
|
|
// XXX TODO: try to see if it is a h/v-unit like 1ex, 2px, 1em
|
|
}
|
|
}
|
|
}
|
|
|
|
// get Nav's magic font scaler
|
|
PRInt32 scaler;
|
|
aPresContext->GetFontScaler(&scaler);
|
|
float scaleFactor = nsStyleUtil::GetScalingFactor(scaler);
|
|
const nsFont& defaultFont = aPresContext->GetDefaultFontDeprecated();
|
|
|
|
nsCOMPtr<nsIContent> childContent;
|
|
childFrame->GetContent(getter_AddRefs(childContent));
|
|
|
|
nsCOMPtr<nsIPresShell> shell;
|
|
aPresContext->GetShell(getter_AddRefs(shell));
|
|
|
|
nsIFrame* firstFrame = nsnull;
|
|
nsIFrame* lastFrame = this;
|
|
nsIStyleContext* lastStyleContext = mStyleContext;
|
|
nsCOMPtr<nsIStyleContext> newStyleContext;
|
|
|
|
// XXX seems not to decrease when the initail font-size is large (100pt)
|
|
nsAutoString fontSize = (0 < gap)
|
|
? ":-moz-math-font-size-smaller"
|
|
: ":-moz-math-font-size-larger";
|
|
nsCOMPtr<nsIAtom> fontAtom(getter_AddRefs(NS_NewAtom(fontSize)));
|
|
|
|
PRBool isSmaller = PR_TRUE;
|
|
if (0 > gap) { isSmaller = PR_FALSE; gap = -gap; } // absolute value
|
|
|
|
PRInt32 smallestFontSize, smallestFontIndex;
|
|
if (isSmaller) {
|
|
// find the smallest font-size in this subtree
|
|
smallestFontSize = FindSmallestFontSizeFor(childFrame);
|
|
}
|
|
|
|
while (0 < gap--) {
|
|
|
|
if (isSmaller) {
|
|
// look ahead for the next smallest font size that will be in the subtree
|
|
smallestFontIndex = nsStyleUtil::FindNextSmallerFontSize(smallestFontSize, (PRInt32)defaultFont.size, scaleFactor);
|
|
smallestFontSize = nsStyleUtil::CalcFontPointSize(smallestFontIndex, (PRInt32)defaultFont.size, scaleFactor);
|
|
//printf("About to move to fontsize:%dpt(%dtwips)\n",
|
|
//NSTwipsToFloorIntPoints(smallestFontSize), smallestFontSize);
|
|
if (smallestFontSize < scriptminsize) {
|
|
// don't bother doing any work
|
|
//printf("..... stopping ......\n");
|
|
// XXX there should be a mechanism so that we never try this subtree again
|
|
break;
|
|
}
|
|
}
|
|
|
|
aPresContext->ResolvePseudoStyleContextFor(childContent, fontAtom, lastStyleContext,
|
|
PR_FALSE, getter_AddRefs(newStyleContext));
|
|
if (newStyleContext && newStyleContext.get() != lastStyleContext) {
|
|
// create a new wrapper frame and append it as sole child of the last created frame
|
|
nsIFrame* newFrame = nsnull;
|
|
NS_NewMathMLWrapperFrame(shell, &newFrame);
|
|
NS_ASSERTION(newFrame, "Failed to create new frame");
|
|
if (!newFrame) break;
|
|
newFrame->Init(aPresContext, childContent, lastFrame, newStyleContext, nsnull);
|
|
|
|
if (nsnull == firstFrame) {
|
|
firstFrame = newFrame;
|
|
}
|
|
if (this != lastFrame) {
|
|
lastFrame->SetInitialChildList(aPresContext, nsnull, newFrame);
|
|
}
|
|
lastStyleContext = newStyleContext;
|
|
lastFrame = newFrame;
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
if (nsnull != firstFrame) { // at least one new frame was created
|
|
mFrames.ReplaceFrame(this, childFrame, firstFrame);
|
|
childFrame->SetParent(lastFrame);
|
|
childFrame->SetNextSibling(nsnull);
|
|
aPresContext->ReParentStyleContext(childFrame, lastStyleContext);
|
|
lastFrame->SetInitialChildList(aPresContext, nsnull, childFrame);
|
|
|
|
// if the child was an embellished operator,
|
|
// make the whole list embellished as well
|
|
nsEmbellishData embellishData;
|
|
aMathMLFrame->GetEmbellishData(embellishData);
|
|
if (0 != embellishData.flags && nsnull != embellishData.firstChild) {
|
|
do { // walk the hierarchy in a bottom-up manner
|
|
rv = lastFrame->QueryInterface(nsIMathMLFrame::GetIID(), (void**)&aMathMLFrame);
|
|
NS_ASSERTION(NS_SUCCEEDED(rv) && aMathMLFrame, "Mystery!");
|
|
if (NS_FAILED(rv) || !aMathMLFrame) break;
|
|
embellishData.firstChild = childFrame;
|
|
aMathMLFrame->SetEmbellishData(embellishData);
|
|
childFrame = lastFrame;
|
|
lastFrame->GetParent(&lastFrame);
|
|
} while (lastFrame != this);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
|
|
/* //////////////////
|
|
* Frame construction
|
|
* =============================================================================
|
|
*/
|
|
|
|
NS_IMETHODIMP
|
|
nsMathMLContainerFrame::Paint(nsIPresContext* aPresContext,
|
|
nsIRenderingContext& aRenderingContext,
|
|
const nsRect& aDirtyRect,
|
|
nsFramePaintLayer aWhichLayer)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
|
|
rv = nsHTMLContainerFrame::Paint(aPresContext,
|
|
aRenderingContext,
|
|
aDirtyRect,
|
|
aWhichLayer);
|
|
#ifdef SHOW_BOUNDING_BOX
|
|
// for visual debug
|
|
// ----------------
|
|
// if you want to see your bounding box, make sure to properly fill
|
|
// your mBoundingMetrics and mReference point, and set
|
|
// mPresentationData.flags |= NS_MATHML_SHOW_BOUNDING_METRICS
|
|
// in the Init() of your sub-class
|
|
|
|
if (NS_FRAME_PAINT_LAYER_FOREGROUND == aWhichLayer &&
|
|
NS_MATHML_PAINT_BOUNDING_METRICS(mPresentationData.flags))
|
|
{
|
|
aRenderingContext.SetColor(NS_RGB(0,0,255));
|
|
|
|
nscoord x = mReference.x + mBoundingMetrics.leftBearing;
|
|
nscoord y = mReference.y;
|
|
nscoord w = mBoundingMetrics.rightBearing - mBoundingMetrics.leftBearing;
|
|
nscoord h = mBoundingMetrics.ascent + mBoundingMetrics.descent;
|
|
|
|
aRenderingContext.DrawRect(x,y,w,h);
|
|
}
|
|
#endif
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMathMLContainerFrame::Init(nsIPresContext* aPresContext,
|
|
nsIContent* aContent,
|
|
nsIFrame* aParent,
|
|
nsIStyleContext* aContext,
|
|
nsIFrame* aPrevInFlow)
|
|
{
|
|
nsresult rv;
|
|
// first, let the base class do its Init()
|
|
rv = nsHTMLContainerFrame::Init(aPresContext, aContent, aParent, aContext, aPrevInFlow);
|
|
|
|
// now, if our parent implements the nsIMathMLFrame interface, we inherit
|
|
// its scriptlevel and displaystyle. If the parent later wishes to increment
|
|
// with other values, it will do so in its SetInitialChildList() method.
|
|
|
|
mPresentationData.flags = NS_MATHML_DISPLAYSTYLE;
|
|
mPresentationData.scriptLevel = 0;
|
|
mPresentationData.mstyle = nsnull;
|
|
|
|
mEmbellishData.flags = 0;
|
|
mEmbellishData.firstChild = nsnull;
|
|
|
|
nsIMathMLFrame* aMathMLFrame = nsnull;
|
|
nsresult res = aParent->QueryInterface(nsIMathMLFrame::GetIID(), (void**)&aMathMLFrame);
|
|
if (NS_SUCCEEDED(res) && nsnull != aMathMLFrame) {
|
|
nsPresentationData parentData;
|
|
aMathMLFrame->GetPresentationData(parentData);
|
|
|
|
mPresentationData.mstyle = parentData.mstyle;
|
|
mPresentationData.scriptLevel = parentData.scriptLevel;
|
|
if (NS_MATHML_IS_DISPLAYSTYLE(parentData.flags))
|
|
mPresentationData.flags |= NS_MATHML_DISPLAYSTYLE;
|
|
else
|
|
mPresentationData.flags &= ~NS_MATHML_DISPLAYSTYLE;
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMathMLContainerFrame::SetInitialChildList(nsIPresContext* aPresContext,
|
|
nsIAtom* aListName,
|
|
nsIFrame* aChildList)
|
|
{
|
|
// First, let the base class do its job
|
|
nsresult rv;
|
|
rv = nsHTMLContainerFrame::SetInitialChildList(aPresContext, aListName, aChildList);
|
|
|
|
// Next, since we are an inline frame, and since we are a container, we have to
|
|
// be very careful with the way we treat our children. Things look okay when
|
|
// all of our children are only MathML frames. But there are problems if one of
|
|
// our children happens to be an nsInlineFrame, e.g., from generated content such
|
|
// as :before { content: open-quote } or :after { content: close-quote }
|
|
// The code asserts during reflow (in nsLineLayout::BeginSpan)
|
|
// Also there are problems when our children are hybrid, e.g., from html markups.
|
|
// In short, the nsInlineFrame class expects a number of *invariants* that are not
|
|
// met when we mix things.
|
|
|
|
// So what we do here is to wrap children that happen to be nsInlineFrames in
|
|
// anonymous block frames.
|
|
// XXX Question: Do we have to handle Insert/Remove/Append on behalf of
|
|
// these anonymous blocks?
|
|
// Note: By construction, our anonymous blocks have only one child.
|
|
|
|
nsIFrame* next = mFrames.FirstChild();
|
|
while (next) {
|
|
nsIFrame* child = next;
|
|
rv = next->GetNextSibling(&next);
|
|
if (!IsOnlyWhitespace(child)) {
|
|
nsInlineFrame* inlineFrame = nsnull;
|
|
rv = child->QueryInterface(nsInlineFrame::kInlineFrameCID, (void**)&inlineFrame);
|
|
if (NS_SUCCEEDED(rv) && inlineFrame) {
|
|
// create a new anonymous block frame to wrap this child...
|
|
nsCOMPtr<nsIPresShell> shell;
|
|
aPresContext->GetShell(getter_AddRefs(shell));
|
|
nsIFrame* anonymous;
|
|
rv = NS_NewBlockFrame(shell, &anonymous);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
nsCOMPtr<nsIStyleContext> newStyleContext;
|
|
aPresContext->ResolvePseudoStyleContextFor(mContent, nsHTMLAtoms::mozAnonymousBlock,
|
|
mStyleContext, PR_FALSE,
|
|
getter_AddRefs(newStyleContext));
|
|
rv = anonymous->Init(aPresContext, mContent, this, newStyleContext, nsnull);
|
|
if (NS_FAILED(rv)) {
|
|
anonymous->Destroy(aPresContext);
|
|
return rv;
|
|
}
|
|
mFrames.ReplaceFrame(this, child, anonymous);
|
|
child->SetParent(anonymous);
|
|
child->SetNextSibling(nsnull);
|
|
aPresContext->ReParentStyleContext(child, newStyleContext);
|
|
anonymous->SetInitialChildList(aPresContext, nsnull, child);
|
|
}
|
|
}
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
#if 0
|
|
// helper method to compute the desired size of a frame
|
|
NS_IMETHODIMP
|
|
nsMathMLContainerFrame::GetDesiredStretchSize(nsIPresContext* aPresContext,
|
|
nsIRenderingContext& aRenderingContext,
|
|
nsHTMLReflowMetrics& aDesiredSize)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
nsRect rect;
|
|
nscoord count = 0;
|
|
nsIFrame* childFrame = mFrames.FirstChild();
|
|
while (childFrame) {
|
|
if (!IsOnlyWhitespace(childFrame)) {
|
|
childFrame->GetRect(rect);
|
|
if (IsEmbellishOperator(childFrame)) {
|
|
// We have encountered an embellished operator...
|
|
// It is treated as if the embellishments were not there!
|
|
nsIMathMLFrame* aMathMLFrame = nsnull;
|
|
rv = childFrame->QueryInterface(nsIMathMLFrame::GetIID(), (void**)&aMathMLFrame);
|
|
NS_ASSERTION(NS_SUCCEEDED(rv) && aMathMLFrame, "Mystery!");
|
|
if (NS_SUCCEEDED(rv) && aMathMLFrame) {
|
|
nsEmbellishData embellishData;
|
|
aMathMLFrame->GetEmbellishData(embellishData);
|
|
embellishData.core->GetRect(rect);
|
|
}
|
|
}
|
|
if (0 == aDirection) { // for horizontal positioning of child frames like in mrow
|
|
aDesiredSize.width += rect.width;
|
|
if (aDesiredSize.ascent < rect.y) aDesiredSize.ascent = rect.y;
|
|
if (aDesiredSize.descent < rect.x) aDesiredSize.descent = rect.x;
|
|
}
|
|
else if (1 == aDirection) { // for vertical positioning of child frames like in mfrac, munder
|
|
if (0 == count) { // it is the ascent and descent of the 'base' that is returned by defaul
|
|
aDesiredSize.ascent = rect.y;
|
|
aDesiredSize.descent = rect.x;
|
|
}
|
|
if (aDesiredSize.width < rect.width) aDesiredSize.width = rect.width;
|
|
}
|
|
count++;
|
|
}
|
|
rv = childFrame->GetNextSibling(&childFrame);
|
|
NS_ASSERTION(NS_SUCCEEDED(rv),"failed to get next child");
|
|
}
|
|
aDesiredSize.height = aDesiredSize.ascent + aDesiredSize.descent;
|
|
|
|
// Check if embellished operators didn't have (non-empty) siblings...
|
|
if (0 < count && 0 == aDirection && 0 == aDesiredSize.height) {
|
|
// Return the font ascent and font descent
|
|
nsCOMPtr<nsIFontMetrics> fm;
|
|
nsStyleFont font;
|
|
mStyleContext->GetStyle(eStyleStruct_Font, font);
|
|
aPresContext->GetMetricsFor(font.mFont, getter_AddRefs(fm));
|
|
fm->GetMaxAscent(aDesiredSize.ascent);
|
|
fm->GetMaxDescent(aDesiredSize.descent);
|
|
aDesiredSize.height = aDesiredSize.ascent + aDesiredSize.descent;
|
|
}
|
|
if (0 < count && 1 == aDirection && 0 == aDesiredSize.width) {
|
|
// Return em
|
|
nsStyleFont font;
|
|
mStyleContext->GetStyle(eStyleStruct_Font, font);
|
|
aDesiredSize.width = NSToCoordRound(float(font.mFont.size));
|
|
}
|
|
return NS_OK;
|
|
}
|
|
#endif
|
|
|
|
|
|
// helper function to reflow token elements
|
|
// note that mBoundingMetrics is computed here
|
|
nsresult
|
|
nsMathMLContainerFrame::ReflowTokenFor(nsIFrame* aFrame,
|
|
nsIPresContext* aPresContext,
|
|
nsHTMLReflowMetrics& aDesiredSize,
|
|
const nsHTMLReflowState& aReflowState,
|
|
nsReflowStatus& aStatus)
|
|
{
|
|
NS_PRECONDITION(aFrame, "null arg");
|
|
nsresult rv = NS_OK;
|
|
|
|
// ask our children to compute their bounding metrics
|
|
nsHTMLReflowMetrics childDesiredSize(aDesiredSize.maxElementSize,
|
|
aDesiredSize.mFlags | NS_REFLOW_CALC_BOUNDING_METRICS);
|
|
nsSize availSize(aReflowState.mComputedWidth, aReflowState.mComputedHeight);
|
|
nsBoundingMetrics bm;
|
|
PRInt32 count = 0;
|
|
nsIFrame* childFrame;
|
|
aFrame->FirstChild(nsnull, &childFrame);
|
|
while (childFrame) {
|
|
if (IsOnlyWhitespace(childFrame)) {
|
|
ReflowEmptyChild(aPresContext, childFrame);
|
|
}
|
|
else {
|
|
nsHTMLReflowState childReflowState(aPresContext, aReflowState,
|
|
childFrame, availSize);
|
|
rv = NS_STATIC_CAST(nsMathMLContainerFrame*,
|
|
aFrame)->ReflowChild(childFrame,
|
|
aPresContext, childDesiredSize,
|
|
childReflowState, aStatus);
|
|
NS_ASSERTION(NS_FRAME_IS_COMPLETE(aStatus), "bad status");
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
// origins are used as placeholders to store the child's ascent and descent.
|
|
childFrame->SetRect(aPresContext,
|
|
nsRect(childDesiredSize.descent, childDesiredSize.ascent,
|
|
childDesiredSize.width, childDesiredSize.height));
|
|
// compute and cache the bounding metrics
|
|
if (0 == count)
|
|
bm = childDesiredSize.mBoundingMetrics;
|
|
else
|
|
bm += childDesiredSize.mBoundingMetrics;
|
|
|
|
count++;
|
|
}
|
|
rv = childFrame->GetNextSibling(&childFrame);
|
|
NS_ASSERTION(NS_SUCCEEDED(rv),"failed to get next child");
|
|
}
|
|
NS_STATIC_CAST(nsMathMLContainerFrame*, aFrame)->SetBoundingMetrics(bm);
|
|
|
|
// Place and size children
|
|
NS_STATIC_CAST(nsMathMLContainerFrame*,
|
|
aFrame)->FinalizeReflow(0, aPresContext, *aReflowState.rendContext,
|
|
aDesiredSize);
|
|
return NS_OK;
|
|
}
|
|
|
|
// helper function to place token elements
|
|
// mBoundingMetrics is computed at the ReflowToken pass, it is
|
|
// not computed here because our children may be text frames that
|
|
// do not implement the GetBoundingMetrics() interface.
|
|
nsresult
|
|
nsMathMLContainerFrame::PlaceTokenFor(nsIFrame* aFrame,
|
|
nsIPresContext* aPresContext,
|
|
nsIRenderingContext& aRenderingContext,
|
|
PRBool aPlaceOrigin,
|
|
nsHTMLReflowMetrics& aDesiredSize)
|
|
{
|
|
aDesiredSize.width = aDesiredSize.height = aDesiredSize.ascent = aDesiredSize.descent = 0;
|
|
|
|
nsRect rect;
|
|
nsIFrame* childFrame;
|
|
aFrame->FirstChild(nsnull, &childFrame);
|
|
while (childFrame) {
|
|
if (!IsOnlyWhitespace(childFrame)) {
|
|
childFrame->GetRect(rect);
|
|
aDesiredSize.width += rect.width;
|
|
if (aDesiredSize.descent < rect.x) aDesiredSize.descent = rect.x;
|
|
if (aDesiredSize.ascent < rect.y) aDesiredSize.ascent = rect.y;
|
|
}
|
|
childFrame->GetNextSibling(&childFrame);
|
|
}
|
|
aDesiredSize.height = aDesiredSize.ascent + aDesiredSize.descent;
|
|
|
|
if (aPlaceOrigin) {
|
|
nscoord dy;
|
|
nscoord dx = 0;
|
|
|
|
aFrame->FirstChild(nsnull, &childFrame);
|
|
while (childFrame) {
|
|
childFrame->GetRect(rect);
|
|
|
|
nsHTMLReflowMetrics childSize(nsnull);
|
|
childSize.width = rect.width;
|
|
childSize.height = rect.height;
|
|
|
|
// Place and size the child
|
|
dy = aDesiredSize.ascent - rect.y;
|
|
NS_STATIC_CAST(nsMathMLContainerFrame*,
|
|
aFrame)->FinishReflowChild(childFrame, aPresContext,
|
|
childSize, dx, dy, 0);
|
|
dx += rect.width;
|
|
childFrame->GetNextSibling(&childFrame);
|
|
}
|
|
}
|
|
nsBoundingMetrics bm;
|
|
NS_STATIC_CAST(nsMathMLContainerFrame*,
|
|
aFrame)->GetBoundingMetrics(bm);
|
|
NS_STATIC_CAST(nsMathMLContainerFrame*,
|
|
aFrame)->SetReference(nsPoint(0,aDesiredSize.ascent-bm.ascent));
|
|
return NS_OK;
|
|
}
|
|
|
|
// helper function to reflow all children
|
|
NS_IMETHODIMP
|
|
nsMathMLContainerFrame::ReflowChildren(PRInt32 aDirection,
|
|
nsIPresContext* aPresContext,
|
|
nsHTMLReflowMetrics& aDesiredSize,
|
|
const nsHTMLReflowState& aReflowState,
|
|
nsReflowStatus& aStatus)
|
|
{
|
|
NS_PRECONDITION(aDirection==0 || aDirection==1, "Unknown direction");
|
|
|
|
nsresult rv = NS_OK;
|
|
nsReflowStatus childStatus;
|
|
nsSize availSize(aReflowState.mComputedWidth, aReflowState.mComputedHeight);
|
|
|
|
// Asks the child to cache its bounding metrics
|
|
nsHTMLReflowMetrics childDesiredSize(aDesiredSize.maxElementSize,
|
|
aDesiredSize.mFlags | NS_REFLOW_CALC_BOUNDING_METRICS);
|
|
|
|
aDesiredSize.width = aDesiredSize.height = aDesiredSize.ascent = aDesiredSize.descent = 0;
|
|
|
|
nscoord count = 0;
|
|
nsIFrame* childFrame = mFrames.FirstChild();
|
|
while (childFrame) {
|
|
//////////////
|
|
// WHITESPACE: don't forget that whitespace doesn't count in MathML!
|
|
if (IsOnlyWhitespace(childFrame)) {
|
|
ReflowEmptyChild(aPresContext, childFrame);
|
|
}
|
|
else {
|
|
nsHTMLReflowState childReflowState(aPresContext, aReflowState,
|
|
childFrame, availSize);
|
|
rv = ReflowChild(childFrame, aPresContext, childDesiredSize,
|
|
childReflowState, childStatus);
|
|
NS_ASSERTION(NS_FRAME_IS_COMPLETE(childStatus), "bad status");
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
// At this stage, the origin points of the children have no use, so we will use the
|
|
// origins as placeholders to store the child's ascent and descent. Later on,
|
|
// we should set the origins so as to overwrite what we are storing there now.
|
|
childFrame->SetRect(aPresContext,
|
|
nsRect(childDesiredSize.descent, childDesiredSize.ascent,
|
|
childDesiredSize.width, childDesiredSize.height));
|
|
|
|
if (NS_MATHML_WILL_STRETCH_ALL_CHILDREN(mEmbellishData.flags) &&
|
|
IsEmbellishOperator(childFrame)) {
|
|
// We have encountered an embellished operator...
|
|
// It is treated as if the embellishments were not there!
|
|
nsIMathMLFrame* aMathMLFrame = nsnull;
|
|
rv = childFrame->QueryInterface(nsIMathMLFrame::GetIID(), (void**)&aMathMLFrame);
|
|
NS_ASSERTION(NS_SUCCEEDED(rv) && aMathMLFrame, "Mystery!");
|
|
if (NS_SUCCEEDED(rv) && aMathMLFrame) {
|
|
|
|
nsEmbellishData embellishData;
|
|
aMathMLFrame->GetEmbellishData(embellishData);
|
|
nsRect rect;
|
|
embellishData.core->GetRect(rect);
|
|
childDesiredSize.descent = rect.x;
|
|
childDesiredSize.ascent = rect.y;
|
|
childDesiredSize.width = rect.width;
|
|
childDesiredSize.height = rect.height;
|
|
|
|
#if 0
|
|
nsIRenderingContext& renderingContext = *aReflowState.rendContext;
|
|
nsStretchMetrics stretchSize;
|
|
aMathMLFrame->GetDesiredStretchSize(aPresContext, renderingContext, stretchSize);
|
|
childDesiredSize.descent = stretchSize.descent;
|
|
childDesiredSize.ascent = stretchSize.ascent;
|
|
childDesiredSize.width = stretchSize.width;
|
|
childDesiredSize.height = stretchSize.height;
|
|
#endif
|
|
}
|
|
}
|
|
if (0 == aDirection) { // for horizontal positioning of child frames like in mrow
|
|
aDesiredSize.width += childDesiredSize.width;
|
|
if (aDesiredSize.ascent < childDesiredSize.ascent) aDesiredSize.ascent = childDesiredSize.ascent;
|
|
if (aDesiredSize.descent < childDesiredSize.descent) aDesiredSize.descent = childDesiredSize.descent;
|
|
}
|
|
else if (1 == aDirection) { // for vertical positioning of child frames like in mfrac, munder
|
|
if (0 == count) { // it is the ascent and descent of the 'base' that is returned by defaul
|
|
aDesiredSize.ascent = childDesiredSize.ascent;
|
|
aDesiredSize.descent = childDesiredSize.descent;
|
|
}
|
|
if (aDesiredSize.width < childDesiredSize.width) aDesiredSize.width = childDesiredSize.width;
|
|
}
|
|
count++;
|
|
}
|
|
rv = childFrame->GetNextSibling(&childFrame);
|
|
NS_ASSERTION(NS_SUCCEEDED(rv),"failed to get next child");
|
|
}
|
|
aDesiredSize.height = aDesiredSize.ascent + aDesiredSize.descent;
|
|
|
|
// Check if embellished operators didn't have (non-empty) siblings...
|
|
if (0 < count && 0 == aDirection && 0 == aDesiredSize.height) {
|
|
// Return the font ascent and font descent
|
|
nsCOMPtr<nsIFontMetrics> fm;
|
|
nsStyleFont font;
|
|
mStyleContext->GetStyle(eStyleStruct_Font, font);
|
|
aPresContext->GetMetricsFor(font.mFont, getter_AddRefs(fm));
|
|
fm->GetMaxAscent(aDesiredSize.ascent);
|
|
fm->GetMaxDescent(aDesiredSize.descent);
|
|
aDesiredSize.height = aDesiredSize.ascent + aDesiredSize.descent;
|
|
}
|
|
if (0 < count && 1 == aDirection && 0 == aDesiredSize.width) {
|
|
// Return em
|
|
nsStyleFont font;
|
|
mStyleContext->GetStyle(eStyleStruct_Font, font);
|
|
aDesiredSize.width = NSToCoordRound(float(font.mFont.size));
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
// helper function to stretch all children
|
|
NS_IMETHODIMP
|
|
nsMathMLContainerFrame::StretchChildren(nsIPresContext* aPresContext,
|
|
nsIRenderingContext& aRenderingContext,
|
|
nsStretchDirection aStretchDirection,
|
|
nsStretchMetrics& aContainerSize)
|
|
{
|
|
nsRect rect;
|
|
nsIFrame* childFrame = mFrames.FirstChild();
|
|
while (childFrame) {
|
|
if (mEmbellishData.firstChild == childFrame) {
|
|
// Skip this child... because:
|
|
// If we are here it means we are an embellished container and
|
|
// for now, we don't touch our embellished child frame.
|
|
// Its stretch will be handled separatedly when we receive
|
|
// stretch command fired by our parent frame.
|
|
}
|
|
else {
|
|
nsIMathMLFrame* aMathMLFrame;
|
|
nsresult rv = childFrame->QueryInterface(nsIMathMLFrame::GetIID(), (void**)&aMathMLFrame);
|
|
if (NS_SUCCEEDED(rv) && aMathMLFrame) {
|
|
// retrieve the metrics that was stored at the ReflowChildren pass
|
|
childFrame->GetRect(rect);
|
|
nsStretchMetrics childSize(rect.x, rect.y, rect.width, rect.height);
|
|
aMathMLFrame->Stretch(aPresContext, aRenderingContext,
|
|
aStretchDirection, aContainerSize, childSize);
|
|
// store the updated metrics
|
|
childFrame->SetRect(aPresContext,
|
|
nsRect(childSize.descent, childSize.ascent,
|
|
childSize.width, childSize.height));
|
|
}
|
|
}
|
|
childFrame->GetNextSibling(&childFrame);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMathMLContainerFrame::Reflow(nsIPresContext* aPresContext,
|
|
nsHTMLReflowMetrics& aDesiredSize,
|
|
const nsHTMLReflowState& aReflowState,
|
|
nsReflowStatus& aStatus)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
|
|
/////////////
|
|
// Reflow children
|
|
ReflowChildren(0, aPresContext, aDesiredSize, aReflowState, aStatus);
|
|
|
|
/////////////
|
|
// If we are a container which is entitled to stretch its children, then we
|
|
// ask our stretchy children to stretch themselves
|
|
|
|
nsIRenderingContext& renderingContext = *aReflowState.rendContext;
|
|
|
|
if (NS_MATHML_WILL_STRETCH_ALL_CHILDREN(mEmbellishData.flags)) {
|
|
nsStretchMetrics containerSize(aDesiredSize);
|
|
nsStretchDirection stretchDir = NS_STRETCH_DIRECTION_VERTICAL;
|
|
|
|
StretchChildren(aPresContext, renderingContext, stretchDir, containerSize);
|
|
}
|
|
|
|
/////////////
|
|
// Place children now by re-adjusting the origins to align the baselines
|
|
FinalizeReflow(0, aPresContext, renderingContext, aDesiredSize);
|
|
|
|
aStatus = NS_FRAME_COMPLETE;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMathMLContainerFrame::Place(nsIPresContext* aPresContext,
|
|
nsIRenderingContext& aRenderingContext,
|
|
PRBool aPlaceOrigin,
|
|
nsHTMLReflowMetrics& aDesiredSize)
|
|
{
|
|
aDesiredSize.width = aDesiredSize.height = aDesiredSize.ascent = aDesiredSize.descent = 0;
|
|
|
|
PRInt32 count = 0;
|
|
nsBoundingMetrics bm;
|
|
nsHTMLReflowMetrics childSize(nsnull);
|
|
nsIFrame* childFrame = mFrames.FirstChild();
|
|
while (childFrame) {
|
|
if (!IsOnlyWhitespace(childFrame)) {
|
|
GetReflowAndBoundingMetricsFor(childFrame, childSize, bm);
|
|
|
|
aDesiredSize.width += childSize.width;
|
|
if (aDesiredSize.descent < childSize.descent) aDesiredSize.descent = childSize.descent;
|
|
if (aDesiredSize.ascent < childSize.ascent) aDesiredSize.ascent = childSize.ascent;
|
|
|
|
// Compute and cache our bounding metrics
|
|
if (0 == count)
|
|
mBoundingMetrics = bm;
|
|
else
|
|
mBoundingMetrics += bm;
|
|
|
|
count++;
|
|
}
|
|
childFrame->GetNextSibling(&childFrame);
|
|
}
|
|
aDesiredSize.height = aDesiredSize.ascent + aDesiredSize.descent;
|
|
|
|
if (aPlaceOrigin) {
|
|
nsRect rect;
|
|
|
|
nscoord dy;
|
|
nscoord dx = 0;
|
|
childFrame = mFrames.FirstChild();
|
|
while (childFrame) {
|
|
childFrame->GetRect(rect);
|
|
|
|
childSize.width = rect.width;
|
|
childSize.height = rect.height;
|
|
|
|
// Place and size the child
|
|
dy = aDesiredSize.ascent - rect.y;
|
|
FinishReflowChild(childFrame, aPresContext, childSize, dx, dy, 0);
|
|
|
|
dx += rect.width;
|
|
childFrame->GetNextSibling(&childFrame);
|
|
}
|
|
}
|
|
|
|
mReference.x = 0;
|
|
mReference.y = aDesiredSize.ascent - mBoundingMetrics.ascent;
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
//==========================
|
|
// *BEWARE* of the wrapper frame!
|
|
// This is the frame that is inserted to alter the style context of
|
|
// scripting elements. What this means is that looking for your parent
|
|
// or your siblings with (possible several) wrapper frames around you
|
|
// can make you wonder what is going on. For example, the direct parent
|
|
// of the subscript within <msub> is not <msub>, but instead the wrapper
|
|
// frame that was insterted to alter the style context of the subscript!
|
|
// You will seldom need to find out who is exactly your parent. You should
|
|
// first rethink your code to see if you can avoid finding who is your parent.
|
|
// Be careful, there are wrapper frames all over the place, and probably
|
|
// one or many are wrapping you if you are in a position where the
|
|
// scriptlevel is non zero.
|
|
|
|
nsresult
|
|
NS_NewMathMLWrapperFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame)
|
|
{
|
|
NS_PRECONDITION(aNewFrame, "null OUT ptr");
|
|
if (nsnull == aNewFrame) {
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
nsMathMLWrapperFrame* it = new (aPresShell) nsMathMLWrapperFrame;
|
|
if (nsnull == it) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
*aNewFrame = it;
|
|
return NS_OK;
|
|
}
|
|
|
|
nsMathMLWrapperFrame::nsMathMLWrapperFrame()
|
|
{
|
|
}
|
|
|
|
nsMathMLWrapperFrame::~nsMathMLWrapperFrame()
|
|
{
|
|
}
|
|
|
|
// For Reflow() and Place(), pretend we are a token to re-use code
|
|
NS_IMETHODIMP
|
|
nsMathMLWrapperFrame::Reflow(nsIPresContext* aPresContext,
|
|
nsHTMLReflowMetrics& aDesiredSize,
|
|
const nsHTMLReflowState& aReflowState,
|
|
nsReflowStatus& aStatus)
|
|
{
|
|
return ReflowTokenFor(this, aPresContext, aDesiredSize,
|
|
aReflowState, aStatus);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMathMLWrapperFrame::Place(nsIPresContext* aPresContext,
|
|
nsIRenderingContext& aRenderingContext,
|
|
PRBool aPlaceOrigin,
|
|
nsHTMLReflowMetrics& aDesiredSize)
|
|
{
|
|
return PlaceTokenFor(this, aPresContext, aRenderingContext,
|
|
aPlaceOrigin, aDesiredSize);
|
|
}
|