Back to index

lightning-sunbird  0.9+nobinonly
nsTXTToHTMLConv.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 the GNU General Public License Version 2 or later (the "GPL"), or
00026  * 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 "nsTXTToHTMLConv.h"
00039 #include "nsNetUtil.h"
00040 #include "nsIStringStream.h"
00041 #include "nsAutoPtr.h"
00042 
00043 #define TOKEN_DELIMITERS NS_LITERAL_STRING("\t\r\n ").get()
00044 
00045 // nsISupports methods
00046 NS_IMPL_THREADSAFE_ISUPPORTS4(nsTXTToHTMLConv,
00047                               nsIStreamConverter,
00048                               nsITXTToHTMLConv,
00049                               nsIRequestObserver,
00050                               nsIStreamListener)
00051 
00052 
00053 // nsIStreamConverter methods
00054 NS_IMETHODIMP
00055 nsTXTToHTMLConv::Convert(nsIInputStream *aFromStream,
00056                          const char *aFromType, const char *aToType,
00057                          nsISupports *aCtxt, nsIInputStream * *_retval)
00058 {
00059     return NS_ERROR_NOT_IMPLEMENTED;
00060 }
00061 
00062 NS_IMETHODIMP
00063 nsTXTToHTMLConv::AsyncConvertData(const char *aFromType,
00064                                   const char *aToType,
00065                                   nsIStreamListener *aListener,
00066                                   nsISupports *aCtxt)
00067 {
00068     NS_ASSERTION(aListener, "null pointer");
00069     mListener = aListener;
00070     return NS_OK;
00071 }
00072 
00073 
00074 // nsIRequestObserver methods
00075 NS_IMETHODIMP
00076 nsTXTToHTMLConv::OnStartRequest(nsIRequest* request, nsISupports *aContext)
00077 {
00078     mBuffer.AssignLiteral("<html>\n<head><title>");
00079     mBuffer.Append(mPageTitle);
00080     mBuffer.AppendLiteral("</title></head>\n<body>\n");
00081     if (mPreFormatHTML) {     // Use <pre> tags
00082         mBuffer.AppendLiteral("<pre>\n");
00083     }
00084 
00085     // Push mBuffer to the listener now, so the initial HTML will not
00086     // be parsed in OnDataAvailable().
00087 
00088     nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
00089     if (channel)
00090         channel->SetContentType(NS_LITERAL_CSTRING("text/html"));
00091     // else, assume there is a channel somewhere that knows what it is doing!
00092 
00093     nsresult rv = mListener->OnStartRequest(request, aContext);
00094     if (NS_FAILED(rv)) return rv;
00095 
00096     // The request may have been canceled, and if that happens, we want to
00097     // suppress calls to OnDataAvailable.
00098     request->GetStatus(&rv);
00099     if (NS_FAILED(rv)) return rv;
00100 
00101     nsCOMPtr<nsIInputStream> inputData;
00102     rv = NS_NewStringInputStream(getter_AddRefs(inputData), mBuffer);
00103     if (NS_FAILED(rv)) return rv;
00104 
00105     rv = mListener->OnDataAvailable(request, aContext,
00106                                     inputData, 0, mBuffer.Length());
00107     if (NS_FAILED(rv)) return rv;
00108     mBuffer.Truncate();
00109     return rv;
00110 }
00111 
00112 NS_IMETHODIMP
00113 nsTXTToHTMLConv::OnStopRequest(nsIRequest* request, nsISupports *aContext,
00114                                nsresult aStatus)
00115 {
00116     nsresult rv = NS_OK;
00117     if (mToken) {
00118         // we still have an outstanding token
00119         NS_ASSERTION(mToken->prepend,
00120                      "Non prepending tokens should be handled in "
00121                      "OnDataAvailable. There should only be a single "
00122                      "prepending token left to be processed.");
00123         (void)CatHTML(0, mBuffer.Length());
00124     }
00125     if (mPreFormatHTML) {
00126         mBuffer.AppendLiteral("</pre>\n");
00127     }
00128     mBuffer.AppendLiteral("\n</body></html>");
00129 
00130     nsCOMPtr<nsIInputStream> inputData;
00131 
00132     rv = NS_NewStringInputStream(getter_AddRefs(inputData), mBuffer);
00133     if (NS_FAILED(rv)) return rv;
00134 
00135     rv = mListener->OnDataAvailable(request, aContext,
00136                                     inputData, 0, mBuffer.Length());
00137     if (NS_FAILED(rv)) return rv;
00138 
00139     return mListener->OnStopRequest(request, aContext, aStatus);
00140 }
00141 
00142 // nsITXTToHTMLConv methods
00143 NS_IMETHODIMP
00144 nsTXTToHTMLConv::SetTitle(const PRUnichar *aTitle)
00145 {
00146     mPageTitle.Assign(aTitle);
00147     return NS_OK;
00148 }
00149 
00150 NS_IMETHODIMP
00151 nsTXTToHTMLConv::PreFormatHTML(PRBool value)
00152 {
00153     mPreFormatHTML = value;
00154     return NS_OK;
00155 }
00156 
00157 // nsIStreamListener method
00158 NS_IMETHODIMP
00159 nsTXTToHTMLConv::OnDataAvailable(nsIRequest* request, nsISupports *aContext,
00160                                  nsIInputStream *aInStream,
00161                                  PRUint32 aOffset, PRUint32 aCount)
00162 {
00163     nsresult rv = NS_OK;
00164     nsString pushBuffer;
00165     PRUint32 amtRead = 0;
00166     nsAutoArrayPtr<char> buffer(new char[aCount+1]);
00167     if (!buffer) return NS_ERROR_OUT_OF_MEMORY;
00168 
00169     do {
00170         PRUint32 read = 0;
00171         // XXX readSegments, to avoid the first copy?
00172         rv = aInStream->Read(buffer, aCount-amtRead, &read);
00173         if (NS_FAILED(rv)) return rv;
00174 
00175         buffer[read] = '\0';
00176         // XXX charsets?? non-latin1 characters?? utf-16??
00177         AppendASCIItoUTF16(buffer, mBuffer);
00178         amtRead += read;
00179 
00180         PRInt32 front = -1, back = -1, tokenLoc = -1, cursor = 0;
00181 
00182         while ( (tokenLoc = FindToken(cursor, &mToken)) > -1) {
00183             if (mToken->prepend) {
00184                 front = mBuffer.RFindCharInSet(TOKEN_DELIMITERS, tokenLoc);
00185                 front++;
00186                 back = mBuffer.FindCharInSet(TOKEN_DELIMITERS, tokenLoc);
00187             } else {
00188                 front = tokenLoc;
00189                 back = front + mToken->token.Length();
00190             }
00191             if (back == -1) {
00192                 // didn't find an ending, buffer up.
00193                 mBuffer.Left(pushBuffer, front);
00194                 cursor = front;
00195                 break;
00196             }
00197             // found the end of the token.
00198             cursor = CatHTML(front, back);
00199         }
00200 
00201         PRInt32 end = mBuffer.RFind(TOKEN_DELIMITERS, mBuffer.Length());
00202         mBuffer.Left(pushBuffer, PR_MAX(cursor, end));
00203         mBuffer.Cut(0, PR_MAX(cursor, end));
00204         cursor = 0;
00205 
00206         if (!pushBuffer.IsEmpty()) {
00207             nsCOMPtr<nsIInputStream> inputData;
00208 
00209             rv = NS_NewStringInputStream(getter_AddRefs(inputData), pushBuffer);
00210             if (NS_FAILED(rv))
00211                 return rv;
00212 
00213             rv = mListener->OnDataAvailable(request, aContext,
00214                                             inputData, 0, pushBuffer.Length());
00215             if (NS_FAILED(rv))
00216                 return rv;
00217         }
00218     } while (amtRead < aCount);
00219 
00220     return rv;
00221 }
00222 
00223 // nsTXTToHTMLConv methods
00224 nsTXTToHTMLConv::nsTXTToHTMLConv()
00225 {
00226     mToken = nsnull;
00227     mPreFormatHTML = PR_FALSE;
00228 }
00229 
00230 static PRBool CleanupTokens(void *aElement, void *aData)
00231 {
00232     if (aElement) delete (convToken*)aElement;
00233     return PR_TRUE;
00234 }
00235 
00236 nsTXTToHTMLConv::~nsTXTToHTMLConv()
00237 {
00238     mTokens.EnumerateForwards((nsVoidArrayEnumFunc)CleanupTokens, nsnull);
00239 }
00240 
00241 nsresult
00242 nsTXTToHTMLConv::Init()
00243 {
00244     nsresult rv = NS_OK;
00245 
00246     // build up the list of tokens to handle
00247     convToken *token = new convToken;
00248     if (!token) return NS_ERROR_OUT_OF_MEMORY;
00249     token->prepend = PR_FALSE;
00250     token->token.Assign(PRUnichar('<'));
00251     token->modText.AssignLiteral("&lt;");
00252     mTokens.AppendElement(token);
00253 
00254     token = new convToken;
00255     if (!token) return NS_ERROR_OUT_OF_MEMORY;
00256     token->prepend = PR_FALSE;
00257     token->token.Assign(PRUnichar('>'));
00258     token->modText.AssignLiteral("&gt;");
00259     mTokens.AppendElement(token);
00260 
00261     token = new convToken;
00262     if (!token) return NS_ERROR_OUT_OF_MEMORY;
00263     token->prepend = PR_FALSE;
00264     token->token.Assign(PRUnichar('&'));
00265     token->modText.AssignLiteral("&amp;");
00266     mTokens.AppendElement(token);
00267 
00268     token = new convToken;
00269     if (!token) return NS_ERROR_OUT_OF_MEMORY;
00270     token->prepend = PR_TRUE;
00271     token->token.AssignLiteral("http://"); // XXX need to iterate through all protos
00272     mTokens.AppendElement(token);
00273 
00274     token = new convToken;
00275     if (!token) return NS_ERROR_OUT_OF_MEMORY;
00276     token->prepend = PR_TRUE;
00277     token->token.Assign(PRUnichar('@'));
00278     token->modText.AssignLiteral("mailto:");
00279     mTokens.AppendElement(token);
00280 
00281     return rv;
00282 }
00283 
00284 PRInt32
00285 nsTXTToHTMLConv::FindToken(PRInt32 cursor, convToken* *_retval)
00286 {
00287     PRInt32 loc = -1, firstToken = mBuffer.Length();
00288     PRInt8 token = -1;
00289     for (PRInt8 i=0; i < mTokens.Count(); i++) {
00290         loc = mBuffer.Find(((convToken*)mTokens[i])->token, cursor);
00291         if (loc != -1)
00292             if (loc < firstToken) {
00293                 firstToken = loc;
00294                 token = i;
00295             }
00296     }
00297     if (token == -1)
00298         return -1;
00299 
00300     *_retval = (convToken*)mTokens[token];
00301     return firstToken;
00302 }
00303 
00304 PRInt32
00305 nsTXTToHTMLConv::CatHTML(PRInt32 front, PRInt32 back)
00306 {
00307     PRInt32 cursor = 0;
00308     PRInt32 modLen = mToken->modText.Length();
00309     if (!mToken->prepend) {
00310         // replace the entire token (from delimiter to delimiter)
00311         mBuffer.Cut(front, back - front);
00312         mBuffer.Insert(mToken->modText, front);
00313         cursor = front+modLen;
00314     } else {
00315         nsString linkText;
00316         // href is implied
00317         mBuffer.Mid(linkText, front, back-front);
00318         mBuffer.Insert(NS_LITERAL_STRING("<a href=\""), front);
00319         cursor += front+9;
00320         if (modLen)
00321             mBuffer.Insert(mToken->modText, cursor);
00322         cursor += modLen-front+back;
00323         mBuffer.Insert(NS_LITERAL_STRING("\">"), cursor);
00324         cursor += 2;
00325         mBuffer.Insert(linkText, cursor);
00326         cursor += linkText.Length();
00327         mBuffer.Insert(NS_LITERAL_STRING("</a>"), cursor);
00328         cursor += 4;
00329     }
00330     mToken = nsnull; // indicates completeness
00331     return cursor;
00332 }