Back to index

lightning-sunbird  0.9+nobinonly
nsMathMLTokenFrame.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  * 
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  * 
00015  * The Original Code is Mozilla MathML Project.
00016  * 
00017  * The Initial Developer of the Original Code is
00018  * The University of Queensland.
00019  * Portions created by the Initial Developer are Copyright (C) 1999
00020  * the Initial Developer. All Rights Reserved.
00021  * 
00022  * Contributor(s): 
00023  *   Roger B. Sidje <rbs@maths.uq.edu.au>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either the GNU General Public License Version 2 or later (the "GPL"), or
00027  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "nsCOMPtr.h"
00040 #include "nsFrame.h"
00041 #include "nsPresContext.h"
00042 #include "nsUnitConversion.h"
00043 #include "nsStyleContext.h"
00044 #include "nsStyleConsts.h"
00045 #include "nsIRenderingContext.h"
00046 #include "nsIFontMetrics.h"
00047 
00048 #include "nsIDOMText.h"
00049 #include "nsITextContent.h"
00050 #include "nsFrameManager.h"
00051 #include "nsLayoutAtoms.h"
00052 #include "nsStyleChangeList.h"
00053 #include "nsINameSpaceManager.h"
00054 
00055 #include "nsMathMLTokenFrame.h"
00056 
00057 nsresult
00058 NS_NewMathMLTokenFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame)
00059 {
00060   NS_PRECONDITION(aNewFrame, "null OUT ptr");
00061   if (nsnull == aNewFrame) {
00062     return NS_ERROR_NULL_POINTER;
00063   }
00064   nsMathMLTokenFrame* it = new (aPresShell) nsMathMLTokenFrame;
00065   if (nsnull == it) {
00066     return NS_ERROR_OUT_OF_MEMORY;
00067   }
00068   *aNewFrame = it;  
00069   return NS_OK;
00070 }
00071 
00072 nsMathMLTokenFrame::nsMathMLTokenFrame()
00073 {
00074 }
00075 
00076 nsMathMLTokenFrame::~nsMathMLTokenFrame()
00077 {
00078 }
00079 
00080 eMathMLFrameType
00081 nsMathMLTokenFrame::GetMathMLFrameType()
00082 {
00083   // treat everything other than <mi> as ordinary...
00084   if (mContent->Tag() != nsMathMLAtoms::mi_) {
00085     return eMathMLFrameType_Ordinary;
00086   }
00087 
00088   // for <mi>, distinguish between italic and upright...
00089   nsAutoString value;
00090   mContent->GetAttr(kNameSpaceID_None, nsMathMLAtoms::MOZfontstyle, value);
00091 
00092   // treat invariant the same as italic to inherit its inter-space properties
00093   return value.EqualsLiteral("normal")
00094     ? eMathMLFrameType_UprightIdentifier
00095     : eMathMLFrameType_ItalicIdentifier;
00096 }
00097 
00098 static void
00099 CompressWhitespace(nsIContent* aContent)
00100 {
00101   PRUint32 numKids = aContent->GetChildCount();
00102   for (PRUint32 kid = 0; kid < numKids; kid++) {
00103     nsCOMPtr<nsITextContent> tc(do_QueryInterface(aContent->GetChildAt(kid)));
00104     if (tc && tc->IsContentOfType(nsIContent::eTEXT)) {
00105       nsAutoString text;
00106       tc->AppendTextTo(text);
00107       text.CompressWhitespace();
00108       tc->SetText(text, PR_FALSE); // not meant to be used if notify is needed
00109     }
00110   }
00111 }
00112 
00113 NS_IMETHODIMP
00114 nsMathMLTokenFrame::Init(nsPresContext*  aPresContext,
00115                          nsIContent*      aContent,
00116                          nsIFrame*        aParent,
00117                          nsStyleContext*  aContext,
00118                          nsIFrame*        aPrevInFlow)
00119 {
00120   // leading and trailing whitespace doesn't count -- bug 15402
00121   // brute force removal for people who do <mi> a </mi> instead of <mi>a</mi>
00122   // XXX the best fix is to skip these in nsTextFrame
00123   CompressWhitespace(aContent);
00124 
00125   // let the base class do its Init()
00126   return nsMathMLContainerFrame::Init(aPresContext, aContent, aParent, aContext, aPrevInFlow);
00127 }
00128 
00129 NS_IMETHODIMP
00130 nsMathMLTokenFrame::SetInitialChildList(nsPresContext* aPresContext,
00131                                         nsIAtom*        aListName,
00132                                         nsIFrame*       aChildList)
00133 {
00134   // First, let the base class do its work
00135   nsresult rv = nsMathMLContainerFrame::SetInitialChildList(aPresContext, aListName, aChildList);
00136   if (NS_FAILED(rv))
00137     return rv;
00138 
00139   // Safety measure to cater for math fonts with metrics that sometimes
00140   // cause glyphs in the text frames to protrude outside. Without this,
00141   // such glyphs may be clipped at the painting stage
00142   mState |= NS_FRAME_OUTSIDE_CHILDREN;
00143   nsIFrame* childFrame = mFrames.FirstChild();
00144   while (childFrame) {
00145     childFrame->AddStateBits(NS_FRAME_OUTSIDE_CHILDREN);
00146     childFrame = childFrame->GetNextSibling();
00147   }
00148 
00149   SetQuotes(aPresContext);
00150   ProcessTextData(aPresContext);
00151   return rv;
00152 }
00153 
00154 nsresult
00155 nsMathMLTokenFrame::Reflow(nsPresContext*          aPresContext,
00156                            nsHTMLReflowMetrics&     aDesiredSize,
00157                            const nsHTMLReflowState& aReflowState,
00158                            nsReflowStatus&          aStatus)
00159 {
00160   nsresult rv = NS_OK;
00161 
00162   // See if this is an incremental reflow
00163   if (aReflowState.reason == eReflowReason_Incremental) {
00164 #ifdef MATHML_NOISY_INCREMENTAL_REFLOW
00165 printf("nsMathMLContainerFrame::ReflowTokenFor:IncrementalReflow received by: ");
00166 nsFrame::ListTag(stdout, aFrame);
00167 printf("\n");
00168 #endif
00169   }
00170 
00171   // initializations needed for empty markup like <mtag></mtag>
00172   aDesiredSize.width = aDesiredSize.height = 0;
00173   aDesiredSize.ascent = aDesiredSize.descent = 0;
00174   aDesiredSize.mBoundingMetrics.Clear();
00175 
00176   // ask our children to compute their bounding metrics
00177   nsHTMLReflowMetrics childDesiredSize(aDesiredSize.mComputeMEW,
00178                       aDesiredSize.mFlags | NS_REFLOW_CALC_BOUNDING_METRICS);
00179   nsSize availSize(aReflowState.mComputedWidth, aReflowState.mComputedHeight);
00180   PRInt32 count = 0;
00181   nsIFrame* childFrame = GetFirstChild(nsnull);
00182   while (childFrame) {
00183     nsReflowReason reason = (childFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW)
00184       ? eReflowReason_Initial : aReflowState.reason;
00185     nsHTMLReflowState childReflowState(aPresContext, aReflowState,
00186                                        childFrame, availSize, reason);
00187     rv = ReflowChild(childFrame, aPresContext, childDesiredSize,
00188                      childReflowState, aStatus);
00189     //NS_ASSERTION(NS_FRAME_IS_COMPLETE(aStatus), "bad status");
00190     if (NS_FAILED(rv)) return rv;
00191 
00192     // origins are used as placeholders to store the child's ascent and descent.
00193     childFrame->SetRect(nsRect(childDesiredSize.descent, childDesiredSize.ascent,
00194                                childDesiredSize.width, childDesiredSize.height));
00195     // compute and cache the bounding metrics
00196     if (0 == count)
00197       aDesiredSize.mBoundingMetrics  = childDesiredSize.mBoundingMetrics;
00198     else
00199       aDesiredSize.mBoundingMetrics += childDesiredSize.mBoundingMetrics;
00200 
00201     count++;
00202     childFrame = childFrame->GetNextSibling();
00203   }
00204 
00205   if (aDesiredSize.mComputeMEW) {
00206     aDesiredSize.mMaxElementWidth = childDesiredSize.mMaxElementWidth;
00207   }
00208 
00209   FinishAndStoreOverflow(&aDesiredSize);
00210   // Act as if there is overflow no matter what. This is a 
00211   // safety measure to cater for math fonts with metrics that sometimes
00212   // cause glyphs in the text frames to protrude outside. Without this,
00213   // such glyphs may be clipped at the painting stage
00214   // This flag has already been set on the children as well in 
00215   // SetInitialChildList()
00216   mState |= NS_FRAME_OUTSIDE_CHILDREN;
00217 
00218   // cache the frame's mBoundingMetrics
00219   mBoundingMetrics = aDesiredSize.mBoundingMetrics;
00220 
00221   // place and size children
00222   FinalizeReflow(*aReflowState.rendContext, aDesiredSize);
00223 
00224   aStatus = NS_FRAME_COMPLETE;
00225   NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
00226   return NS_OK;
00227 }
00228 
00229 // For token elements, mBoundingMetrics is computed at the ReflowToken
00230 // pass, it is not computed here because our children may be text frames
00231 // that do not implement the GetBoundingMetrics() interface.
00232 nsresult
00233 nsMathMLTokenFrame::Place(nsIRenderingContext& aRenderingContext,
00234                           PRBool               aPlaceOrigin,
00235                           nsHTMLReflowMetrics& aDesiredSize)
00236 {
00237   nsCOMPtr<nsIFontMetrics> fm =
00238     GetPresContext()->GetMetricsFor(GetStyleFont()->mFont);
00239   nscoord ascent, descent;
00240   fm->GetMaxAscent(ascent);
00241   fm->GetMaxDescent(descent);
00242 
00243   aDesiredSize.mBoundingMetrics = mBoundingMetrics;
00244   aDesiredSize.width = mBoundingMetrics.width;
00245   aDesiredSize.ascent = PR_MAX(mBoundingMetrics.ascent, ascent);
00246   aDesiredSize.descent = PR_MAX(mBoundingMetrics.descent, descent);
00247   aDesiredSize.height = aDesiredSize.ascent + aDesiredSize.descent;
00248 
00249   if (aPlaceOrigin) {
00250     nscoord dy, dx = 0;
00251     nsIFrame* childFrame = GetFirstChild(nsnull);
00252     while (childFrame) {
00253       nsRect rect = childFrame->GetRect();
00254       nsHTMLReflowMetrics childSize(nsnull);
00255       childSize.width = rect.width;
00256       childSize.height = aDesiredSize.height; //rect.height;
00257 
00258       // place and size the child; (dx,0) makes the caret happy - bug 188146
00259       dy = rect.IsEmpty() ? 0 : aDesiredSize.ascent - rect.y;
00260       FinishReflowChild(childFrame, GetPresContext(), nsnull, childSize, dx, dy, 0);
00261       dx += rect.width;
00262       childFrame = childFrame->GetNextSibling();
00263     }
00264   }
00265 
00266   SetReference(nsPoint(0, aDesiredSize.ascent));
00267 
00268   return NS_OK;
00269 }
00270 
00271 NS_IMETHODIMP
00272 nsMathMLTokenFrame::ReflowDirtyChild(nsIPresShell* aPresShell,
00273                                      nsIFrame*     aChild)
00274 {
00275   // if we get this, it means it was called by the nsTextFrame beneath us, and
00276   // this means something changed in the text content. So re-process our text
00277 
00278   ProcessTextData(aPresShell->GetPresContext());
00279 
00280   mState |= NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN;
00281   return mParent->ReflowDirtyChild(aPresShell, this);
00282 }
00283 
00284 NS_IMETHODIMP
00285 nsMathMLTokenFrame::AttributeChanged(nsIContent*     aContent,
00286                                      PRInt32         aNameSpaceID,
00287                                      nsIAtom*        aAttribute,
00288                                      PRInt32         aModType)
00289 {
00290   if (nsMathMLAtoms::lquote_ == aAttribute ||
00291       nsMathMLAtoms::rquote_ == aAttribute) {
00292     SetQuotes(GetPresContext());
00293   }
00294 
00295   return nsMathMLContainerFrame::
00296          AttributeChanged(aContent, aNameSpaceID,
00297                           aAttribute, aModType);
00298 }
00299 
00300 void
00301 nsMathMLTokenFrame::ProcessTextData(nsPresContext* aPresContext)
00302 {
00303   SetTextStyle(aPresContext);
00304 }
00305 
00307 // For <mi>, if the content is not a single character, turn the font to
00308 // normal (this function will also query attributes from the mstyle hierarchy)
00309 
00310 void
00311 nsMathMLTokenFrame::SetTextStyle(nsPresContext* aPresContext)
00312 {
00313   if (mContent->Tag() != nsMathMLAtoms::mi_)
00314     return;
00315 
00316   if (!mFrames.FirstChild())
00317     return;
00318 
00319   // Get the text content that we enclose and its length
00320   // our content can include comment-nodes, attribute-nodes, text-nodes...
00321   // we use the DOM to make sure that we are only looking at text-nodes...
00322   nsAutoString data;
00323   PRUint32 numKids = mContent->GetChildCount();
00324   for (PRUint32 kid = 0; kid < numKids; kid++) {
00325     nsCOMPtr<nsIDOMText> kidText(do_QueryInterface(mContent->GetChildAt(kid)));
00326     if (kidText) {
00327       nsAutoString kidData;
00328       kidText->GetData(kidData);
00329       data += kidData;
00330     }
00331   }
00332 
00333   PRInt32 length = data.Length();
00334   if (!length)
00335     return;
00336 
00337   // attributes may override the default behavior
00338   nsAutoString fontstyle;
00339   GetAttribute(mContent, mPresentationData.mstyle, nsMathMLAtoms::fontstyle_, fontstyle);
00340   if (1 == length && nsMathMLOperators::LookupInvariantChar(data[0])) {
00341     // bug 65951 - a non-stylable character has its own intrinsic appearance
00342     fontstyle.AssignLiteral("invariant");
00343   }
00344   if (fontstyle.IsEmpty()) {
00345     fontstyle.AssignASCII((1 == length) ? "italic" : "normal"); 
00346   }
00347 
00348   // set the -moz-math-font-style attribute without notifying that we want a reflow
00349   mContent->SetAttr(kNameSpaceID_None, nsMathMLAtoms::MOZfontstyle, fontstyle, PR_FALSE);
00350 
00351   // then, re-resolve the style contexts in our subtree
00352   nsFrameManager *fm = aPresContext->FrameManager();
00353   nsStyleChangeList changeList;
00354   fm->ComputeStyleChangeFor(this, &changeList, NS_STYLE_HINT_NONE);
00355 #ifdef DEBUG
00356   // Use the parent frame to make sure we catch in-flows and such
00357   nsIFrame* parentFrame = GetParent();
00358   fm->DebugVerifyStyleTree(parentFrame ? parentFrame : this);
00359 #endif
00360 }
00361 
00363 // For <ms>, it is assumed that the mathml.css file contains two rules:
00364 // ms:before { content: open-quote; }
00365 // ms:after { content: close-quote; }
00366 // With these two rules, the frame construction code will
00367 // create inline frames that contain text frames which themselves
00368 // contain the text content of the quotes.
00369 // So the main idea in this code is to see if there are lquote and 
00370 // rquote attributes. If these are there, we ovewrite the default
00371 // quotes in the text frames.
00372 //
00373 // But what if the mathml.css file wasn't loaded? 
00374 // We also check that we are not relying on null pointers...
00375 
00376 static void
00377 SetQuote(nsPresContext* aPresContext, 
00378          nsIFrame*       aFrame, 
00379          nsString&       aValue)
00380 {
00381   nsIFrame* textFrame;
00382   do {
00383     // walk down the hierarchy of first children because they could be wrapped
00384     textFrame = aFrame->GetFirstChild(nsnull);
00385     if (textFrame) {
00386       if (textFrame->GetType() == nsLayoutAtoms::textFrame)
00387         break;
00388     }
00389     aFrame = textFrame;
00390   } while (textFrame);
00391   if (textFrame) {
00392     nsIContent* quoteContent = textFrame->GetContent();
00393     if (quoteContent) {
00394       nsCOMPtr<nsIDOMText> domText(do_QueryInterface(quoteContent));
00395       if (domText) {
00396         nsCOMPtr<nsITextContent> tc(do_QueryInterface(quoteContent));
00397         if (tc) {
00398           tc->SetText(aValue, PR_FALSE); // no notify since we don't want a reflow yet
00399         }
00400       }
00401     }
00402   }
00403 }
00404 
00405 void
00406 nsMathMLTokenFrame::SetQuotes(nsPresContext* aPresContext)
00407 {
00408   if (mContent->Tag() != nsMathMLAtoms::ms_)
00409     return;
00410 
00411   nsIFrame* rightFrame = nsnull;
00412   nsIFrame* baseFrame = nsnull;
00413   nsIFrame* leftFrame = mFrames.FirstChild();
00414   if (leftFrame)
00415     baseFrame = leftFrame->GetNextSibling();
00416   if (baseFrame)
00417     rightFrame = baseFrame->GetNextSibling();
00418   if (!leftFrame || !baseFrame || !rightFrame)
00419     return;
00420 
00421   nsAutoString value;
00422   // lquote
00423   if (NS_CONTENT_ATTR_NOT_THERE != GetAttribute(mContent, mPresentationData.mstyle,
00424                    nsMathMLAtoms::lquote_, value)) {
00425     SetQuote(aPresContext, leftFrame, value);
00426   }
00427   // rquote
00428   if (NS_CONTENT_ATTR_NOT_THERE != GetAttribute(mContent, mPresentationData.mstyle,
00429                    nsMathMLAtoms::rquote_, value)) {
00430     SetQuote(aPresContext, rightFrame, value);
00431   }
00432 }