1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/streamconv/converters/nsTXTToHTMLConv.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,309 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "nsTXTToHTMLConv.h" 1.10 +#include "nsEscape.h" 1.11 +#include "nsStringStream.h" 1.12 +#include "nsAutoPtr.h" 1.13 +#include "nsIChannel.h" 1.14 +#include <algorithm> 1.15 + 1.16 +#define TOKEN_DELIMITERS MOZ_UTF16("\t\r\n ") 1.17 + 1.18 +// nsISupports methods 1.19 +NS_IMPL_ISUPPORTS(nsTXTToHTMLConv, 1.20 + nsIStreamConverter, 1.21 + nsITXTToHTMLConv, 1.22 + nsIRequestObserver, 1.23 + nsIStreamListener) 1.24 + 1.25 + 1.26 +// nsIStreamConverter methods 1.27 +NS_IMETHODIMP 1.28 +nsTXTToHTMLConv::Convert(nsIInputStream *aFromStream, 1.29 + const char *aFromType, const char *aToType, 1.30 + nsISupports *aCtxt, nsIInputStream * *_retval) 1.31 +{ 1.32 + return NS_ERROR_NOT_IMPLEMENTED; 1.33 +} 1.34 + 1.35 +NS_IMETHODIMP 1.36 +nsTXTToHTMLConv::AsyncConvertData(const char *aFromType, 1.37 + const char *aToType, 1.38 + nsIStreamListener *aListener, 1.39 + nsISupports *aCtxt) 1.40 +{ 1.41 + NS_ASSERTION(aListener, "null pointer"); 1.42 + mListener = aListener; 1.43 + return NS_OK; 1.44 +} 1.45 + 1.46 + 1.47 +// nsIRequestObserver methods 1.48 +NS_IMETHODIMP 1.49 +nsTXTToHTMLConv::OnStartRequest(nsIRequest* request, nsISupports *aContext) 1.50 +{ 1.51 + mBuffer.AssignLiteral("<html>\n<head><title>"); 1.52 + mBuffer.Append(mPageTitle); 1.53 + mBuffer.AppendLiteral("</title></head>\n<body>\n"); 1.54 + if (mPreFormatHTML) { // Use <pre> tags 1.55 + mBuffer.AppendLiteral("<pre>\n"); 1.56 + } 1.57 + 1.58 + // Push mBuffer to the listener now, so the initial HTML will not 1.59 + // be parsed in OnDataAvailable(). 1.60 + 1.61 + nsCOMPtr<nsIChannel> channel = do_QueryInterface(request); 1.62 + if (channel) 1.63 + channel->SetContentType(NS_LITERAL_CSTRING("text/html")); 1.64 + // else, assume there is a channel somewhere that knows what it is doing! 1.65 + 1.66 + nsresult rv = mListener->OnStartRequest(request, aContext); 1.67 + if (NS_FAILED(rv)) return rv; 1.68 + 1.69 + // The request may have been canceled, and if that happens, we want to 1.70 + // suppress calls to OnDataAvailable. 1.71 + request->GetStatus(&rv); 1.72 + if (NS_FAILED(rv)) return rv; 1.73 + 1.74 + nsCOMPtr<nsIInputStream> inputData; 1.75 + rv = NS_NewStringInputStream(getter_AddRefs(inputData), mBuffer); 1.76 + if (NS_FAILED(rv)) return rv; 1.77 + 1.78 + rv = mListener->OnDataAvailable(request, aContext, 1.79 + inputData, 0, mBuffer.Length()); 1.80 + if (NS_FAILED(rv)) return rv; 1.81 + mBuffer.Truncate(); 1.82 + return rv; 1.83 +} 1.84 + 1.85 +NS_IMETHODIMP 1.86 +nsTXTToHTMLConv::OnStopRequest(nsIRequest* request, nsISupports *aContext, 1.87 + nsresult aStatus) 1.88 +{ 1.89 + nsresult rv = NS_OK; 1.90 + if (mToken) { 1.91 + // we still have an outstanding token 1.92 + NS_ASSERTION(mToken->prepend, 1.93 + "Non prepending tokens should be handled in " 1.94 + "OnDataAvailable. There should only be a single " 1.95 + "prepending token left to be processed."); 1.96 + (void)CatHTML(0, mBuffer.Length()); 1.97 + } 1.98 + if (mPreFormatHTML) { 1.99 + mBuffer.AppendLiteral("</pre>\n"); 1.100 + } 1.101 + mBuffer.AppendLiteral("\n</body></html>"); 1.102 + 1.103 + nsCOMPtr<nsIInputStream> inputData; 1.104 + 1.105 + rv = NS_NewStringInputStream(getter_AddRefs(inputData), mBuffer); 1.106 + if (NS_FAILED(rv)) return rv; 1.107 + 1.108 + rv = mListener->OnDataAvailable(request, aContext, 1.109 + inputData, 0, mBuffer.Length()); 1.110 + if (NS_FAILED(rv)) return rv; 1.111 + 1.112 + return mListener->OnStopRequest(request, aContext, aStatus); 1.113 +} 1.114 + 1.115 +// nsITXTToHTMLConv methods 1.116 +NS_IMETHODIMP 1.117 +nsTXTToHTMLConv::SetTitle(const char16_t *aTitle) 1.118 +{ 1.119 + mPageTitle.Assign(aTitle); 1.120 + return NS_OK; 1.121 +} 1.122 + 1.123 +NS_IMETHODIMP 1.124 +nsTXTToHTMLConv::PreFormatHTML(bool value) 1.125 +{ 1.126 + mPreFormatHTML = value; 1.127 + return NS_OK; 1.128 +} 1.129 + 1.130 +// nsIStreamListener method 1.131 +NS_IMETHODIMP 1.132 +nsTXTToHTMLConv::OnDataAvailable(nsIRequest* request, nsISupports *aContext, 1.133 + nsIInputStream *aInStream, 1.134 + uint64_t aOffset, uint32_t aCount) 1.135 +{ 1.136 + nsresult rv = NS_OK; 1.137 + nsString pushBuffer; 1.138 + uint32_t amtRead = 0; 1.139 + nsAutoArrayPtr<char> buffer(new char[aCount+1]); 1.140 + if (!buffer) return NS_ERROR_OUT_OF_MEMORY; 1.141 + 1.142 + do { 1.143 + uint32_t read = 0; 1.144 + // XXX readSegments, to avoid the first copy? 1.145 + rv = aInStream->Read(buffer, aCount-amtRead, &read); 1.146 + if (NS_FAILED(rv)) return rv; 1.147 + 1.148 + buffer[read] = '\0'; 1.149 + // XXX charsets?? non-latin1 characters?? utf-16?? 1.150 + AppendASCIItoUTF16(buffer, mBuffer); 1.151 + amtRead += read; 1.152 + 1.153 + int32_t front = -1, back = -1, tokenLoc = -1, cursor = 0; 1.154 + 1.155 + while ( (tokenLoc = FindToken(cursor, &mToken)) > -1) { 1.156 + if (mToken->prepend) { 1.157 + front = mBuffer.RFindCharInSet(TOKEN_DELIMITERS, tokenLoc); 1.158 + front++; 1.159 + back = mBuffer.FindCharInSet(TOKEN_DELIMITERS, tokenLoc); 1.160 + } else { 1.161 + front = tokenLoc; 1.162 + back = front + mToken->token.Length(); 1.163 + } 1.164 + if (back == -1) { 1.165 + // didn't find an ending, buffer up. 1.166 + mBuffer.Left(pushBuffer, front); 1.167 + cursor = front; 1.168 + break; 1.169 + } 1.170 + // found the end of the token. 1.171 + cursor = CatHTML(front, back); 1.172 + } 1.173 + 1.174 + int32_t end = mBuffer.RFind(TOKEN_DELIMITERS, mBuffer.Length()); 1.175 + mBuffer.Left(pushBuffer, std::max(cursor, end)); 1.176 + mBuffer.Cut(0, std::max(cursor, end)); 1.177 + cursor = 0; 1.178 + 1.179 + if (!pushBuffer.IsEmpty()) { 1.180 + nsCOMPtr<nsIInputStream> inputData; 1.181 + 1.182 + rv = NS_NewStringInputStream(getter_AddRefs(inputData), pushBuffer); 1.183 + if (NS_FAILED(rv)) 1.184 + return rv; 1.185 + 1.186 + rv = mListener->OnDataAvailable(request, aContext, 1.187 + inputData, 0, pushBuffer.Length()); 1.188 + if (NS_FAILED(rv)) 1.189 + return rv; 1.190 + } 1.191 + } while (amtRead < aCount); 1.192 + 1.193 + return rv; 1.194 +} 1.195 + 1.196 +// nsTXTToHTMLConv methods 1.197 +nsTXTToHTMLConv::nsTXTToHTMLConv() 1.198 +{ 1.199 + mToken = nullptr; 1.200 + mPreFormatHTML = false; 1.201 +} 1.202 + 1.203 +nsTXTToHTMLConv::~nsTXTToHTMLConv() 1.204 +{ 1.205 + mTokens.Clear(); 1.206 +} 1.207 + 1.208 +nsresult 1.209 +nsTXTToHTMLConv::Init() 1.210 +{ 1.211 + nsresult rv = NS_OK; 1.212 + 1.213 + // build up the list of tokens to handle 1.214 + convToken *token = new convToken; 1.215 + if (!token) return NS_ERROR_OUT_OF_MEMORY; 1.216 + token->prepend = false; 1.217 + token->token.Assign(char16_t('<')); 1.218 + token->modText.AssignLiteral("<"); 1.219 + mTokens.AppendElement(token); 1.220 + 1.221 + token = new convToken; 1.222 + if (!token) return NS_ERROR_OUT_OF_MEMORY; 1.223 + token->prepend = false; 1.224 + token->token.Assign(char16_t('>')); 1.225 + token->modText.AssignLiteral(">"); 1.226 + mTokens.AppendElement(token); 1.227 + 1.228 + token = new convToken; 1.229 + if (!token) return NS_ERROR_OUT_OF_MEMORY; 1.230 + token->prepend = false; 1.231 + token->token.Assign(char16_t('&')); 1.232 + token->modText.AssignLiteral("&"); 1.233 + mTokens.AppendElement(token); 1.234 + 1.235 + token = new convToken; 1.236 + if (!token) return NS_ERROR_OUT_OF_MEMORY; 1.237 + token->prepend = true; 1.238 + token->token.AssignLiteral("http://"); // XXX need to iterate through all protos 1.239 + mTokens.AppendElement(token); 1.240 + 1.241 + token = new convToken; 1.242 + if (!token) return NS_ERROR_OUT_OF_MEMORY; 1.243 + token->prepend = true; 1.244 + token->token.Assign(char16_t('@')); 1.245 + token->modText.AssignLiteral("mailto:"); 1.246 + mTokens.AppendElement(token); 1.247 + 1.248 + return rv; 1.249 +} 1.250 + 1.251 +int32_t 1.252 +nsTXTToHTMLConv::FindToken(int32_t cursor, convToken* *_retval) 1.253 +{ 1.254 + int32_t loc = -1, firstToken = mBuffer.Length(); 1.255 + int8_t token = -1; 1.256 + for (uint8_t i=0; i < mTokens.Length(); i++) { 1.257 + loc = mBuffer.Find(mTokens[i]->token, cursor); 1.258 + if (loc != -1) 1.259 + if (loc < firstToken) { 1.260 + firstToken = loc; 1.261 + token = i; 1.262 + } 1.263 + } 1.264 + if (token == -1) 1.265 + return -1; 1.266 + 1.267 + *_retval = mTokens[token]; 1.268 + return firstToken; 1.269 +} 1.270 + 1.271 +int32_t 1.272 +nsTXTToHTMLConv::CatHTML(int32_t front, int32_t back) 1.273 +{ 1.274 + int32_t cursor = 0; 1.275 + int32_t modLen = mToken->modText.Length(); 1.276 + if (!mToken->prepend) { 1.277 + // replace the entire token (from delimiter to delimiter) 1.278 + mBuffer.Cut(front, back - front); 1.279 + mBuffer.Insert(mToken->modText, front); 1.280 + cursor = front+modLen; 1.281 + } else { 1.282 + nsString linkText; 1.283 + // href is implied 1.284 + mBuffer.Mid(linkText, front, back-front); 1.285 + 1.286 + mBuffer.Insert(NS_LITERAL_STRING("<a href=\""), front); 1.287 + cursor += front+9; 1.288 + if (modLen) { 1.289 + mBuffer.Insert(mToken->modText, cursor); 1.290 + cursor += modLen; 1.291 + } 1.292 + 1.293 + NS_ConvertUTF16toUTF8 linkTextUTF8(linkText); 1.294 + nsCString escaped; 1.295 + if (NS_EscapeURL(linkTextUTF8.Data(), linkTextUTF8.Length(), esc_Minimal, escaped)) { 1.296 + mBuffer.Cut(cursor, back - front); 1.297 + CopyUTF8toUTF16(escaped, linkText); 1.298 + mBuffer.Insert(linkText, cursor); 1.299 + back = front + linkText.Length(); 1.300 + } 1.301 + 1.302 + cursor += back-front; 1.303 + mBuffer.Insert(NS_LITERAL_STRING("\">"), cursor); 1.304 + cursor += 2; 1.305 + mBuffer.Insert(linkText, cursor); 1.306 + cursor += linkText.Length(); 1.307 + mBuffer.Insert(NS_LITERAL_STRING("</a>"), cursor); 1.308 + cursor += 4; 1.309 + } 1.310 + mToken = nullptr; // indicates completeness 1.311 + return cursor; 1.312 +}