Back to index

lightning-sunbird  0.9+nobinonly
mimetpfl.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C; tab-width: 4; 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  *   Ben Bucksch <mozilla@bucksch.org>
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 "mimetpfl.h"
00040 #include "mimebuf.h"
00041 #include "prmem.h"
00042 #include "plstr.h"
00043 #include "mozITXTToHTMLConv.h"
00044 #include "nsString.h"
00045 #include "nsReadableUtils.h"
00046 #include "nsMimeStringResources.h"
00047 #include "nsIPrefBranch.h"
00048 #include "nsIServiceManager.h"
00049 #include "mimemoz2.h"
00050 #include "prprf.h"
00051 #include "nsMsgI18N.h"
00052 
00053 static const PRUint32 kSpacesForATab = 4; // Must be at least 1.
00054 static const PRUint32 kInitialBufferSize = 100;
00055 
00056 #define MIME_SUPERCLASS mimeInlineTextClass
00057 MimeDefClass(MimeInlineTextPlainFlowed, MimeInlineTextPlainFlowedClass,
00058                       mimeInlineTextPlainFlowedClass, &MIME_SUPERCLASS);
00059 
00060 static int MimeInlineTextPlainFlowed_parse_begin (MimeObject *);
00061 static int MimeInlineTextPlainFlowed_parse_line (char *, PRInt32, MimeObject *);
00062 static int MimeInlineTextPlainFlowed_parse_eof (MimeObject *, PRBool);
00063 
00064 static MimeInlineTextPlainFlowedExData *MimeInlineTextPlainFlowedExDataList = nsnull;
00065 
00066 // From mimetpla.cpp
00067 extern "C" void MimeTextBuildPrefixCSS(
00068                        PRInt32 quotedSizeSetting,      // mail.quoted_size
00069                        PRInt32    quotedStyleSetting,  // mail.quoted_style
00070                        char       *citationColor,      // mail.citation_color
00071                        nsACString &style);
00072 // Definition below
00073 static
00074 nsresult Line_convert_whitespace(const nsAFlatString& a_line,
00075                                  const PRBool a_convert_all_whitespace,
00076                                  nsAFlatString& a_out_line);
00077 
00078 static int
00079 MimeInlineTextPlainFlowedClassInitialize(MimeInlineTextPlainFlowedClass *clazz)
00080 {
00081   MimeObjectClass *oclass = (MimeObjectClass *) clazz;
00082   NS_ASSERTION(!oclass->class_initialized, "class not initialized");
00083   oclass->parse_begin = MimeInlineTextPlainFlowed_parse_begin;
00084   oclass->parse_line  = MimeInlineTextPlainFlowed_parse_line;
00085   oclass->parse_eof   = MimeInlineTextPlainFlowed_parse_eof;
00086   
00087   return 0;
00088 }
00089 
00090 static int
00091 MimeInlineTextPlainFlowed_parse_begin (MimeObject *obj)
00092 {
00093   int status = ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_begin(obj);
00094   if (status < 0) return status;
00095 
00096   status =  MimeObject_write(obj, "", 0, PR_TRUE); /* force out any separators... */
00097   if(status<0) return status;
00098 
00099   PRBool quoting = ( obj->options
00100     && ( obj->options->format_out == nsMimeOutput::nsMimeMessageQuoting ||
00101          obj->options->format_out == nsMimeOutput::nsMimeMessageBodyQuoting
00102        )       );  // The output will be inserted in the composer as quotation
00103   PRBool plainHTML = quoting || (obj->options &&
00104        obj->options->format_out == nsMimeOutput::nsMimeMessageSaveAs);
00105        // Just good(tm) HTML. No reliance on CSS.
00106 
00107   // Setup the data structure that is connected to the actual document
00108   // Saved in a linked list in case this is called with several documents
00109   // at the same time.
00110   /* This memory is freed when parse_eof is called. So it better be! */
00111   struct MimeInlineTextPlainFlowedExData *exdata =
00112     (MimeInlineTextPlainFlowedExData *)PR_MALLOC(sizeof(struct MimeInlineTextPlainFlowedExData));
00113   if(!exdata) return MIME_OUT_OF_MEMORY;
00114 
00115   MimeInlineTextPlainFlowed *text = (MimeInlineTextPlainFlowed *) obj;
00116 
00117   // Link it up.
00118   exdata->next = MimeInlineTextPlainFlowedExDataList;
00119   MimeInlineTextPlainFlowedExDataList = exdata;
00120 
00121   // Initialize data
00122 
00123   exdata->ownerobj = obj;
00124   exdata->inflow = PR_FALSE;
00125   exdata->quotelevel = 0;
00126   exdata->isSig = PR_FALSE;
00127 
00128   // check for DelSp=yes (RFC 3676)
00129 
00130   char *content_type_row =
00131     (obj->headers
00132      ? MimeHeaders_get(obj->headers, HEADER_CONTENT_TYPE, PR_FALSE, PR_FALSE)
00133      : 0);
00134   char *content_type_delsp =
00135     (content_type_row
00136      ? MimeHeaders_get_parameter(content_type_row, "delsp", NULL,NULL)
00137      : 0);
00138   ((MimeInlineTextPlainFlowed *)obj)->delSp = content_type_delsp && !nsCRT::strcasecmp(content_type_delsp, "yes");
00139   PR_Free(content_type_delsp);
00140   PR_Free(content_type_row);
00141 
00142   // Get Prefs for viewing
00143 
00144   exdata->fixedwidthfont = PR_FALSE;
00145   //  Quotes
00146   text->mQuotedSizeSetting = 0;   // mail.quoted_size
00147   text->mQuotedStyleSetting = 0;  // mail.quoted_style
00148   text->mCitationColor = nsnull;  // mail.citation_color
00149 
00150   nsIPrefBranch *prefBranch = GetPrefBranch(obj->options);
00151   if (prefBranch)
00152   {
00153     prefBranch->GetIntPref("mail.quoted_size", &(text->mQuotedSizeSetting));
00154     prefBranch->GetIntPref("mail.quoted_style", &(text->mQuotedStyleSetting));
00155     prefBranch->GetCharPref("mail.citation_color", &(text->mCitationColor));
00156     nsresult rv = prefBranch->GetBoolPref("mail.fixed_width_messages",
00157                                           &(exdata->fixedwidthfont));
00158     NS_ASSERTION(NS_SUCCEEDED(rv), "failed to get pref");
00159          // Check at least the success of one
00160   }
00161 
00162   // Get font
00163   // only used for viewing (!plainHTML)
00164   nsCAutoString fontstyle;
00165   nsCAutoString fontLang;     // langgroup of the font
00166 
00167 
00168   // generic font-family name ( -moz-fixed for fixed font and NULL for
00169   // variable font ) is sufficient now that bug 105199 has been fixed.
00170 
00171   if (exdata->fixedwidthfont)
00172     fontstyle = "font-family: -moz-fixed";
00173 
00174   if (nsMimeOutput::nsMimeMessageBodyDisplay == obj->options->format_out ||
00175       nsMimeOutput::nsMimeMessagePrintOutput == obj->options->format_out)
00176   {
00177     PRInt32 fontSize;       // default font size
00178     PRInt32 fontSizePercentage;   // size percentage
00179     nsresult rv = GetMailNewsFont(obj, exdata->fixedwidthfont,
00180                                   &fontSize, &fontSizePercentage, fontLang);
00181     if (NS_SUCCEEDED(rv))
00182     {
00183       if ( ! fontstyle.IsEmpty() ) {
00184         fontstyle += "; ";
00185       }
00186       fontstyle += "font-size: ";
00187       fontstyle.AppendInt(fontSize);
00188       fontstyle += "px;";
00189     }
00190   }
00191 
00192   // Opening <div>.
00193   if (!quoting)
00194        /* 4.x' editor can't break <div>s (e.g. to interleave comments).
00195           We'll add the class to the <blockquote type=cite> later. */
00196   {
00197     nsCAutoString openingDiv("<div class=\"moz-text-flowed\"");
00198     // We currently have to add formatting here. :-(
00199     if (!plainHTML && !fontstyle.IsEmpty())
00200     {
00201       openingDiv += " style=\"";
00202       openingDiv += fontstyle;
00203       openingDiv += '"';
00204     }
00205     if (!plainHTML && !fontLang.IsEmpty())
00206     {
00207       openingDiv += " lang=\"";
00208       openingDiv += fontLang;
00209       openingDiv += '\"';
00210     }
00211     openingDiv += ">";
00212     status = MimeObject_write(obj, openingDiv.get(), openingDiv.Length(), PR_FALSE);
00213     if (status < 0) return status;
00214   }
00215 
00216   return 0;
00217 }
00218 
00219 static int
00220 MimeInlineTextPlainFlowed_parse_eof (MimeObject *obj, PRBool abort_p)
00221 {
00222   int status = 0;
00223   struct MimeInlineTextPlainFlowedExData *exdata = nsnull;
00224 
00225   PRBool quoting = ( obj->options
00226     && ( obj->options->format_out == nsMimeOutput::nsMimeMessageQuoting ||
00227          obj->options->format_out == nsMimeOutput::nsMimeMessageBodyQuoting
00228        )           );  // see above
00229 
00230   // Has this method already been called for this object?
00231   // In that case return.
00232   if (obj->closed_p) return 0;
00233   
00234   /* Run parent method first, to flush out any buffered data. */
00235   status = ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_eof(obj, abort_p);
00236   if (status < 0) goto EarlyOut;
00237 
00238   // Look up and unlink "our" extended data structure
00239   // We do it in the beginning so that if an error occur, we can
00240   // just free |exdata|.
00241   struct MimeInlineTextPlainFlowedExData **prevexdata;
00242   prevexdata = &MimeInlineTextPlainFlowedExDataList;
00243 
00244   while ((exdata = *prevexdata) != nsnull) {
00245     if (exdata->ownerobj == obj) {
00246       // Fill hole
00247       *prevexdata = exdata->next;
00248       break;
00249     }
00250     prevexdata = &exdata->next;
00251   }
00252   NS_ASSERTION (exdata, "The extra data has disappeared!");
00253 
00254   if (!obj->output_p) {
00255     status = 0;
00256     goto EarlyOut;
00257   }
00258     
00259   for(; exdata->quotelevel > 0; exdata->quotelevel--) {
00260     status = MimeObject_write(obj, "</blockquote>", 13, PR_FALSE);
00261     if(status<0) goto EarlyOut;
00262   }
00263     
00264   if (exdata->isSig && !quoting) {
00265     status = MimeObject_write(obj, "</div>", 6, PR_FALSE); // .moz-txt-sig
00266     if (status<0) goto EarlyOut;
00267   }
00268   if (!quoting) // HACK (see above)
00269   {
00270     status = MimeObject_write(obj, "</div>", 6, PR_FALSE); // .moz-text-flowed
00271     if (status<0) goto EarlyOut;
00272   }
00273     
00274   status = 0;
00275 
00276 EarlyOut:  
00277   PR_Free(exdata);
00278 
00279   // Free mCitationColor
00280   MimeInlineTextPlainFlowed *text = (MimeInlineTextPlainFlowed *) obj;
00281   PR_FREEIF(text->mCitationColor);
00282   text->mCitationColor = nsnull;
00283   
00284   return status;
00285 }
00286 
00287 
00288 static int
00289 MimeInlineTextPlainFlowed_parse_line (char *line, PRInt32 length, MimeObject *obj)
00290 {
00291   int status;
00292   PRBool quoting = ( obj->options
00293     && ( obj->options->format_out == nsMimeOutput::nsMimeMessageQuoting ||
00294          obj->options->format_out == nsMimeOutput::nsMimeMessageBodyQuoting
00295        )           );  // see above
00296   PRBool plainHTML = quoting || (obj->options &&
00297        obj->options->format_out == nsMimeOutput::nsMimeMessageSaveAs);
00298        // see above
00299 
00300   struct MimeInlineTextPlainFlowedExData *exdata;
00301   exdata = MimeInlineTextPlainFlowedExDataList;
00302   while(exdata && (exdata->ownerobj != obj)) {
00303     exdata = exdata->next;
00304   }
00305 
00306   NS_ASSERTION(exdata, "The extra data has disappeared!");
00307 
00308   NS_ASSERTION(length > 0, "zero length");
00309   if (length <= 0) return 0;
00310 
00311   uint32 linequotelevel = 0;
00312   const char *linep = line;
00313   // Space stuffed?
00314   if(' ' == *linep) {
00315     linep++;
00316   } else {
00317     // count '>':s before the first non-'>'
00318     while('>' == *linep) {
00319       linep++;
00320       linequotelevel++;
00321     }
00322     // Space stuffed?
00323     if(' ' == *linep) {
00324       linep++;
00325     }
00326   }
00327 
00328   // Look if the last character (after stripping ending end
00329   // of lines and quoting stuff) is a SPACE. If it is, we are looking at a
00330   // flowed line. Normally we assume that the last two chars
00331   // are CR and LF as said in RFC822, but that doesn't seem to
00332   // be the case always.
00333   PRBool flowed = PR_FALSE;
00334   PRBool sigSeparator = PR_FALSE;
00335   PRInt32 index = length-1;
00336   while(index >= 0 && ('\r' == line[index] || '\n' == line[index])) {
00337     index--;
00338   }
00339   if (index > linep - line && ' ' == line[index])
00340        /* Ignore space stuffing, i.e. lines with just
00341           (quote marks and) a space count as empty */
00342   {
00343     flowed = PR_TRUE;
00344     sigSeparator = (index - (linep - line) + 1 == 3) && !nsCRT::strncmp(linep, "-- ", 3);
00345     if (((MimeInlineTextPlainFlowed *) obj)->delSp && ! sigSeparator)
00346        /* If line is flowed and DelSp=yes, logically
00347           delete trailing space. Line consisting of
00348           dash dash space ("-- "), commonly used as
00349           signature separator, gets special handling
00350           (RFC 3676) */
00351     {
00352       length--;
00353       line[index] = '\0';
00354     }
00355   }
00356 
00357   mozITXTToHTMLConv *conv = GetTextConverter(obj->options);
00358 
00359   PRBool skipConversion = !conv ||
00360                           (obj->options && obj->options->force_user_charset);
00361 
00362   nsAutoString lineSource;
00363   nsXPIDLString lineResult;
00364     
00365   char *mailCharset = NULL;
00366   nsresult rv;
00367 
00368   if (!skipConversion)
00369   {
00370     // Convert only if the source string is not empty
00371     if (length - (linep - line) > 0)
00372     {
00373       PRBool whattodo = obj->options->whattodo;
00374       if (plainHTML)
00375       {
00376         if (quoting)
00377           whattodo = 0;
00378         else
00379           whattodo = whattodo & ~mozITXTToHTMLConv::kGlyphSubstitution;
00380                     /* Do recognition for the case, the result is viewed in
00381                         Mozilla, but not GlyphSubstitution, because other UAs
00382                         might not be able to display the glyphs. */
00383       }
00384       
00385       const nsDependentCSubstring& inputStr = Substring(linep, linep + (length - (linep - line)));
00386 
00387       // For 'SaveAs', |line| is in |mailCharset|.
00388       // convert |line| to UTF-16 before 'html'izing (calling ScanTXT())
00389       if (obj->options->format_out == nsMimeOutput::nsMimeMessageSaveAs)
00390       {
00391         // Get the mail charset of this message.
00392         MimeInlineText  *inlinetext = (MimeInlineText *) obj;
00393         if (!inlinetext->initializeCharset)
00394           ((MimeInlineTextClass*)&mimeInlineTextClass)->initialize_charset(obj);
00395         mailCharset = inlinetext->charset;
00396         if (mailCharset && *mailCharset) {
00397           rv = nsMsgI18NConvertToUnicode(mailCharset, nsPromiseFlatCString(inputStr), lineSource);
00398           NS_ENSURE_SUCCESS(rv, -1);
00399         }
00400         else // this probably never happens...
00401           CopyUTF8toUTF16(inputStr, lineSource);
00402       }
00403       else   // line is in UTF-8
00404         CopyUTF8toUTF16(inputStr, lineSource);
00405 
00406       // This is the main TXT to HTML conversion:
00407       // escaping (very important), eventually recognizing etc.
00408       rv = conv->ScanTXT(lineSource.get(), whattodo, getter_Copies(lineResult));
00409       NS_ENSURE_SUCCESS(rv, -1);
00410     }
00411   }
00412   else
00413   {
00414     CopyUTF8toUTF16(nsDependentCString(line, length), lineResult);
00415     status = NS_OK;
00416   }
00417 
00418   nsCAutoString preface;
00419 
00420   /* Correct number of blockquotes */
00421   int32 quoteleveldiff=linequotelevel - exdata->quotelevel;
00422   if((quoteleveldiff != 0) && flowed && exdata->inflow) {
00423     // From RFC 2646 4.5
00424     // The receiver SHOULD handle this error by using the 'quote-depth-wins' rule,
00425     // which is to ignore the flowed indicator and treat the line as fixed.  That
00426     // is, the change in quote depth ends the paragraph.
00427 
00428     // We get that behaviour by just going on.
00429   }
00430   while(quoteleveldiff>0) {
00431     quoteleveldiff--;
00432     preface += "<blockquote type=cite";
00433     // This is to have us observe the user pref settings for citations
00434     MimeInlineTextPlainFlowed *tObj = (MimeInlineTextPlainFlowed *) obj;
00435 
00436     nsCAutoString style;
00437     MimeTextBuildPrefixCSS(tObj->mQuotedSizeSetting, tObj->mQuotedStyleSetting,
00438                            tObj->mCitationColor, style);
00439     if (!plainHTML && !style.IsEmpty())
00440     {
00441       preface += " style=\"";
00442       preface += style;
00443       preface += '"';
00444     }
00445     preface += '>';
00446   }
00447   while(quoteleveldiff<0) {
00448     quoteleveldiff++;
00449     preface += "</blockquote>";
00450   }
00451   exdata->quotelevel = linequotelevel;
00452 
00453   nsAutoString lineResult2;
00454 
00455   if(flowed) {
00456     // Check RFC 2646 "4.3. Usenet Signature Convention": "-- "+CRLF is
00457     // not a flowed line
00458     if (sigSeparator)
00459     {
00460       if (linequotelevel > 0 || exdata->isSig)
00461       {
00462         preface += "--&nbsp;<br>";
00463       } else {
00464         exdata->isSig = PR_TRUE;
00465         preface += "<div class=\"moz-txt-sig\"><span class=\"moz-txt-tag\">"
00466                    "--&nbsp;<br></span>";
00467       }
00468     } else {
00469       Line_convert_whitespace(lineResult, PR_FALSE /* Allow wraps */,
00470                               lineResult2);
00471     }
00472 
00473     exdata->inflow=PR_TRUE;
00474   } else {
00475     // Fixed paragraph.
00476     Line_convert_whitespace(lineResult,
00477                             !plainHTML && !obj->options->wrap_long_lines_p
00478                               /* If wrap, convert all spaces but the last in
00479                                  a row into nbsp, otherwise all. */,
00480                             lineResult2);
00481     lineResult2.AppendLiteral("<br>");
00482     exdata->inflow = PR_FALSE;
00483   } // End Fixed line
00484 
00485   if (!(exdata->isSig && quoting))
00486   {
00487     status = MimeObject_write(obj, preface.get(), preface.Length(), PR_TRUE);
00488     if (status < 0) return status;
00489     nsCAutoString outString;
00490     if (obj->options->format_out != nsMimeOutput::nsMimeMessageSaveAs ||
00491         !mailCharset || !*mailCharset)
00492       CopyUTF16toUTF8(lineResult2, outString);
00493     else
00494     { // convert back to mailCharset before writing.       
00495       rv = nsMsgI18NConvertFromUnicode(mailCharset, lineResult2, outString);
00496       NS_ENSURE_SUCCESS(rv, -1);
00497     }
00498     status = MimeObject_write(obj, outString.get(), outString.Length(), PR_TRUE);
00499     return status;
00500   }
00501   else
00502     return NS_OK;
00503 }
00504 
00505 
00519 static void Update_in_tag_info(PRBool *a_in_tag, /* IN/OUT */
00520                    PRBool *a_in_quote_in_tag, /* IN/OUT */
00521                    PRUnichar *a_quote_char, /* IN/OUT (pointer to single char) */
00522                    PRUnichar a_current_char) /* IN */
00523 {
00524   if(*a_in_tag) {
00525     // Keep us informed of what's quoted so that we
00526     // don't end the tag too soon. For instance in
00527     // <font face="weird>font<name">
00528     if(*a_in_quote_in_tag) {
00529       // We are in a quote. A quote is ended by the same
00530       // character that started it ('...' or "...")
00531       if(*a_quote_char == a_current_char) {
00532         *a_in_quote_in_tag = PR_FALSE;
00533       }
00534     } else {
00535       // We are not currently in a quote, but we may enter
00536       // one right this minute.
00537       switch(a_current_char) {
00538       case '"':
00539       case '\'':
00540         *a_in_quote_in_tag = PR_TRUE;
00541         *a_quote_char = a_current_char;
00542         break;       
00543       case '>':
00544         // Tag is ended
00545         *a_in_tag = PR_FALSE;
00546         break;
00547       default:
00548         // Do nothing
00549         ;
00550       }
00551     }
00552     return;
00553   }
00554 
00555   // Not in a tag. 
00556   // Check if we are entering a tag by looking for '<'.
00557   // All normal occurances of '<' should have been replaced
00558   // by &lt;
00559   if ('<' == a_current_char) {
00560     *a_in_tag = PR_TRUE;
00561     *a_in_quote_in_tag = PR_FALSE;
00562   }
00563 }
00564 
00565 
00576 static void Convert_whitespace(const PRUnichar a_current_char,
00577                                const PRUnichar a_next_char,
00578                                const PRBool a_convert_all_whitespace,
00579                                nsAFlatString& a_out_string)
00580 {
00581   NS_ASSERTION('\t' == a_current_char || ' ' == a_current_char,
00582                "Convert_whitespace got something else than a whitespace!");
00583 
00584   PRUint32 number_of_nbsp = 0;
00585   PRUint32 number_of_space = 1; // Assume we're going to output one space.
00586 
00587   /* Output the spaces for a tab. All but the last are made into &nbsp;.
00588      The last is treated like a normal space. 
00589   */
00590   if('\t' == a_current_char) {
00591     number_of_nbsp = kSpacesForATab - 1;
00592   }
00593 
00594   if(' ' == a_next_char || '\t' == a_next_char || a_convert_all_whitespace) {
00595     number_of_nbsp += number_of_space;
00596     number_of_space = 0;
00597   }
00598 
00599   while(number_of_nbsp--) {
00600     a_out_string.AppendLiteral("&nbsp;");
00601   }
00602 
00603   while(number_of_space--) {
00604     // a_out_string += ' '; gives error
00605     a_out_string.AppendLiteral(" ");
00606   }
00607 
00608   return;
00609 }
00610 
00619 static
00620 nsresult Line_convert_whitespace(const nsAFlatString& a_line,
00621                                  const PRBool a_convert_all_whitespace,
00622                                  nsAFlatString& a_out_line)
00623 {
00624   PRBool in_tag = PR_FALSE;
00625   PRBool in_quote_in_tag = PR_FALSE;
00626   PRUnichar quote_char;
00627 
00628   for (PRUint32 i = 0; a_line.Length() > i; i++)
00629   {
00630     const PRUnichar ic = a_line[i];  // Cache
00631 
00632     Update_in_tag_info(&in_tag, &in_quote_in_tag, &quote_char, ic);
00633     // We don't touch anything inside a tag.
00634     if (!in_tag) {
00635       if (ic == ' ' || ic == '\t') {
00636         // Convert the whitespace to something appropriate
00637         Convert_whitespace(ic, a_line.Length() > i + 1 ? a_line[i + 1] : '\0',
00638                            a_convert_all_whitespace ||
00639                            !i, // First char on line
00640                            a_out_line);
00641       } else if (ic == '\r') {
00642         // strip CRs
00643       } else {
00644         a_out_line += ic;
00645       } 
00646     } else {
00647       // In tag. Don't change anything
00648       a_out_line += ic;
00649     }
00650   }
00651   return NS_OK;
00652 }