Back to index

lightning-sunbird  0.9+nobinonly
nsMathMLFrame.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 of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or 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 "nsINameSpaceManager.h"
00040 #include "nsMathMLFrame.h"
00041 #include "nsMathMLChar.h"
00042 #include "nsCSSAnonBoxes.h"
00043 
00044 // used to map attributes into CSS rules
00045 #include "nsIDocument.h"
00046 #include "nsStyleSet.h"
00047 #include "nsIStyleSheet.h"
00048 #include "nsICSSStyleSheet.h"
00049 #include "nsIDOMCSSStyleSheet.h"
00050 #include "nsICSSRule.h"
00051 #include "nsICSSStyleRule.h"
00052 #include "nsStyleChangeList.h"
00053 #include "nsFrameManager.h"
00054 #include "nsNetUtil.h"
00055 #include "nsIURI.h"
00056 #include "nsContentCID.h"
00057 #include "nsAutoPtr.h"
00058 #include "nsStyleSet.h"
00059 #include "nsStyleUtil.h"
00060 static NS_DEFINE_CID(kCSSStyleSheetCID, NS_CSS_STYLESHEET_CID);
00061 
00062 
00063 NS_IMPL_QUERY_INTERFACE1(nsMathMLFrame, nsIMathMLFrame)
00064 
00065 eMathMLFrameType
00066 nsMathMLFrame::GetMathMLFrameType()
00067 {
00068   // see if it is an embellished operator (mapped to 'Op' in TeX)
00069   if (mEmbellishData.coreFrame)
00070     return GetMathMLFrameTypeFor(mEmbellishData.coreFrame);
00071 
00072   // if it has a prescribed base, fetch the type from there
00073   if (mPresentationData.baseFrame)
00074     return GetMathMLFrameTypeFor(mPresentationData.baseFrame);
00075 
00076   // everything else is treated as ordinary (mapped to 'Ord' in TeX)
00077   return eMathMLFrameType_Ordinary;  
00078 }
00079 
00080 NS_IMETHODIMP
00081 nsMathMLFrame::InheritAutomaticData(nsIFrame* aParent) 
00082 {
00083   mEmbellishData.flags = 0;
00084   mEmbellishData.coreFrame = nsnull;
00085   mEmbellishData.direction = NS_STRETCH_DIRECTION_UNSUPPORTED;
00086   mEmbellishData.leftSpace = 0;
00087   mEmbellishData.rightSpace = 0;
00088 
00089   mPresentationData.flags = 0;
00090   mPresentationData.baseFrame = nsnull;
00091   mPresentationData.mstyle = nsnull;
00092   mPresentationData.scriptLevel = 0;
00093 
00094   // by default, just inherit the display & scriptlevel of our parent
00095   nsPresentationData parentData;
00096   GetPresentationDataFrom(aParent, parentData);
00097   mPresentationData.mstyle = parentData.mstyle;
00098   mPresentationData.scriptLevel = parentData.scriptLevel;
00099   if (NS_MATHML_IS_DISPLAYSTYLE(parentData.flags)) {
00100     mPresentationData.flags |= NS_MATHML_DISPLAYSTYLE;
00101   }
00102 
00103 #if defined(NS_DEBUG) && defined(SHOW_BOUNDING_BOX)
00104   mPresentationData.flags |= NS_MATHML_SHOW_BOUNDING_METRICS;
00105 #endif
00106 
00107   return NS_OK;
00108 }
00109 
00110 NS_IMETHODIMP
00111 nsMathMLFrame::UpdatePresentationData(PRInt32         aScriptLevelIncrement,
00112                                       PRUint32        aFlagsValues,
00113                                       PRUint32        aFlagsToUpdate)
00114 {
00115   mPresentationData.scriptLevel += aScriptLevelIncrement;
00116   // update flags that are relevant to this call
00117   if (NS_MATHML_IS_DISPLAYSTYLE(aFlagsToUpdate)) {
00118     // updating the displaystyle flag is allowed
00119     if (NS_MATHML_IS_DISPLAYSTYLE(aFlagsValues)) {
00120       mPresentationData.flags |= NS_MATHML_DISPLAYSTYLE;
00121     }
00122     else {
00123       mPresentationData.flags &= ~NS_MATHML_DISPLAYSTYLE;
00124     }
00125   }
00126   if (NS_MATHML_IS_COMPRESSED(aFlagsToUpdate)) {
00127     // updating the compression flag is allowed
00128     if (NS_MATHML_IS_COMPRESSED(aFlagsValues)) {
00129       // 'compressed' means 'prime' style in App. G, TeXbook
00130       mPresentationData.flags |= NS_MATHML_COMPRESSED;
00131     }
00132     // no else. the flag is sticky. it retains its value once it is set
00133   }
00134   return NS_OK;
00135 }
00136 
00137 // Helper to give a style context suitable for doing the stretching of
00138 // a MathMLChar. Frame classes that use this should ensure that the 
00139 // extra leaf style contexts given to the MathMLChars are acessible to
00140 // the Style System via the Get/Set AdditionalStyleContext() APIs.
00141 /* static */ void
00142 nsMathMLFrame::ResolveMathMLCharStyle(nsPresContext*  aPresContext,
00143                                       nsIContent*      aContent,
00144                                       nsStyleContext*  aParentStyleContext,
00145                                       nsMathMLChar*    aMathMLChar,
00146                                       PRBool           aIsMutableChar)
00147 {
00148   nsIAtom* pseudoStyle = (aIsMutableChar) ?
00149     nsCSSAnonBoxes::mozMathStretchy :
00150     nsCSSAnonBoxes::mozMathAnonymous; // savings
00151   nsRefPtr<nsStyleContext> newStyleContext;
00152   newStyleContext = aPresContext->StyleSet()->
00153     ResolvePseudoStyleFor(aContent, pseudoStyle, aParentStyleContext);
00154 
00155   if (newStyleContext)
00156     aMathMLChar->SetStyleContext(newStyleContext);
00157 }
00158 
00159 /* static */ void
00160 nsMathMLFrame::GetEmbellishDataFrom(nsIFrame*        aFrame,
00161                                     nsEmbellishData& aEmbellishData)
00162 {
00163   // initialize OUT params
00164   aEmbellishData.flags = 0;
00165   aEmbellishData.coreFrame = nsnull;
00166   aEmbellishData.direction = NS_STRETCH_DIRECTION_UNSUPPORTED;
00167   aEmbellishData.leftSpace = 0;
00168   aEmbellishData.rightSpace = 0;
00169 
00170   if (aFrame && aFrame->IsFrameOfType(nsIFrame::eMathML)) {
00171     nsIMathMLFrame* mathMLFrame;
00172     CallQueryInterface(aFrame, &mathMLFrame);
00173     if (mathMLFrame) {
00174       mathMLFrame->GetEmbellishData(aEmbellishData);
00175     }
00176   }
00177 }
00178 
00179 // helper to get the presentation data of a frame, by possibly walking up
00180 // the frame hierarchy if we happen to be surrounded by non-MathML frames.
00181 /* static */ void
00182 nsMathMLFrame::GetPresentationDataFrom(nsIFrame*           aFrame,
00183                                        nsPresentationData& aPresentationData,
00184                                        PRBool              aClimbTree)
00185 {
00186   // initialize OUT params
00187   aPresentationData.flags = 0;
00188   aPresentationData.baseFrame = nsnull;
00189   aPresentationData.mstyle = nsnull;
00190   aPresentationData.scriptLevel = 0;
00191 
00192   nsIFrame* frame = aFrame;
00193   while (frame) {
00194     if (frame->IsFrameOfType(nsIFrame::eMathML)) {
00195       nsIMathMLFrame* mathMLFrame;
00196       CallQueryInterface(frame, &mathMLFrame);
00197       if (mathMLFrame) {
00198         mathMLFrame->GetPresentationData(aPresentationData);
00199         break;
00200       }
00201     }
00202     // stop if the caller doesn't want to lookup beyond the frame
00203     if (!aClimbTree) {
00204       break;
00205     }
00206     // stop if we reach the root <math> tag
00207     nsIContent* content = frame->GetContent();
00208     NS_ASSERTION(content, "dangling frame without a content node");
00209     if (!content)
00210       break;
00211 
00212     if (content->Tag() == nsMathMLAtoms::math) {
00213       const nsStyleDisplay* display = frame->GetStyleDisplay();
00214       if (display->mDisplay == NS_STYLE_DISPLAY_BLOCK) {
00215         aPresentationData.flags |= NS_MATHML_DISPLAYSTYLE;
00216       }
00217       break;
00218     }
00219     frame = frame->GetParent();
00220   }
00221   NS_ASSERTION(frame, "bad MathML markup - could not find the top <math> element");
00222 }
00223 
00224 // helper to get an attribute from the content or the surrounding <mstyle> hierarchy
00225 /* static */ nsresult
00226 nsMathMLFrame::GetAttribute(nsIContent* aContent,
00227                             nsIFrame*   aMathMLmstyleFrame,
00228                             nsIAtom*    aAttributeAtom,
00229                             nsString&   aValue)
00230 {
00231   nsresult rv = NS_CONTENT_ATTR_NOT_THERE;
00232 
00233   // see if we can get the attribute from the content
00234   if (aContent) {
00235     rv = aContent->GetAttr(kNameSpaceID_None, aAttributeAtom, aValue);
00236   }
00237 
00238   if (NS_CONTENT_ATTR_NOT_THERE == rv) {
00239     // see if we can get the attribute from the mstyle frame
00240     if (aMathMLmstyleFrame) {
00241       nsIFrame* mstyleParent = aMathMLmstyleFrame->GetParent();
00242 
00243       nsPresentationData mstyleParentData;
00244       mstyleParentData.mstyle = nsnull;
00245 
00246       if (mstyleParent) {
00247         nsIMathMLFrame* mathMLFrame;
00248         mstyleParent->QueryInterface(NS_GET_IID(nsIMathMLFrame), (void**)&mathMLFrame);
00249         if (mathMLFrame) {
00250           mathMLFrame->GetPresentationData(mstyleParentData);
00251         }
00252       }
00253 
00254       // recurse all the way up into the <mstyle> hierarchy
00255       rv = GetAttribute(aMathMLmstyleFrame->GetContent(),
00256                      mstyleParentData.mstyle, aAttributeAtom, aValue);
00257     }
00258   }
00259   return rv;
00260 }
00261 
00262 /* static */ void
00263 nsMathMLFrame::GetRuleThickness(nsIRenderingContext& aRenderingContext,
00264                                 nsIFontMetrics*      aFontMetrics,
00265                                 nscoord&             aRuleThickness)
00266 {
00267   // get the bounding metrics of the overbar char, the rendering context
00268   // is assumed to have been set with the font of the current style context
00269 #ifdef NS_DEBUG
00270   nsCOMPtr<nsIFontMetrics> currFontMetrics;
00271   aRenderingContext.GetFontMetrics(*getter_AddRefs(currFontMetrics));
00272   NS_ASSERTION(currFontMetrics->Font().Equals(aFontMetrics->Font()),
00273       "unexpected state");
00274 #endif
00275   nscoord xHeight;
00276   aFontMetrics->GetXHeight(xHeight);
00277   PRUnichar overBar = 0x00AF;
00278   nsBoundingMetrics bm;
00279   nsresult rv = aRenderingContext.GetBoundingMetrics(&overBar, PRUint32(1), bm);
00280   if (NS_SUCCEEDED(rv)) {
00281     aRuleThickness = bm.ascent + bm.descent;
00282   }
00283   if (NS_FAILED(rv) || aRuleThickness <= 0 || aRuleThickness >= xHeight) {
00284     // fall-back to the other version
00285     GetRuleThickness(aFontMetrics, aRuleThickness);
00286   }
00287 
00288 #if 0
00289   nscoord oldRuleThickness;
00290   GetRuleThickness(aFontMetrics, oldRuleThickness);
00291 
00292   PRUnichar sqrt = 0xE063; // a sqrt glyph from TeX's CMEX font
00293   rv = aRenderingContext.GetBoundingMetrics(&sqrt, PRUint32(1), bm);
00294   nscoord sqrtrule = bm.ascent; // according to TeX, the ascent should be the rule
00295 
00296   printf("xheight:%4d rule:%4d oldrule:%4d  sqrtrule:%4d\n",
00297           xHeight, aRuleThickness, oldRuleThickness, sqrtrule);
00298 #endif
00299 }
00300 
00301 /* static */ void
00302 nsMathMLFrame::GetAxisHeight(nsIRenderingContext& aRenderingContext,
00303                              nsIFontMetrics*      aFontMetrics,
00304                              nscoord&             aAxisHeight)
00305 {
00306   // get the bounding metrics of the minus sign, the rendering context
00307   // is assumed to have been set with the font of the current style context
00308 #ifdef NS_DEBUG
00309   nsCOMPtr<nsIFontMetrics> currFontMetrics;
00310   aRenderingContext.GetFontMetrics(*getter_AddRefs(currFontMetrics));
00311   NS_ASSERTION(currFontMetrics->Font().Equals(aFontMetrics->Font()),
00312        "unexpected state");
00313 #endif
00314   nscoord xHeight;
00315   aFontMetrics->GetXHeight(xHeight);
00316   PRUnichar minus = 0x2212; // not '-', but official Unicode minus sign
00317   nsBoundingMetrics bm;
00318   nsresult rv = aRenderingContext.GetBoundingMetrics(&minus, PRUint32(1), bm);
00319   if (NS_SUCCEEDED(rv)) {
00320     aAxisHeight = bm.ascent - (bm.ascent + bm.descent)/2;
00321   }
00322   if (NS_FAILED(rv) || aAxisHeight <= 0 || aAxisHeight >= xHeight) {
00323     // fall-back to the other version
00324     GetAxisHeight(aFontMetrics, aAxisHeight);
00325   }
00326 }
00327 
00328 // ================
00329 // Utilities for parsing and retrieving numeric values
00330 // All returned values are in twips.
00331 
00332 /*
00333 The REC says:
00334   An explicit plus sign ('+') is not allowed as part of a numeric value
00335   except when it is specifically listed in the syntax (as a quoted '+'  or "+"),
00336 
00337   Units allowed
00338   ID  Description
00339   em  ems (font-relative unit traditionally used for horizontal lengths)
00340   ex  exs (font-relative unit traditionally used for vertical lengths)
00341   px  pixels, or pixel size of a "typical computer display"
00342   in  inches (1 inch = 2.54 centimeters)
00343   cm  centimeters
00344   mm  millimeters
00345   pt  points (1 point = 1/72 inch)
00346   pc  picas (1 pica = 12 points)
00347   %   percentage of default value
00348 
00349 Implementation here:
00350   The numeric value is valid only if it is of the form nnn.nnn [h/v-unit]
00351 */
00352 
00353 /* static */ PRBool
00354 nsMathMLFrame::ParseNumericValue(nsString&   aString,
00355                                  nsCSSValue& aCSSValue)
00356 {
00357   aCSSValue.Reset();
00358   aString.CompressWhitespace(); //  aString is not a const in this code...
00359 
00360   PRInt32 stringLength = aString.Length();
00361   if (!stringLength)
00362     return PR_FALSE;
00363 
00364   nsAutoString number, unit;
00365 
00366   // Gather up characters that make up the number
00367   PRBool gotDot = PR_FALSE;
00368   PRUnichar c;
00369   for (PRInt32 i = 0; i < stringLength; i++) {
00370     c = aString[i];
00371     if (gotDot && c == '.')
00372       return PR_FALSE;  // two dots encountered
00373     else if (c == '.')
00374       gotDot = PR_TRUE;
00375     else if (!nsCRT::IsAsciiDigit(c)) {
00376       aString.Right(unit, stringLength - i);
00377       unit.CompressWhitespace(); // some authors leave blanks before the unit
00378       break;
00379     }
00380     number.Append(c);
00381   }
00382 
00383   // on exit, also return a nicer string version of the value in case
00384   // the caller wants it (e.g., this removes whitespace before units)
00385   aString.Assign(number);
00386   aString.Append(unit);
00387 
00388   // Convert number to floating point
00389   PRInt32 errorCode;
00390   float floatValue = number.ToFloat(&errorCode);
00391   if (errorCode)
00392     return PR_FALSE;
00393 
00394   nsCSSUnit cssUnit;
00395   if (unit.IsEmpty()) {
00396     cssUnit = eCSSUnit_Number; // no explicit unit, this is a number that will act as a multiplier
00397   }
00398   else if (unit.EqualsLiteral("%")) {
00399     aCSSValue.SetPercentValue(floatValue / 100.0f);
00400     return PR_TRUE;
00401   }
00402   else if (unit.EqualsLiteral("em")) cssUnit = eCSSUnit_EM;
00403   else if (unit.EqualsLiteral("ex")) cssUnit = eCSSUnit_XHeight;
00404   else if (unit.EqualsLiteral("px")) cssUnit = eCSSUnit_Pixel;
00405   else if (unit.EqualsLiteral("in")) cssUnit = eCSSUnit_Inch;
00406   else if (unit.EqualsLiteral("cm")) cssUnit = eCSSUnit_Centimeter;
00407   else if (unit.EqualsLiteral("mm")) cssUnit = eCSSUnit_Millimeter;
00408   else if (unit.EqualsLiteral("pt")) cssUnit = eCSSUnit_Point;
00409   else if (unit.EqualsLiteral("pc")) cssUnit = eCSSUnit_Pica;
00410   else // unexpected unit
00411     return PR_FALSE;
00412 
00413   aCSSValue.SetFloatValue(floatValue, cssUnit);
00414   return PR_TRUE;
00415 }
00416 
00417 /* static */ nscoord
00418 nsMathMLFrame::CalcLength(nsPresContext*   aPresContext,
00419                           nsStyleContext*   aStyleContext,
00420                           const nsCSSValue& aCSSValue)
00421 {
00422   NS_ASSERTION(aCSSValue.IsLengthUnit(), "not a length unit");
00423 
00424   if (aCSSValue.IsFixedLengthUnit()) {
00425     return aCSSValue.GetLengthTwips();
00426   }
00427 
00428   nsCSSUnit unit = aCSSValue.GetUnit();
00429 
00430   if (eCSSUnit_Pixel == unit) {
00431     return NSFloatPixelsToTwips(aCSSValue.GetFloatValue(),
00432                                 aPresContext->ScaledPixelsToTwips());
00433   }
00434   else if (eCSSUnit_EM == unit) {
00435     const nsStyleFont* font = aStyleContext->GetStyleFont();
00436     return NSToCoordRound(aCSSValue.GetFloatValue() * (float)font->mFont.size);
00437   }
00438   else if (eCSSUnit_XHeight == unit) {
00439     nscoord xHeight;
00440     const nsStyleFont* font = aStyleContext->GetStyleFont();
00441     nsCOMPtr<nsIFontMetrics> fm = aPresContext->GetMetricsFor(font->mFont);
00442     fm->GetXHeight(xHeight);
00443     return NSToCoordRound(aCSSValue.GetFloatValue() * (float)xHeight);
00444   }
00445 
00446   return 0;
00447 }
00448 
00449 /* static */ PRBool
00450 nsMathMLFrame::ParseNamedSpaceValue(nsIFrame*   aMathMLmstyleFrame,
00451                                     nsString&   aString,
00452                                     nsCSSValue& aCSSValue)
00453 {
00454   aCSSValue.Reset();
00455   aString.CompressWhitespace(); //  aString is not a const in this code...
00456   if (!aString.Length()) return PR_FALSE;
00457 
00458   // See if it is one of the 'namedspace' (ranging 1/18em...7/18em)
00459   PRInt32 i = 0;
00460   nsIAtom* namedspaceAtom = nsnull;
00461   if (aString.EqualsLiteral("veryverythinmathspace")) {
00462     i = 1;
00463     namedspaceAtom = nsMathMLAtoms::veryverythinmathspace_;
00464   }
00465   else if (aString.EqualsLiteral("verythinmathspace")) {
00466     i = 2;
00467     namedspaceAtom = nsMathMLAtoms::verythinmathspace_;
00468   }
00469   else if (aString.EqualsLiteral("thinmathspace")) {
00470     i = 3;
00471     namedspaceAtom = nsMathMLAtoms::thinmathspace_;
00472   }
00473   else if (aString.EqualsLiteral("mediummathspace")) {
00474     i = 4;
00475     namedspaceAtom = nsMathMLAtoms::mediummathspace_;
00476   }
00477   else if (aString.EqualsLiteral("thickmathspace")) {
00478     i = 5;
00479     namedspaceAtom = nsMathMLAtoms::thickmathspace_;
00480   }
00481   else if (aString.EqualsLiteral("verythickmathspace")) {
00482     i = 6;
00483     namedspaceAtom = nsMathMLAtoms::verythickmathspace_;
00484   }
00485   else if (aString.EqualsLiteral("veryverythickmathspace")) {
00486     i = 7;
00487     namedspaceAtom = nsMathMLAtoms::veryverythickmathspace_;
00488   }
00489 
00490   if (0 != i) {
00491     if (aMathMLmstyleFrame) {
00492       // see if there is a <mstyle> that has overriden the default value
00493       // GetAttribute() will recurse all the way up into the <mstyle> hierarchy
00494       nsAutoString value;
00495       if (NS_CONTENT_ATTR_HAS_VALUE ==
00496           GetAttribute(nsnull, aMathMLmstyleFrame, namedspaceAtom, value)) {
00497         if (ParseNumericValue(value, aCSSValue) &&
00498             aCSSValue.IsLengthUnit()) {
00499           return PR_TRUE;
00500         }
00501       }
00502     }
00503 
00504     // fall back to the default value
00505     aCSSValue.SetFloatValue(float(i)/float(18), eCSSUnit_EM);
00506     return PR_TRUE;
00507   }
00508 
00509   return PR_FALSE;
00510 }
00511 
00512 // ================
00513 // Utils to map attributes into CSS rules (work-around to bug 69409 which
00514 // is not scheduled to be fixed anytime soon)
00515 //
00516 
00517 static const PRInt32 kMathMLversion1 = 1;
00518 static const PRInt32 kMathMLversion2 = 2;
00519 
00520 struct
00521 nsCSSMapping {
00522   PRInt32        compatibility;
00523   const nsIAtom* attrAtom;
00524   const char*    cssProperty;
00525 };
00526 
00527 static void
00528 GetMathMLAttributeStyleSheet(nsPresContext* aPresContext,
00529                              nsIStyleSheet** aSheet)
00530 {
00531   static const char kTitle[] = "Internal MathML/CSS Attribute Style Sheet";
00532   *aSheet = nsnull;
00533 
00534   // first, look if the attribute stylesheet is already there
00535   nsStyleSet *styleSet = aPresContext->StyleSet();
00536   NS_ASSERTION(styleSet, "no style set");
00537 
00538   nsAutoString title;
00539   for (PRInt32 i = styleSet->SheetCount(nsStyleSet::eAgentSheet) - 1;
00540        i >= 0; --i) {
00541     nsIStyleSheet *sheet = styleSet->StyleSheetAt(nsStyleSet::eAgentSheet, i);
00542     nsCOMPtr<nsICSSStyleSheet> cssSheet(do_QueryInterface(sheet));
00543     if (cssSheet) {
00544       cssSheet->GetTitle(title);
00545       if (title.Equals(NS_ConvertASCIItoUCS2(kTitle))) {
00546         *aSheet = sheet;
00547         NS_IF_ADDREF(*aSheet);
00548         return;
00549       }
00550     }
00551   }
00552 
00553   // then, create a new one if it isn't yet there
00554   nsCOMPtr<nsIURI> uri;
00555   NS_NewURI(getter_AddRefs(uri), "about:internal-mathml-attribute-stylesheet");
00556   if (!uri)
00557     return;
00558   nsCOMPtr<nsICSSStyleSheet_MOZILLA_1_8_BRANCH> cssSheet =
00559     do_CreateInstance(kCSSStyleSheetCID);
00560   if (!cssSheet)
00561     return;
00562   cssSheet->SetURIs18(uri, uri, uri);
00563   cssSheet->SetTitle(NS_ConvertASCIItoUCS2(kTitle));
00564   // all done, no further activity from the net involved, so we better do this
00565   cssSheet->SetComplete();
00566 
00567   nsCOMPtr<nsIDOMCSSStyleSheet> domSheet(do_QueryInterface(cssSheet));
00568   if (domSheet) {
00569     PRUint32 index;
00570     domSheet->InsertRule(NS_LITERAL_STRING("@namespace url(http://www.w3.org/1998/Math/MathML);"),
00571                                            0, &index);
00572   }
00573 
00574   // insert the stylesheet into the styleset without notifying observers
00575   // XXX Should this be at a different level?
00576   styleSet->PrependStyleSheet(nsStyleSet::eAgentSheet, cssSheet);
00577   *aSheet = cssSheet;
00578   NS_ADDREF(*aSheet);
00579 }
00580 
00581 /* static */ PRInt32
00582 nsMathMLFrame::MapAttributesIntoCSS(nsPresContext* aPresContext,
00583                                     nsIContent*     aContent)
00584 {
00585   // normal case, quick return if there are no attributes
00586   NS_ASSERTION(aContent, "null arg");
00587   PRUint32 attrCount = 0;
00588   if (aContent)
00589     attrCount = aContent->GetAttrCount();
00590   if (!attrCount)
00591     return 0;
00592 
00593   // need to initialize here -- i.e., after registering nsMathMLAtoms
00594   static const nsCSSMapping
00595   kCSSMappingTable[] = {
00596     {kMathMLversion2, nsMathMLAtoms::mathcolor_,      "color:"},
00597     {kMathMLversion1, nsMathMLAtoms::color_,          "color:"},
00598     {kMathMLversion2, nsMathMLAtoms::mathsize_,       "font-size:"},
00599     {kMathMLversion1, nsMathMLAtoms::fontsize_,       "font-size:"},
00600     {kMathMLversion1, nsMathMLAtoms::fontfamily_,     "font-family:"},
00601     {kMathMLversion2, nsMathMLAtoms::mathbackground_, "background-color:"},
00602     {kMathMLversion1, nsMathMLAtoms::background_,     "background-color:"},
00603     {0, nsnull, nsnull}
00604   };
00605 
00606   nsCOMPtr<nsIDocument> doc;
00607   nsCOMPtr<nsIStyleSheet> sheet;
00608   nsCOMPtr<nsICSSStyleSheet> cssSheet;
00609   nsCOMPtr<nsIDOMCSSStyleSheet> domSheet;
00610 
00611   PRInt32 nameSpaceID;
00612   nsCOMPtr<nsIAtom> prefix;
00613   nsCOMPtr<nsIAtom> attrAtom;
00614   PRInt32 ruleCount = 0;
00615   for (PRUint32 i = 0; i < attrCount; ++i) {
00616     aContent->GetAttrNameAt(i, &nameSpaceID,
00617                             getter_AddRefs(attrAtom),
00618                             getter_AddRefs(prefix));
00619 
00620     // lookup the equivalent CSS property
00621     const nsCSSMapping* map = kCSSMappingTable;
00622     while (map->attrAtom && map->attrAtom != attrAtom)
00623       ++map;
00624     if (!map->attrAtom)
00625       continue;
00626     nsAutoString cssProperty(NS_ConvertASCIItoUCS2(map->cssProperty));
00627 
00628     nsAutoString attrValue;
00629     aContent->GetAttr(nameSpaceID, attrAtom, attrValue);
00630     if (attrValue.IsEmpty())
00631       continue;
00632     nsAutoString escapedAttrValue;
00633     nsStyleUtil::EscapeCSSString(attrValue, escapedAttrValue);
00634 
00635     // don't add rules that are already in mathml.css
00636     // (this will also clean up whitespace before units - see bug 125303)
00637     if (attrAtom == nsMathMLAtoms::fontsize_ || attrAtom == nsMathMLAtoms::mathsize_) {
00638       nsCSSValue cssValue;
00639       nsAutoString numericValue(attrValue);
00640       if (!ParseNumericValue(numericValue, cssValue))
00641         continue;
00642       // on exit, ParseNumericValue also returns a nicer string
00643       // in which the whitespace before the unit is cleaned up 
00644       cssProperty.Append(numericValue);
00645     }
00646     else
00647       cssProperty.Append(attrValue);
00648 
00649     nsAutoString attrName;
00650     attrAtom->ToString(attrName);
00651 
00652     // make a style rule that maps to the equivalent CSS property
00653     nsAutoString cssRule;
00654     cssRule.Assign(NS_LITERAL_STRING("[")  + attrName +
00655                    NS_LITERAL_STRING("='") + escapedAttrValue +
00656                    NS_LITERAL_STRING("']{") + cssProperty + NS_LITERAL_STRING("}"));
00657 
00658     if (!sheet) {
00659       // first time... we do this to defer the lookup up to the
00660       // point where we encounter attributes that actually matter
00661       doc = aContent->GetDocument();
00662       if (!doc) 
00663         return 0;
00664       GetMathMLAttributeStyleSheet(aPresContext, getter_AddRefs(sheet));
00665       if (!sheet)
00666         return 0;
00667       // by construction, these cannot be null at this point
00668       cssSheet = do_QueryInterface(sheet);
00669       domSheet = do_QueryInterface(sheet);
00670       NS_ASSERTION(cssSheet && domSheet, "unexpected null pointers");
00671       // we will keep the sheet orphan as we populate it. This way,
00672       // observers of the document won't be notified and we avoid any troubles
00673       // that may come from reconstructing the frame tree. Our rules only need
00674       // a re-resolve of style data and a reflow, not a reconstruct-all...
00675       sheet->SetOwningDocument(nsnull);
00676     }
00677 
00678     // check for duplicate, if a similar rule is already there, don't bother to add another one
00679     nsAutoString selector;
00680     selector.Assign(NS_LITERAL_STRING("*[") + attrName +
00681                     NS_LITERAL_STRING("=\"") + escapedAttrValue +
00682                     NS_LITERAL_STRING("\"]"));
00683     PRInt32 k, count;
00684     cssSheet->StyleRuleCount(count);
00685     for (k = 0; k < count; ++k) {
00686       nsAutoString tmpSelector;
00687       nsCOMPtr<nsICSSRule> tmpRule;
00688       cssSheet->GetStyleRuleAt(k, *getter_AddRefs(tmpRule));
00689       nsCOMPtr<nsICSSStyleRule> tmpStyleRule = do_QueryInterface(tmpRule);
00690       if (tmpStyleRule) {
00691         tmpStyleRule->GetSelectorText(tmpSelector);
00692         if (tmpSelector.Equals(selector)) {
00693           k = -1;
00694           break;
00695         }
00696       }
00697     }
00698     if (k >= 0) {
00699       // insert the rule (note: when the sheet already has @namespace and
00700       // friends, insert after them, e.g., at the end, otherwise it won't work)
00701       // For MathML 2, insert at the end to give it precedence
00702       PRInt32 pos = (map->compatibility == kMathMLversion2) ? count : 1;
00703       PRUint32 index;
00704       domSheet->InsertRule(cssRule, pos, &index);
00705       ++ruleCount;
00706     }
00707   }
00708   // restore the sheet to its owner
00709   if (sheet) {
00710     sheet->SetOwningDocument(doc);
00711   }
00712 
00713   return ruleCount;
00714 }
00715 
00716 /* static */ PRInt32
00717 nsMathMLFrame::MapAttributesIntoCSS(nsPresContext* aPresContext,
00718                                     nsIFrame*       aFrame)
00719 {
00720   PRInt32 ruleCount = MapAttributesIntoCSS(aPresContext, aFrame->GetContent());
00721   if (!ruleCount)
00722     return 0;
00723 
00724   // now, re-resolve the style contexts in our subtree
00725   nsFrameManager *fm = aPresContext->FrameManager();
00726   nsStyleChangeList changeList;
00727   fm->ComputeStyleChangeFor(aFrame, &changeList, NS_STYLE_HINT_NONE);
00728 #ifdef DEBUG
00729   // Use the parent frame to make sure we catch in-flows and such
00730   nsIFrame* parentFrame = aFrame->GetParent();
00731   fm->DebugVerifyStyleTree(parentFrame ? parentFrame : aFrame);
00732 #endif
00733 
00734   return ruleCount;
00735 }