Back to index

lightning-sunbird  0.9+nobinonly
nsStyleUtil.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.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include <math.h>
00039 #include "nsStyleUtil.h"
00040 #include "nsCRT.h"
00041 #include "nsStyleConsts.h"
00042 #include "nsUnitConversion.h"
00043 
00044 #include "nsHTMLAtoms.h"
00045 #include "nsILinkHandler.h"
00046 #include "nsILink.h"
00047 #include "nsIXMLContent.h"
00048 #include "nsIDocument.h"
00049 #include "nsINameSpaceManager.h"
00050 #include "nsIURI.h"
00051 #include "nsNetUtil.h"
00052 #include "nsReadableUtils.h"
00053 #include "nsContentUtils.h"
00054 #include "nsTextFormatter.h"
00055 
00056 // XXX This is here because nsCachedStyleData is accessed outside of
00057 // the content module; e.g., by nsCSSFrameConstructor.
00058 #include "nsRuleNode.h"
00059 
00060 nsCachedStyleData::StyleStructInfo
00061 nsCachedStyleData::gInfo[] = {
00062 
00063 #define STYLE_STRUCT_INHERITED(name, checkdata_cb, ctor_args) \
00064   { offsetof(nsCachedStyleData, mInheritedData), \
00065     offsetof(nsInheritedStyleData, m##name##Data), \
00066     PR_FALSE },
00067 #define STYLE_STRUCT_RESET(name, checkdata_cb, ctor_args) \
00068   { offsetof(nsCachedStyleData, mResetData), \
00069     offsetof(nsResetStyleData, m##name##Data), \
00070     PR_TRUE },
00071 
00072 #include "nsStyleStructList.h"
00073 
00074 #undef STYLE_STRUCT_INHERITED
00075 #undef STYLE_STRUCT_RESET
00076 
00077   { 0, 0, 0 }
00078 };
00079 
00080 #define POSITIVE_SCALE_FACTOR 1.10 /* 10% */
00081 #define NEGATIVE_SCALE_FACTOR .90  /* 10% */
00082 
00083 
00084 //------------------------------------------------------------------------------
00085 //
00086 //------------------------------------------------------------------------------
00087 
00088 /*
00089  * Return the scaling percentage given a font scaler
00090  * Lifted from layutil.c
00091  */
00092 float nsStyleUtil::GetScalingFactor(PRInt32 aScaler)
00093 {
00094   double scale = 1.0;
00095   double mult;
00096   PRInt32 count;
00097 
00098   if(aScaler < 0)   {
00099     count = -aScaler;
00100     mult = NEGATIVE_SCALE_FACTOR;
00101   }
00102   else {
00103     count = aScaler;
00104     mult = POSITIVE_SCALE_FACTOR;
00105   }
00106 
00107   /* use the percentage scaling factor to the power of the pref */
00108   while(count--) {
00109     scale *= mult;
00110   }
00111 
00112   return (float)scale;
00113 }
00114 
00115 
00116 //------------------------------------------------------------------------------
00117 // Font Algorithm Code
00118 //------------------------------------------------------------------------------
00119 
00120 nscoord
00121 nsStyleUtil::CalcFontPointSize(PRInt32 aHTMLSize, PRInt32 aBasePointSize,
00122                                float aScalingFactor, nsPresContext* aPresContext,
00123                                nsFontSizeType aFontSizeType)
00124 {
00125 #define sFontSizeTableMin  9 
00126 #define sFontSizeTableMax 16 
00127 
00128 // This table seems to be the one used by MacIE5. We hope its adoption in Mozilla
00129 // and eventually in WinIE5.5 will help to establish a standard rendering across
00130 // platforms and browsers. For now, it is used only in Strict mode. More can be read
00131 // in the document written by Todd Farhner at:
00132 // http://style.verso.com/font_size_intervals/altintervals.html
00133 //
00134   static PRInt32 sStrictFontSizeTable[sFontSizeTableMax - sFontSizeTableMin + 1][8] =
00135   {
00136       { 9,    9,     9,     9,    11,    14,    18,    27},
00137       { 9,    9,     9,    10,    12,    15,    20,    30},
00138       { 9,    9,    10,    11,    13,    17,    22,    33},
00139       { 9,    9,    10,    12,    14,    18,    24,    36},
00140       { 9,   10,    12,    13,    16,    20,    26,    39},
00141       { 9,   10,    12,    14,    17,    21,    28,    42},
00142       { 9,   10,    13,    15,    18,    23,    30,    45},
00143       { 9,   10,    13,    16,    18,    24,    32,    48}
00144   };
00145 // HTML       1      2      3      4      5      6      7
00146 // CSS  xxs   xs     s      m      l     xl     xxl
00147 //                          |
00148 //                      user pref
00149 //
00150 //------------------------------------------------------------
00151 //
00152 // This table gives us compatibility with WinNav4 for the default fonts only.
00153 // In WinNav4, the default fonts were:
00154 //
00155 //     Times/12pt ==   Times/16px at 96ppi
00156 //   Courier/10pt == Courier/13px at 96ppi
00157 //
00158 // The 2 lines below marked "anchored" have the exact pixel sizes used by
00159 // WinNav4 for Times/12pt and Courier/10pt at 96ppi. As you can see, the
00160 // HTML size 3 (user pref) for those 2 anchored lines is 13px and 16px.
00161 //
00162 // All values other than the anchored values were filled in by hand, never
00163 // going below 9px, and maintaining a "diagonal" relationship. See for
00164 // example the 13s -- they follow a diagonal line through the table.
00165 //
00166   static PRInt32 sQuirksFontSizeTable[sFontSizeTableMax - sFontSizeTableMin + 1][8] =
00167   {
00168       { 9,    9,     9,     9,    11,    14,    18,    28 },
00169       { 9,    9,     9,    10,    12,    15,    20,    31 },
00170       { 9,    9,     9,    11,    13,    17,    22,    34 },
00171       { 9,    9,    10,    12,    14,    18,    24,    37 },
00172       { 9,    9,    10,    13,    16,    20,    26,    40 }, // anchored (13)
00173       { 9,    9,    11,    14,    17,    21,    28,    42 },
00174       { 9,   10,    12,    15,    17,    23,    30,    45 },
00175       { 9,   10,    13,    16,    18,    24,    32,    48 }  // anchored (16)
00176   };
00177 // HTML       1      2      3      4      5      6      7
00178 // CSS  xxs   xs     s      m      l     xl     xxl
00179 //                          |
00180 //                      user pref
00181 
00182 #if 0
00183 //
00184 // These are the exact pixel values used by WinIE5 at 96ppi.
00185 //
00186       { ?,    8,    11,    12,    13,    16,    21,    32 }, // smallest
00187       { ?,    9,    12,    13,    16,    21,    27,    40 }, // smaller
00188       { ?,   10,    13,    16,    18,    24,    32,    48 }, // medium
00189       { ?,   13,    16,    19,    21,    27,    37,    ?? }, // larger
00190       { ?,   16,    19,    21,    24,    32,    43,    ?? }  // largest
00191 //
00192 // HTML       1      2      3      4      5      6      7
00193 // CSS  ?     ?      ?      ?      ?      ?      ?      ?
00194 //
00195 // (CSS not tested yet.)
00196 //
00197 #endif
00198 
00199   static PRInt32 sFontSizeFactors[8] = { 60,75,89,100,120,150,200,300 };
00200 
00201   static PRInt32 sCSSColumns[7]  = {0, 1, 2, 3, 4, 5, 6}; // xxs...xxl
00202   static PRInt32 sHTMLColumns[7] = {1, 2, 3, 4, 5, 6, 7}; // 1...7
00203 
00204   double dFontSize;
00205 
00206   if (aFontSizeType == eFontSize_HTML) {
00207     aHTMLSize--;    // input as 1-7
00208   }
00209 
00210   if (aHTMLSize < 0)
00211     aHTMLSize = 0;
00212   else if (aHTMLSize > 6)
00213     aHTMLSize = 6;
00214 
00215   PRInt32* column;
00216   switch (aFontSizeType)
00217   {
00218     case eFontSize_HTML: column = sHTMLColumns; break;
00219     case eFontSize_CSS:  column = sCSSColumns;  break;
00220   }
00221 
00222   // Make special call specifically for fonts (needed PrintPreview)
00223   PRInt32 fontSize = NSTwipsToIntPixels(aBasePointSize,
00224                                         aPresContext->TwipsToPixelsForFonts());
00225 
00226   if ((fontSize >= sFontSizeTableMin) && (fontSize <= sFontSizeTableMax))
00227   {
00228     float p2t;
00229     p2t = aPresContext->PixelsToTwips();
00230 
00231     PRInt32 row = fontSize - sFontSizeTableMin;
00232 
00233          if (aPresContext->CompatibilityMode() == eCompatibility_NavQuirks) {
00234            dFontSize = NSIntPixelsToTwips(sQuirksFontSizeTable[row][column[aHTMLSize]], p2t);
00235          } else {
00236            dFontSize = NSIntPixelsToTwips(sStrictFontSizeTable[row][column[aHTMLSize]], p2t);
00237          }
00238   }
00239   else
00240   {
00241     PRInt32 factor = sFontSizeFactors[column[aHTMLSize]];
00242     dFontSize = (factor * aBasePointSize) / 100;
00243   }
00244 
00245   dFontSize *= aScalingFactor;
00246 
00247   if (1.0 < dFontSize) {
00248     return (nscoord)dFontSize;
00249   }
00250   return (nscoord)1;
00251 }
00252 
00253 
00254 //------------------------------------------------------------------------------
00255 //
00256 //------------------------------------------------------------------------------
00257 
00258 nscoord nsStyleUtil::FindNextSmallerFontSize(nscoord aFontSize, PRInt32 aBasePointSize, 
00259                                              float aScalingFactor, nsPresContext* aPresContext,
00260                                              nsFontSizeType aFontSizeType)
00261 {
00262   PRInt32 index;
00263   PRInt32 indexMin;
00264   PRInt32 indexMax;
00265   float relativePosition;
00266   nscoord smallerSize;
00267   nscoord indexFontSize = aFontSize; // XXX initialize to quell a spurious gcc3.2 warning
00268   nscoord smallestIndexFontSize;
00269   nscoord largestIndexFontSize;
00270   nscoord smallerIndexFontSize;
00271   nscoord largerIndexFontSize;
00272   float p2t;
00273   nscoord onePx;
00274   
00275   p2t = aPresContext->PixelsToTwips();
00276   onePx = NSToCoordRound(p2t);
00277     
00278        if (aFontSizeType == eFontSize_HTML) {
00279               indexMin = 1;
00280               indexMax = 7;
00281        } else {
00282               indexMin = 0;
00283               indexMax = 6;
00284        }
00285   
00286   smallestIndexFontSize = CalcFontPointSize(indexMin, aBasePointSize, aScalingFactor, aPresContext, aFontSizeType);
00287   largestIndexFontSize = CalcFontPointSize(indexMax, aBasePointSize, aScalingFactor, aPresContext, aFontSizeType); 
00288   if (aFontSize > smallestIndexFontSize) {
00289     if (aFontSize < NSToCoordRound(float(largestIndexFontSize) * 1.5)) { // smaller will be in HTML table
00290       // find largest index smaller than current
00291       for (index = indexMax; index >= indexMin; index--) {
00292         indexFontSize = CalcFontPointSize(index, aBasePointSize, aScalingFactor, aPresContext, aFontSizeType);
00293         if (indexFontSize < aFontSize)
00294           break;
00295       } 
00296       // set up points beyond table for interpolation purposes
00297       if (indexFontSize == smallestIndexFontSize) {
00298         smallerIndexFontSize = indexFontSize - onePx;
00299         largerIndexFontSize = CalcFontPointSize(index+1, aBasePointSize, aScalingFactor, aPresContext, aFontSizeType);
00300       } else if (indexFontSize == largestIndexFontSize) {
00301         smallerIndexFontSize = CalcFontPointSize(index-1, aBasePointSize, aScalingFactor, aPresContext, aFontSizeType);
00302         largerIndexFontSize = NSToCoordRound(float(largestIndexFontSize) * 1.5);
00303       } else {
00304         smallerIndexFontSize = CalcFontPointSize(index-1, aBasePointSize, aScalingFactor, aPresContext, aFontSizeType);
00305         largerIndexFontSize = CalcFontPointSize(index+1, aBasePointSize, aScalingFactor, aPresContext, aFontSizeType);
00306       }
00307       // compute the relative position of the parent size between the two closest indexed sizes
00308       relativePosition = float(aFontSize - indexFontSize) / float(largerIndexFontSize - indexFontSize);            
00309       // set the new size to have the same relative position between the next smallest two indexed sizes
00310       smallerSize = smallerIndexFontSize + NSToCoordRound(relativePosition * (indexFontSize - smallerIndexFontSize));      
00311     }
00312     else {  // larger than HTML table, drop by 33%
00313       smallerSize = NSToCoordRound(float(aFontSize) / 1.5);
00314     }
00315   }
00316   else { // smaller than HTML table, drop by 1px
00317     smallerSize = PR_MAX(aFontSize - onePx, onePx);
00318   }
00319   return smallerSize;
00320 }
00321 
00322 //------------------------------------------------------------------------------
00323 //
00324 //------------------------------------------------------------------------------
00325 
00326 nscoord nsStyleUtil::FindNextLargerFontSize(nscoord aFontSize, PRInt32 aBasePointSize, 
00327                                             float aScalingFactor, nsPresContext* aPresContext,
00328                                             nsFontSizeType aFontSizeType)
00329 {
00330   PRInt32 index;
00331   PRInt32 indexMin;
00332   PRInt32 indexMax;
00333   float relativePosition;
00334   nscoord largerSize;
00335   nscoord indexFontSize = aFontSize; // XXX initialize to quell a spurious gcc3.2 warning
00336   nscoord smallestIndexFontSize;
00337   nscoord largestIndexFontSize;
00338   nscoord smallerIndexFontSize;
00339   nscoord largerIndexFontSize;
00340   float p2t;
00341   nscoord onePx;
00342   
00343   p2t = aPresContext->PixelsToTwips();
00344   onePx = NSToCoordRound(p2t);
00345     
00346        if (aFontSizeType == eFontSize_HTML) {
00347               indexMin = 1;
00348               indexMax = 7;
00349        } else {
00350               indexMin = 0;
00351               indexMax = 6;
00352        }
00353   
00354   smallestIndexFontSize = CalcFontPointSize(indexMin, aBasePointSize, aScalingFactor, aPresContext, aFontSizeType);
00355   largestIndexFontSize = CalcFontPointSize(indexMax, aBasePointSize, aScalingFactor, aPresContext, aFontSizeType); 
00356   if (aFontSize > (smallestIndexFontSize - onePx)) {
00357     if (aFontSize < largestIndexFontSize) { // larger will be in HTML table
00358       // find smallest index larger than current
00359       for (index = indexMin; index <= indexMax; index++) { 
00360         indexFontSize = CalcFontPointSize(index, aBasePointSize, aScalingFactor, aPresContext, aFontSizeType);
00361         if (indexFontSize > aFontSize)
00362           break;
00363       }
00364       // set up points beyond table for interpolation purposes
00365       if (indexFontSize == smallestIndexFontSize) {
00366         smallerIndexFontSize = indexFontSize - onePx;
00367         largerIndexFontSize = CalcFontPointSize(index+1, aBasePointSize, aScalingFactor, aPresContext, aFontSizeType);
00368       } else if (indexFontSize == largestIndexFontSize) {
00369         smallerIndexFontSize = CalcFontPointSize(index-1, aBasePointSize, aScalingFactor, aPresContext, aFontSizeType);
00370         largerIndexFontSize = NSToCoordRound(float(largestIndexFontSize) * 1.5);
00371       } else {
00372         smallerIndexFontSize = CalcFontPointSize(index-1, aBasePointSize, aScalingFactor, aPresContext, aFontSizeType);
00373         largerIndexFontSize = CalcFontPointSize(index+1, aBasePointSize, aScalingFactor, aPresContext, aFontSizeType);
00374       }
00375       // compute the relative position of the parent size between the two closest indexed sizes
00376       relativePosition = float(aFontSize - smallerIndexFontSize) / float(indexFontSize - smallerIndexFontSize);
00377       // set the new size to have the same relative position between the next largest two indexed sizes
00378       largerSize = indexFontSize + NSToCoordRound(relativePosition * (largerIndexFontSize - indexFontSize));      
00379     }
00380     else {  // larger than HTML table, increase by 50%
00381       largerSize = NSToCoordRound(float(aFontSize) * 1.5);
00382     }
00383   }
00384   else { // smaller than HTML table, increase by 1px
00385     float p2t;
00386     p2t = aPresContext->PixelsToTwips();
00387     largerSize = aFontSize + NSToCoordRound(p2t); 
00388   }
00389   return largerSize;
00390 }
00391 
00392 //------------------------------------------------------------------------------
00393 //
00394 //------------------------------------------------------------------------------
00395 
00396 PRInt32 
00397 nsStyleUtil::ConstrainFontWeight(PRInt32 aWeight)
00398 {
00399   aWeight = ((aWeight < 100) ? 100 : ((aWeight > 900) ? 900 : aWeight));
00400   PRInt32 base = ((aWeight / 100) * 100);
00401   PRInt32 step = (aWeight % 100);
00402   PRBool  negativeStep = PRBool(50 < step);
00403   PRInt32 maxStep;
00404   if (negativeStep) {
00405     step = 100 - step;
00406     maxStep = (base / 100);
00407     base += 100;
00408   }
00409   else {
00410     maxStep = ((900 - base) / 100);
00411   }
00412   if (maxStep < step) {
00413     step = maxStep;
00414   }
00415   return (base + ((negativeStep) ? -step : step));
00416 }
00417 
00418 
00419 /*static*/
00420 PRBool nsStyleUtil::IsHTMLLink(nsIContent *aContent, nsIAtom *aTag, nsPresContext *aPresContext, nsLinkState *aState)
00421 {
00422   NS_ASSERTION(aContent && aState, "null arg in IsHTMLLink");
00423 
00424   // check for:
00425   //  - HTML ANCHOR with valid HREF
00426   //  - HTML LINK with valid HREF
00427   //  - HTML AREA with valid HREF
00428 
00429   PRBool result = PR_FALSE;
00430 
00431   if ((aTag == nsHTMLAtoms::a) ||
00432       (aTag == nsHTMLAtoms::link) ||
00433       (aTag == nsHTMLAtoms::area)) {
00434 
00435     nsCOMPtr<nsILink> link( do_QueryInterface(aContent) );
00436     // In XML documents, this can be null.
00437     if (link) {
00438       nsLinkState linkState;
00439       link->GetLinkState(linkState);
00440       if (linkState == eLinkState_Unknown) {
00441         // if it is an anchor, area or link then check the href attribute
00442         // make sure this anchor has a link even if we are not testing state
00443         // if there is no link, then this anchor is not really a linkpseudo.
00444         // bug=23209
00445 
00446         nsCOMPtr<nsIURI> hrefURI;
00447         link->GetHrefURI(getter_AddRefs(hrefURI));
00448 
00449         if (hrefURI) {
00450           nsILinkHandler *linkHandler = aPresContext->GetLinkHandler();
00451           if (linkHandler) {
00452             linkHandler->GetLinkState(hrefURI, linkState);
00453           }
00454           else {
00455             // no link handler?  then all links are unvisited
00456             linkState = eLinkState_Unvisited;
00457           }
00458         } else {
00459           linkState = eLinkState_NotLink;
00460         }
00461         if (linkState != eLinkState_NotLink) {
00462           nsIDocument* doc = aPresContext->GetDocument();
00463           if (doc) {
00464             doc->AddStyleRelevantLink(aContent, hrefURI);
00465           }
00466         }
00467         link->SetLinkState(linkState);
00468       }
00469       if (linkState != eLinkState_NotLink) {
00470         *aState = linkState;
00471         result = PR_TRUE;
00472       }
00473     }
00474   }
00475 
00476   return result;
00477 }
00478 
00479 /*static*/ 
00480 PRBool nsStyleUtil::IsSimpleXlink(nsIContent *aContent, nsPresContext *aPresContext, nsLinkState *aState)
00481 {
00482   // XXX PERF This function will cause serious performance problems on
00483   // pages with lots of XLinks.  We should be caching the visited
00484   // state of the XLinks.  Where???
00485 
00486   NS_ASSERTION(aContent && aState, "invalid call to IsXlink with null content");
00487 
00488   PRBool rv = PR_FALSE;
00489 
00490   if (aContent && aState) {
00491     // first see if we have an XML element
00492     nsCOMPtr<nsIXMLContent> xml(do_QueryInterface(aContent));
00493     if (xml) {
00494       nsCOMPtr<nsIURI> absURI = nsContentUtils::GetXLinkURI(aContent);
00495       if (absURI) {
00496         nsILinkHandler *linkHandler = aPresContext->GetLinkHandler();
00497         if (linkHandler) {
00498           linkHandler->GetLinkState(absURI, *aState);
00499         }
00500         else {
00501           // no link handler?  then all links are unvisited
00502           *aState = eLinkState_Unvisited;
00503         }
00504         nsIDocument* doc = aPresContext->GetDocument();
00505         if (doc) {
00506           doc->AddStyleRelevantLink(aContent, absURI);
00507         }
00508 
00509         rv = PR_TRUE;
00510       }
00511     }
00512   }
00513   return rv;
00514 }
00515 
00516 // Compare two language strings
00517 PRBool nsStyleUtil::DashMatchCompare(const nsAString& aAttributeValue,
00518                                      const nsAString& aSelectorValue,
00519                                      const nsStringComparator& aComparator)
00520 {
00521   PRBool result;
00522   PRUint32 selectorLen = aSelectorValue.Length();
00523   PRUint32 attributeLen = aAttributeValue.Length();
00524   if (selectorLen > attributeLen) {
00525     result = PR_FALSE;
00526   }
00527   else {
00528     nsAString::const_iterator iter;
00529     if (selectorLen != attributeLen &&
00530         *aAttributeValue.BeginReading(iter).advance(selectorLen) !=
00531             PRUnichar('-')) {
00532       // to match, the aAttributeValue must have a dash after the end of
00533       // the aSelectorValue's text (unless the aSelectorValue and the
00534       // aAttributeValue have the same text)
00535       result = PR_FALSE;
00536     }
00537     else {
00538       result = StringBeginsWith(aAttributeValue, aSelectorValue, aComparator);
00539     }
00540   }
00541   return result;
00542 }
00543 
00544 void nsStyleUtil::EscapeCSSString(const nsString& aString, nsAString& aReturn)
00545 {
00546   aReturn.Truncate();
00547 
00548   const nsString::char_type* in = aString.get();
00549   const nsString::char_type* const end = in + aString.Length();
00550   for (; in != end; in++)
00551   {
00552     if (*in < 0x20)
00553     {
00554      // Escape all characters below 0x20 numerically.
00555    
00556      /*
00557       This is the buffer into which snprintf should write. As the hex. value is,
00558       for numbers below 0x20, max. 2 characters long, we don't need more than 5
00559       characters ("\XX "+NUL).
00560      */
00561      PRUnichar buf[5];
00562      nsTextFormatter::snprintf(buf, NS_ARRAY_LENGTH(buf), NS_LITERAL_STRING("\\%hX ").get(), *in);
00563      aReturn.Append(buf);
00564    
00565     } else switch (*in) {
00566       // Special characters which should be escaped: Quotes and backslash
00567       case '\\':
00568       case '\"':
00569       case '\'':
00570        aReturn.Append(PRUnichar('\\'));
00571       // And now, after the eventual escaping character, the actual one.
00572       default:
00573        aReturn.Append(PRUnichar(*in));
00574     }
00575   }
00576 }