netwerk/streamconv/converters/nsTXTToHTMLConv.cpp

changeset 0
6474c204b198
     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("&lt;");
   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("&gt;");
   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("&amp;");
   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 +}

mercurial