netwerk/streamconv/converters/nsMultiMixedConv.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/netwerk/streamconv/converters/nsMultiMixedConv.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1074 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; 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 "nsMultiMixedConv.h"
    1.10 +#include "plstr.h"
    1.11 +#include "nsIHttpChannel.h"
    1.12 +#include "nsNetUtil.h"
    1.13 +#include "nsMimeTypes.h"
    1.14 +#include "nsIStringStream.h"
    1.15 +#include "nsCRT.h"
    1.16 +#include "nsIHttpChannelInternal.h"
    1.17 +#include "nsURLHelper.h"
    1.18 +#include "nsIStreamConverterService.h"
    1.19 +#include <algorithm>
    1.20 +
    1.21 +//
    1.22 +// Helper function for determining the length of data bytes up to
    1.23 +// the next multipart token.  A token is usually preceded by a LF
    1.24 +// or CRLF delimiter.
    1.25 +// 
    1.26 +static uint32_t
    1.27 +LengthToToken(const char *cursor, const char *token)
    1.28 +{
    1.29 +    uint32_t len = token - cursor;
    1.30 +    // Trim off any LF or CRLF preceding the token
    1.31 +    if (len && *(token-1) == '\n') {
    1.32 +        --len;
    1.33 +        if (len && *(token-2) == '\r')
    1.34 +            --len;
    1.35 +    }
    1.36 +    return len;
    1.37 +}
    1.38 +
    1.39 +nsPartChannel::nsPartChannel(nsIChannel *aMultipartChannel, uint32_t aPartID,
    1.40 +                             nsIStreamListener* aListener) :
    1.41 +  mMultipartChannel(aMultipartChannel),
    1.42 +  mListener(aListener),
    1.43 +  mStatus(NS_OK),
    1.44 +  mContentLength(UINT64_MAX),
    1.45 +  mIsByteRangeRequest(false),
    1.46 +  mByteRangeStart(0),
    1.47 +  mByteRangeEnd(0),
    1.48 +  mPartID(aPartID),
    1.49 +  mIsLastPart(false)
    1.50 +{
    1.51 +    mMultipartChannel = aMultipartChannel;
    1.52 +
    1.53 +    // Inherit the load flags from the original channel...
    1.54 +    mMultipartChannel->GetLoadFlags(&mLoadFlags);
    1.55 +
    1.56 +    mMultipartChannel->GetLoadGroup(getter_AddRefs(mLoadGroup));
    1.57 +}
    1.58 +
    1.59 +nsPartChannel::~nsPartChannel()
    1.60 +{
    1.61 +}
    1.62 +
    1.63 +void nsPartChannel::InitializeByteRange(int64_t aStart, int64_t aEnd)
    1.64 +{
    1.65 +    mIsByteRangeRequest = true;
    1.66 +    
    1.67 +    mByteRangeStart = aStart;
    1.68 +    mByteRangeEnd   = aEnd;
    1.69 +}
    1.70 +
    1.71 +nsresult nsPartChannel::SendOnStartRequest(nsISupports* aContext)
    1.72 +{
    1.73 +    return mListener->OnStartRequest(this, aContext);
    1.74 +}
    1.75 +
    1.76 +nsresult nsPartChannel::SendOnDataAvailable(nsISupports* aContext,
    1.77 +                                            nsIInputStream* aStream,
    1.78 +                                            uint64_t aOffset, uint32_t aLen)
    1.79 +{
    1.80 +    return mListener->OnDataAvailable(this, aContext, aStream, aOffset, aLen);
    1.81 +}
    1.82 +
    1.83 +nsresult nsPartChannel::SendOnStopRequest(nsISupports* aContext,
    1.84 +                                          nsresult aStatus)
    1.85 +{
    1.86 +    // Drop the listener
    1.87 +    nsCOMPtr<nsIStreamListener> listener;
    1.88 +    listener.swap(mListener);
    1.89 +    return listener->OnStopRequest(this, aContext, aStatus);
    1.90 +}
    1.91 +
    1.92 +void nsPartChannel::SetContentDisposition(const nsACString& aContentDispositionHeader)
    1.93 +{
    1.94 +    mContentDispositionHeader = aContentDispositionHeader;
    1.95 +    nsCOMPtr<nsIURI> uri;
    1.96 +    GetURI(getter_AddRefs(uri));
    1.97 +    NS_GetFilenameFromDisposition(mContentDispositionFilename,
    1.98 +                                  mContentDispositionHeader, uri);
    1.99 +    mContentDisposition = NS_GetContentDispositionFromHeader(mContentDispositionHeader, this);
   1.100 +}
   1.101 +
   1.102 +//
   1.103 +// nsISupports implementation...
   1.104 +//
   1.105 +
   1.106 +NS_IMPL_ADDREF(nsPartChannel)
   1.107 +NS_IMPL_RELEASE(nsPartChannel)
   1.108 +
   1.109 +NS_INTERFACE_MAP_BEGIN(nsPartChannel)
   1.110 +    NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIChannel)
   1.111 +    NS_INTERFACE_MAP_ENTRY(nsIRequest)
   1.112 +    NS_INTERFACE_MAP_ENTRY(nsIChannel)
   1.113 +    NS_INTERFACE_MAP_ENTRY(nsIByteRangeRequest)
   1.114 +    NS_INTERFACE_MAP_ENTRY(nsIMultiPartChannel)
   1.115 +NS_INTERFACE_MAP_END
   1.116 +
   1.117 +//
   1.118 +// nsIRequest implementation...
   1.119 +//
   1.120 +
   1.121 +NS_IMETHODIMP
   1.122 +nsPartChannel::GetName(nsACString &aResult)
   1.123 +{
   1.124 +    return mMultipartChannel->GetName(aResult);
   1.125 +}
   1.126 +
   1.127 +NS_IMETHODIMP
   1.128 +nsPartChannel::IsPending(bool *aResult)
   1.129 +{
   1.130 +    // For now, consider the active lifetime of each part the same as
   1.131 +    // the underlying multipart channel...  This is not exactly right,
   1.132 +    // but it is good enough :-)
   1.133 +    return mMultipartChannel->IsPending(aResult);
   1.134 +}
   1.135 +
   1.136 +NS_IMETHODIMP
   1.137 +nsPartChannel::GetStatus(nsresult *aResult)
   1.138 +{
   1.139 +    nsresult rv = NS_OK;
   1.140 +
   1.141 +    if (NS_FAILED(mStatus)) {
   1.142 +        *aResult = mStatus;
   1.143 +    } else {
   1.144 +        rv = mMultipartChannel->GetStatus(aResult);
   1.145 +    }
   1.146 +
   1.147 +    return rv;
   1.148 +}
   1.149 +
   1.150 +NS_IMETHODIMP
   1.151 +nsPartChannel::Cancel(nsresult aStatus)
   1.152 +{
   1.153 +    // Cancelling an individual part must not cancel the underlying
   1.154 +    // multipart channel...
   1.155 +    // XXX but we should stop sending data for _this_ part channel!
   1.156 +    mStatus = aStatus;
   1.157 +    return NS_OK;
   1.158 +}
   1.159 +
   1.160 +NS_IMETHODIMP
   1.161 +nsPartChannel::Suspend(void)
   1.162 +{
   1.163 +    // Suspending an individual part must not suspend the underlying
   1.164 +    // multipart channel...
   1.165 +    // XXX why not?
   1.166 +    return NS_OK;
   1.167 +}
   1.168 +
   1.169 +NS_IMETHODIMP
   1.170 +nsPartChannel::Resume(void)
   1.171 +{
   1.172 +    // Resuming an individual part must not resume the underlying
   1.173 +    // multipart channel...
   1.174 +    // XXX why not?
   1.175 +    return NS_OK;
   1.176 +}
   1.177 +
   1.178 +//
   1.179 +// nsIChannel implementation
   1.180 +//
   1.181 +
   1.182 +NS_IMETHODIMP
   1.183 +nsPartChannel::GetOriginalURI(nsIURI * *aURI)
   1.184 +{
   1.185 +    return mMultipartChannel->GetOriginalURI(aURI);
   1.186 +}
   1.187 +
   1.188 +NS_IMETHODIMP
   1.189 +nsPartChannel::SetOriginalURI(nsIURI *aURI)
   1.190 +{
   1.191 +    return mMultipartChannel->SetOriginalURI(aURI);
   1.192 +}
   1.193 +
   1.194 +NS_IMETHODIMP
   1.195 +nsPartChannel::GetURI(nsIURI * *aURI)
   1.196 +{
   1.197 +    return mMultipartChannel->GetURI(aURI);
   1.198 +}
   1.199 +
   1.200 +NS_IMETHODIMP
   1.201 +nsPartChannel::Open(nsIInputStream **result)
   1.202 +{
   1.203 +    // This channel cannot be opened!
   1.204 +    return NS_ERROR_FAILURE;
   1.205 +}
   1.206 +
   1.207 +NS_IMETHODIMP
   1.208 +nsPartChannel::AsyncOpen(nsIStreamListener *aListener, nsISupports *aContext)
   1.209 +{
   1.210 +    // This channel cannot be opened!
   1.211 +    return NS_ERROR_FAILURE;
   1.212 +}
   1.213 +
   1.214 +NS_IMETHODIMP
   1.215 +nsPartChannel::GetLoadFlags(nsLoadFlags *aLoadFlags)
   1.216 +{
   1.217 +    *aLoadFlags = mLoadFlags;
   1.218 +    return NS_OK;
   1.219 +}
   1.220 +
   1.221 +NS_IMETHODIMP
   1.222 +nsPartChannel::SetLoadFlags(nsLoadFlags aLoadFlags)
   1.223 +{
   1.224 +    mLoadFlags = aLoadFlags;
   1.225 +    return NS_OK;
   1.226 +}
   1.227 +
   1.228 +NS_IMETHODIMP
   1.229 +nsPartChannel::GetLoadGroup(nsILoadGroup* *aLoadGroup)
   1.230 +{
   1.231 +    *aLoadGroup = mLoadGroup;
   1.232 +    NS_IF_ADDREF(*aLoadGroup);
   1.233 +
   1.234 +    return NS_OK;
   1.235 +}
   1.236 +
   1.237 +NS_IMETHODIMP
   1.238 +nsPartChannel::SetLoadGroup(nsILoadGroup* aLoadGroup)
   1.239 +{
   1.240 +    mLoadGroup = aLoadGroup;
   1.241 +
   1.242 +    return NS_OK;
   1.243 +}
   1.244 +
   1.245 +NS_IMETHODIMP
   1.246 +nsPartChannel::GetOwner(nsISupports* *aOwner)
   1.247 +{
   1.248 +    return mMultipartChannel->GetOwner(aOwner);
   1.249 +}
   1.250 +
   1.251 +NS_IMETHODIMP
   1.252 +nsPartChannel::SetOwner(nsISupports* aOwner)
   1.253 +{
   1.254 +    return mMultipartChannel->SetOwner(aOwner);
   1.255 +}
   1.256 +
   1.257 +NS_IMETHODIMP
   1.258 +nsPartChannel::GetNotificationCallbacks(nsIInterfaceRequestor* *aCallbacks)
   1.259 +{
   1.260 +    return mMultipartChannel->GetNotificationCallbacks(aCallbacks);
   1.261 +}
   1.262 +
   1.263 +NS_IMETHODIMP
   1.264 +nsPartChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks)
   1.265 +{
   1.266 +    return mMultipartChannel->SetNotificationCallbacks(aCallbacks);
   1.267 +}
   1.268 +
   1.269 +NS_IMETHODIMP 
   1.270 +nsPartChannel::GetSecurityInfo(nsISupports * *aSecurityInfo)
   1.271 +{
   1.272 +    return mMultipartChannel->GetSecurityInfo(aSecurityInfo);
   1.273 +}
   1.274 +
   1.275 +NS_IMETHODIMP
   1.276 +nsPartChannel::GetContentType(nsACString &aContentType)
   1.277 +{
   1.278 +    aContentType = mContentType;
   1.279 +    return NS_OK;
   1.280 +}
   1.281 +
   1.282 +NS_IMETHODIMP
   1.283 +nsPartChannel::SetContentType(const nsACString &aContentType)
   1.284 +{
   1.285 +    bool dummy;
   1.286 +    net_ParseContentType(aContentType, mContentType, mContentCharset, &dummy);
   1.287 +    return NS_OK;
   1.288 +}
   1.289 +
   1.290 +NS_IMETHODIMP
   1.291 +nsPartChannel::GetContentCharset(nsACString &aContentCharset)
   1.292 +{
   1.293 +    aContentCharset = mContentCharset;
   1.294 +    return NS_OK;
   1.295 +}
   1.296 +
   1.297 +NS_IMETHODIMP
   1.298 +nsPartChannel::SetContentCharset(const nsACString &aContentCharset)
   1.299 +{
   1.300 +    mContentCharset = aContentCharset;
   1.301 +    return NS_OK;
   1.302 +}
   1.303 +
   1.304 +NS_IMETHODIMP
   1.305 +nsPartChannel::GetContentLength(int64_t *aContentLength)
   1.306 +{
   1.307 +    *aContentLength = mContentLength;
   1.308 +    return NS_OK;
   1.309 +}
   1.310 +
   1.311 +NS_IMETHODIMP
   1.312 +nsPartChannel::SetContentLength(int64_t aContentLength)
   1.313 +{
   1.314 +    mContentLength = aContentLength;
   1.315 +    return NS_OK;
   1.316 +}
   1.317 +
   1.318 +NS_IMETHODIMP
   1.319 +nsPartChannel::GetContentDisposition(uint32_t *aContentDisposition)
   1.320 +{
   1.321 +    if (mContentDispositionHeader.IsEmpty())
   1.322 +        return NS_ERROR_NOT_AVAILABLE;
   1.323 +
   1.324 +    *aContentDisposition = mContentDisposition;
   1.325 +    return NS_OK;
   1.326 +}
   1.327 +
   1.328 +NS_IMETHODIMP
   1.329 +nsPartChannel::SetContentDisposition(uint32_t aContentDisposition)
   1.330 +{
   1.331 +    return NS_ERROR_NOT_AVAILABLE;
   1.332 +}
   1.333 +
   1.334 +NS_IMETHODIMP
   1.335 +nsPartChannel::GetContentDispositionFilename(nsAString &aContentDispositionFilename)
   1.336 +{
   1.337 +    if (mContentDispositionFilename.IsEmpty())
   1.338 +        return NS_ERROR_NOT_AVAILABLE;
   1.339 +
   1.340 +    aContentDispositionFilename = mContentDispositionFilename;
   1.341 +    return NS_OK;
   1.342 +}
   1.343 +
   1.344 +NS_IMETHODIMP
   1.345 +nsPartChannel::SetContentDispositionFilename(const nsAString &aContentDispositionFilename)
   1.346 +{
   1.347 +    return NS_ERROR_NOT_AVAILABLE;
   1.348 +}
   1.349 +
   1.350 +
   1.351 +NS_IMETHODIMP
   1.352 +nsPartChannel::GetContentDispositionHeader(nsACString &aContentDispositionHeader)
   1.353 +{
   1.354 +    if (mContentDispositionHeader.IsEmpty())
   1.355 +        return NS_ERROR_NOT_AVAILABLE;
   1.356 +
   1.357 +    aContentDispositionHeader = mContentDispositionHeader;
   1.358 +    return NS_OK;
   1.359 +}
   1.360 +
   1.361 +NS_IMETHODIMP
   1.362 +nsPartChannel::GetPartID(uint32_t *aPartID)
   1.363 +{
   1.364 +    *aPartID = mPartID;
   1.365 +    return NS_OK;
   1.366 +}
   1.367 +
   1.368 +NS_IMETHODIMP
   1.369 +nsPartChannel::GetIsLastPart(bool *aIsLastPart)
   1.370 +{
   1.371 +    *aIsLastPart = mIsLastPart;
   1.372 +    return NS_OK;
   1.373 +}
   1.374 +
   1.375 +//
   1.376 +// nsIByteRangeRequest implementation...
   1.377 +//
   1.378 +
   1.379 +NS_IMETHODIMP 
   1.380 +nsPartChannel::GetIsByteRangeRequest(bool *aIsByteRangeRequest)
   1.381 +{
   1.382 +    *aIsByteRangeRequest = mIsByteRangeRequest;
   1.383 +
   1.384 +    return NS_OK;
   1.385 +}
   1.386 +
   1.387 +
   1.388 +NS_IMETHODIMP 
   1.389 +nsPartChannel::GetStartRange(int64_t *aStartRange)
   1.390 +{
   1.391 +    *aStartRange = mByteRangeStart;
   1.392 +
   1.393 +    return NS_OK;
   1.394 +}
   1.395 +
   1.396 +NS_IMETHODIMP 
   1.397 +nsPartChannel::GetEndRange(int64_t *aEndRange)
   1.398 +{
   1.399 +    *aEndRange = mByteRangeEnd;
   1.400 +    return NS_OK;
   1.401 +}
   1.402 +
   1.403 +NS_IMETHODIMP
   1.404 +nsPartChannel::GetBaseChannel(nsIChannel ** aReturn)
   1.405 +{
   1.406 +    NS_ENSURE_ARG_POINTER(aReturn);
   1.407 +
   1.408 +    *aReturn = mMultipartChannel;
   1.409 +    NS_IF_ADDREF(*aReturn);
   1.410 +    return NS_OK;
   1.411 +}
   1.412 +
   1.413 +
   1.414 +// nsISupports implementation
   1.415 +NS_IMPL_ISUPPORTS(nsMultiMixedConv,
   1.416 +                  nsIStreamConverter,
   1.417 +                  nsIStreamListener,
   1.418 +                  nsIRequestObserver)
   1.419 +
   1.420 +
   1.421 +// nsIStreamConverter implementation
   1.422 +
   1.423 +// No syncronous conversion at this time.
   1.424 +NS_IMETHODIMP
   1.425 +nsMultiMixedConv::Convert(nsIInputStream *aFromStream,
   1.426 +                          const char *aFromType,
   1.427 +                          const char *aToType,
   1.428 +                          nsISupports *aCtxt, nsIInputStream **_retval) {
   1.429 +    return NS_ERROR_NOT_IMPLEMENTED;
   1.430 +}
   1.431 +
   1.432 +// Stream converter service calls this to initialize the actual stream converter (us).
   1.433 +NS_IMETHODIMP
   1.434 +nsMultiMixedConv::AsyncConvertData(const char *aFromType, const char *aToType,
   1.435 +                                   nsIStreamListener *aListener, nsISupports *aCtxt) {
   1.436 +    NS_ASSERTION(aListener && aFromType && aToType, "null pointer passed into multi mixed converter");
   1.437 +
   1.438 +    // hook up our final listener. this guy gets the various On*() calls we want to throw
   1.439 +    // at him.
   1.440 +    //
   1.441 +    // WARNING: this listener must be able to handle multiple OnStartRequest, OnDataAvail()
   1.442 +    //  and OnStopRequest() call combinations. We call of series of these for each sub-part
   1.443 +    //  in the raw stream.
   1.444 +    mFinalListener = aListener;
   1.445 +    return NS_OK;
   1.446 +}
   1.447 +
   1.448 +// AutoFree implementation to prevent memory leaks
   1.449 +class AutoFree
   1.450 +{
   1.451 +public:
   1.452 +  AutoFree() : mBuffer(nullptr) {}
   1.453 +
   1.454 +  AutoFree(char *buffer) : mBuffer(buffer) {}
   1.455 +
   1.456 +  ~AutoFree() {
   1.457 +    free(mBuffer);
   1.458 +  }
   1.459 +
   1.460 +  AutoFree& operator=(char *buffer) {
   1.461 +    mBuffer = buffer;
   1.462 +    return *this;
   1.463 +  }
   1.464 +
   1.465 +  operator char*() const {
   1.466 +    return mBuffer;
   1.467 +  }
   1.468 +private:
   1.469 +  char *mBuffer;
   1.470 +};
   1.471 +
   1.472 +// nsIStreamListener implementation
   1.473 +NS_IMETHODIMP
   1.474 +nsMultiMixedConv::OnDataAvailable(nsIRequest *request, nsISupports *context,
   1.475 +                                  nsIInputStream *inStr, uint64_t sourceOffset,
   1.476 +                                  uint32_t count) {
   1.477 +
   1.478 +    if (mToken.IsEmpty()) // no token, no love.
   1.479 +        return NS_ERROR_FAILURE;
   1.480 +
   1.481 +    nsresult rv = NS_OK;
   1.482 +    AutoFree buffer = nullptr;
   1.483 +    uint32_t bufLen = 0, read = 0;
   1.484 +
   1.485 +    NS_ASSERTION(request, "multimixed converter needs a request");
   1.486 +
   1.487 +    nsCOMPtr<nsIChannel> channel = do_QueryInterface(request, &rv);
   1.488 +    if (NS_FAILED(rv)) return rv;
   1.489 +
   1.490 +    // fill buffer
   1.491 +    {
   1.492 +        bufLen = count + mBufLen;
   1.493 +        NS_ENSURE_TRUE((bufLen >= count) && (bufLen >= mBufLen),
   1.494 +                       NS_ERROR_FAILURE);
   1.495 +        buffer = (char *) malloc(bufLen);
   1.496 +        if (!buffer)
   1.497 +            return NS_ERROR_OUT_OF_MEMORY;
   1.498 +
   1.499 +        if (mBufLen) {
   1.500 +            // incorporate any buffered data into the parsing
   1.501 +            memcpy(buffer, mBuffer, mBufLen);
   1.502 +            free(mBuffer);
   1.503 +            mBuffer = 0;
   1.504 +            mBufLen = 0;
   1.505 +        }
   1.506 +        
   1.507 +        rv = inStr->Read(buffer + (bufLen - count), count, &read);
   1.508 +
   1.509 +        if (NS_FAILED(rv) || read == 0) return rv;
   1.510 +        NS_ASSERTION(read == count, "poor data size assumption");
   1.511 +    }
   1.512 +
   1.513 +    char *cursor = buffer;
   1.514 +
   1.515 +    if (mFirstOnData) {
   1.516 +        // this is the first OnData() for this request. some servers
   1.517 +        // don't bother sending a token in the first "part." This is
   1.518 +        // illegal, but we'll handle the case anyway by shoving the
   1.519 +        // boundary token in for the server.
   1.520 +        mFirstOnData = false;
   1.521 +        NS_ASSERTION(!mBufLen, "this is our first time through, we can't have buffered data");
   1.522 +        const char * token = mToken.get();
   1.523 +           
   1.524 +        PushOverLine(cursor, bufLen);
   1.525 +
   1.526 +        if (bufLen < mTokenLen+2) {
   1.527 +            // we don't have enough data yet to make this comparison.
   1.528 +            // skip this check, and try again the next time OnData()
   1.529 +            // is called.
   1.530 +            mFirstOnData = true;
   1.531 +        }
   1.532 +        else if (!PL_strnstr(cursor, token, mTokenLen+2)) {
   1.533 +            buffer = (char *) realloc(buffer, bufLen + mTokenLen + 1);
   1.534 +            if (!buffer)
   1.535 +                return NS_ERROR_OUT_OF_MEMORY;
   1.536 +
   1.537 +            memmove(buffer + mTokenLen + 1, buffer, bufLen);
   1.538 +            memcpy(buffer, token, mTokenLen);
   1.539 +            buffer[mTokenLen] = '\n';
   1.540 +
   1.541 +            bufLen += (mTokenLen + 1);
   1.542 +
   1.543 +            // need to reset cursor to the buffer again (bug 100595)
   1.544 +            cursor = buffer;
   1.545 +        }
   1.546 +    }
   1.547 +
   1.548 +    char *token = nullptr;
   1.549 +
   1.550 +    if (mProcessingHeaders) {
   1.551 +        // we were not able to process all the headers
   1.552 +        // for this "part" given the previous buffer given to 
   1.553 +        // us in the previous OnDataAvailable callback.
   1.554 +        bool done = false;
   1.555 +        rv = ParseHeaders(channel, cursor, bufLen, &done);
   1.556 +        if (NS_FAILED(rv)) return rv;
   1.557 +
   1.558 +        if (done) {
   1.559 +            mProcessingHeaders = false;
   1.560 +            rv = SendStart(channel);
   1.561 +            if (NS_FAILED(rv)) return rv;
   1.562 +        }
   1.563 +    }
   1.564 +
   1.565 +    int32_t tokenLinefeed = 1;
   1.566 +    while ( (token = FindToken(cursor, bufLen)) ) {
   1.567 +
   1.568 +        if (((token + mTokenLen) < (cursor + bufLen)) &&
   1.569 +            (*(token + mTokenLen + 1) == '-')) {
   1.570 +            // This was the last delimiter so we can stop processing
   1.571 +            rv = SendData(cursor, LengthToToken(cursor, token));
   1.572 +            if (NS_FAILED(rv)) return rv;
   1.573 +            return SendStop(NS_OK);
   1.574 +        }
   1.575 +
   1.576 +        if (!mNewPart && token > cursor) {
   1.577 +            // headers are processed, we're pushing data now.
   1.578 +            NS_ASSERTION(!mProcessingHeaders, "we should be pushing raw data");
   1.579 +            rv = SendData(cursor, LengthToToken(cursor, token));
   1.580 +            bufLen -= token - cursor;
   1.581 +            if (NS_FAILED(rv)) return rv;
   1.582 +        }
   1.583 +        // XXX else NS_ASSERTION(token == cursor, "?");
   1.584 +        token += mTokenLen;
   1.585 +        bufLen -= mTokenLen;
   1.586 +        tokenLinefeed = PushOverLine(token, bufLen);
   1.587 +
   1.588 +        if (mNewPart) {
   1.589 +            // parse headers
   1.590 +            mNewPart = false;
   1.591 +            cursor = token;
   1.592 +            bool done = false; 
   1.593 +            rv = ParseHeaders(channel, cursor, bufLen, &done);
   1.594 +            if (NS_FAILED(rv)) return rv;
   1.595 +            if (done) {
   1.596 +                rv = SendStart(channel);
   1.597 +                if (NS_FAILED(rv)) return rv;
   1.598 +            }
   1.599 +            else {
   1.600 +                // we haven't finished processing header info.
   1.601 +                // we'll break out and try to process later.
   1.602 +                mProcessingHeaders = true;
   1.603 +                break;
   1.604 +            }
   1.605 +        }
   1.606 +        else {
   1.607 +            mNewPart = true;
   1.608 +            // Reset state so we don't carry it over from part to part
   1.609 +            mContentType.Truncate();
   1.610 +            mContentLength = UINT64_MAX;
   1.611 +            mContentDisposition.Truncate();
   1.612 +            mIsByteRangeRequest = false;
   1.613 +            mByteRangeStart = 0;
   1.614 +            mByteRangeEnd = 0;
   1.615 +            
   1.616 +            rv = SendStop(NS_OK);
   1.617 +            if (NS_FAILED(rv)) return rv;
   1.618 +            // reset the token to front. this allows us to treat
   1.619 +            // the token as a starting token.
   1.620 +            token -= mTokenLen + tokenLinefeed;
   1.621 +            bufLen += mTokenLen + tokenLinefeed;
   1.622 +            cursor = token;
   1.623 +        }
   1.624 +    }
   1.625 +
   1.626 +    // at this point, we want to buffer up whatever amount (bufLen)
   1.627 +    // we have leftover. However, we *always* want to ensure that
   1.628 +    // we buffer enough data to handle a broken token.
   1.629 +
   1.630 +    // carry over
   1.631 +    uint32_t bufAmt = 0;
   1.632 +    if (mProcessingHeaders)
   1.633 +        bufAmt = bufLen;
   1.634 +    else if (bufLen) {
   1.635 +        // if the data ends in a linefeed, and we're in the middle
   1.636 +        // of a "part" (ie. mPartChannel exists) don't bother
   1.637 +        // buffering, go ahead and send the data we have. Otherwise
   1.638 +        // if we don't have a channel already, then we don't even
   1.639 +        // have enough info to start a part, go ahead and buffer
   1.640 +        // enough to collect a boundary token.
   1.641 +        if (!mPartChannel || !(cursor[bufLen-1] == nsCRT::LF) )
   1.642 +            bufAmt = std::min(mTokenLen - 1, bufLen);
   1.643 +    }
   1.644 +
   1.645 +    if (bufAmt) {
   1.646 +        rv = BufferData(cursor + (bufLen - bufAmt), bufAmt);
   1.647 +        if (NS_FAILED(rv)) return rv;
   1.648 +        bufLen -= bufAmt;
   1.649 +    }
   1.650 +
   1.651 +    if (bufLen) {
   1.652 +        rv = SendData(cursor, bufLen);
   1.653 +        if (NS_FAILED(rv)) return rv;
   1.654 +    }
   1.655 +
   1.656 +    return rv;
   1.657 +}
   1.658 +
   1.659 +
   1.660 +// nsIRequestObserver implementation
   1.661 +NS_IMETHODIMP
   1.662 +nsMultiMixedConv::OnStartRequest(nsIRequest *request, nsISupports *ctxt) {
   1.663 +    // we're assuming the content-type is available at this stage
   1.664 +    NS_ASSERTION(mToken.IsEmpty(), "a second on start???");
   1.665 +    const char *bndry = nullptr;
   1.666 +    nsAutoCString delimiter;
   1.667 +    nsresult rv = NS_OK;
   1.668 +    mContext = ctxt;
   1.669 +
   1.670 +    mFirstOnData = true;
   1.671 +    mTotalSent   = 0;
   1.672 +
   1.673 +    nsCOMPtr<nsIChannel> channel = do_QueryInterface(request, &rv);
   1.674 +    if (NS_FAILED(rv)) return rv;
   1.675 +    
   1.676 +    // ask the HTTP channel for the content-type and extract the boundary from it.
   1.677 +    nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel, &rv);
   1.678 +    if (NS_SUCCEEDED(rv)) {
   1.679 +        rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("content-type"), delimiter);
   1.680 +        if (NS_FAILED(rv)) return rv;
   1.681 +    } else {
   1.682 +        // try asking the channel directly
   1.683 +        rv = channel->GetContentType(delimiter);
   1.684 +        if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
   1.685 +    }
   1.686 +
   1.687 +    bndry = strstr(delimiter.BeginWriting(), "boundary");
   1.688 +    if (!bndry) return NS_ERROR_FAILURE;
   1.689 +
   1.690 +    bndry = strchr(bndry, '=');
   1.691 +    if (!bndry) return NS_ERROR_FAILURE;
   1.692 +
   1.693 +    bndry++; // move past the equals sign
   1.694 +
   1.695 +    char *attrib = (char *) strchr(bndry, ';');
   1.696 +    if (attrib) *attrib = '\0';
   1.697 +
   1.698 +    nsAutoCString boundaryString(bndry);
   1.699 +    if (attrib) *attrib = ';';
   1.700 +
   1.701 +    boundaryString.Trim(" \"");
   1.702 +
   1.703 +    mToken = boundaryString;
   1.704 +    mTokenLen = boundaryString.Length();
   1.705 +    
   1.706 +    if (mTokenLen == 0)
   1.707 +        return NS_ERROR_FAILURE;
   1.708 +
   1.709 +    return NS_OK;
   1.710 +}
   1.711 +
   1.712 +NS_IMETHODIMP
   1.713 +nsMultiMixedConv::OnStopRequest(nsIRequest *request, nsISupports *ctxt,
   1.714 +                                nsresult aStatus) {
   1.715 +
   1.716 +    if (mToken.IsEmpty())  // no token, no love.
   1.717 +        return NS_ERROR_FAILURE;
   1.718 +
   1.719 +    if (mPartChannel) {
   1.720 +        mPartChannel->SetIsLastPart();
   1.721 +
   1.722 +        // we've already called SendStart() (which sets up the mPartChannel,
   1.723 +        // and fires an OnStart()) send any data left over, and then fire the stop.
   1.724 +        if (mBufLen > 0 && mBuffer) {
   1.725 +            (void) SendData(mBuffer, mBufLen);
   1.726 +            // don't bother checking the return value here, if the send failed
   1.727 +            // we're done anyway as we're in the OnStop() callback.
   1.728 +            free(mBuffer);
   1.729 +            mBuffer = nullptr;
   1.730 +            mBufLen = 0;
   1.731 +        }
   1.732 +        (void) SendStop(aStatus);
   1.733 +    } else if (NS_FAILED(aStatus)) {
   1.734 +        // underlying data production problem. we should not be in
   1.735 +        // the middle of sending data. if we were, mPartChannel,
   1.736 +        // above, would have been true.
   1.737 +        
   1.738 +        // if we send the start, the URI Loader's m_targetStreamListener, may
   1.739 +        // be pointing at us causing a nice stack overflow.  So, don't call 
   1.740 +        // OnStartRequest!  -  This breaks necko's semantecs. 
   1.741 +        //(void) mFinalListener->OnStartRequest(request, ctxt);
   1.742 +        
   1.743 +        (void) mFinalListener->OnStopRequest(request, ctxt, aStatus);
   1.744 +    }
   1.745 +
   1.746 +    return NS_OK;
   1.747 +}
   1.748 +
   1.749 +
   1.750 +// nsMultiMixedConv methods
   1.751 +nsMultiMixedConv::nsMultiMixedConv() :
   1.752 +  mCurrentPartID(0)
   1.753 +{
   1.754 +    mTokenLen           = 0;
   1.755 +    mNewPart            = true;
   1.756 +    mContentLength      = UINT64_MAX;
   1.757 +    mBuffer             = nullptr;
   1.758 +    mBufLen             = 0;
   1.759 +    mProcessingHeaders  = false;
   1.760 +    mByteRangeStart     = 0;
   1.761 +    mByteRangeEnd       = 0;
   1.762 +    mTotalSent          = 0;
   1.763 +    mIsByteRangeRequest = false;
   1.764 +}
   1.765 +
   1.766 +nsMultiMixedConv::~nsMultiMixedConv() {
   1.767 +    NS_ASSERTION(!mBuffer, "all buffered data should be gone");
   1.768 +    if (mBuffer) {
   1.769 +        free(mBuffer);
   1.770 +        mBuffer = nullptr;
   1.771 +    }
   1.772 +}
   1.773 +
   1.774 +nsresult
   1.775 +nsMultiMixedConv::BufferData(char *aData, uint32_t aLen) {
   1.776 +    NS_ASSERTION(!mBuffer, "trying to over-write buffer");
   1.777 +
   1.778 +    char *buffer = (char *) malloc(aLen);
   1.779 +    if (!buffer) return NS_ERROR_OUT_OF_MEMORY;
   1.780 +
   1.781 +    memcpy(buffer, aData, aLen);
   1.782 +    mBuffer = buffer;
   1.783 +    mBufLen = aLen;
   1.784 +    return NS_OK;
   1.785 +}
   1.786 +
   1.787 +
   1.788 +nsresult
   1.789 +nsMultiMixedConv::SendStart(nsIChannel *aChannel) {
   1.790 +    nsresult rv = NS_OK;
   1.791 +
   1.792 +    nsCOMPtr<nsIStreamListener> partListener(mFinalListener);
   1.793 +    if (mContentType.IsEmpty()) {
   1.794 +        mContentType.AssignLiteral(UNKNOWN_CONTENT_TYPE);
   1.795 +        nsCOMPtr<nsIStreamConverterService> serv =
   1.796 +            do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv);
   1.797 +        if (NS_SUCCEEDED(rv)) {
   1.798 +            nsCOMPtr<nsIStreamListener> converter;
   1.799 +            rv = serv->AsyncConvertData(UNKNOWN_CONTENT_TYPE,
   1.800 +                                        "*/*",
   1.801 +                                        mFinalListener,
   1.802 +                                        mContext,
   1.803 +                                        getter_AddRefs(converter));
   1.804 +            if (NS_SUCCEEDED(rv)) {
   1.805 +                partListener = converter;
   1.806 +            }
   1.807 +        }
   1.808 +    }
   1.809 +
   1.810 +    // if we already have an mPartChannel, that means we never sent a Stop()
   1.811 +    // before starting up another "part." that would be bad.
   1.812 +    NS_ASSERTION(!mPartChannel, "tisk tisk, shouldn't be overwriting a channel");
   1.813 +
   1.814 +    nsPartChannel *newChannel;
   1.815 +    newChannel = new nsPartChannel(aChannel, mCurrentPartID++, partListener);
   1.816 +    if (!newChannel)
   1.817 +        return NS_ERROR_OUT_OF_MEMORY;
   1.818 +
   1.819 +    if (mIsByteRangeRequest) {
   1.820 +        newChannel->InitializeByteRange(mByteRangeStart, mByteRangeEnd);
   1.821 +    }
   1.822 +
   1.823 +    mTotalSent = 0;
   1.824 +
   1.825 +    // Set up the new part channel...
   1.826 +    mPartChannel = newChannel;
   1.827 +
   1.828 +    rv = mPartChannel->SetContentType(mContentType);
   1.829 +    if (NS_FAILED(rv)) return rv;
   1.830 +
   1.831 +    rv = mPartChannel->SetContentLength(mContentLength);
   1.832 +    if (NS_FAILED(rv)) return rv;
   1.833 +
   1.834 +    mPartChannel->SetContentDisposition(mContentDisposition);
   1.835 +
   1.836 +    nsLoadFlags loadFlags = 0;
   1.837 +    mPartChannel->GetLoadFlags(&loadFlags);
   1.838 +    loadFlags |= nsIChannel::LOAD_REPLACE;
   1.839 +    mPartChannel->SetLoadFlags(loadFlags);
   1.840 +
   1.841 +    nsCOMPtr<nsILoadGroup> loadGroup;
   1.842 +    (void)mPartChannel->GetLoadGroup(getter_AddRefs(loadGroup));
   1.843 +
   1.844 +    // Add the new channel to the load group (if any)
   1.845 +    if (loadGroup) {
   1.846 +        rv = loadGroup->AddRequest(mPartChannel, nullptr);
   1.847 +        if (NS_FAILED(rv)) return rv;
   1.848 +    }
   1.849 +
   1.850 +    // Let's start off the load. NOTE: we don't forward on the channel passed
   1.851 +    // into our OnDataAvailable() as it's the root channel for the raw stream.
   1.852 +    return mPartChannel->SendOnStartRequest(mContext);
   1.853 +}
   1.854 +
   1.855 +
   1.856 +nsresult
   1.857 +nsMultiMixedConv::SendStop(nsresult aStatus) {
   1.858 +    
   1.859 +    nsresult rv = NS_OK;
   1.860 +    if (mPartChannel) {
   1.861 +        rv = mPartChannel->SendOnStopRequest(mContext, aStatus);
   1.862 +        // don't check for failure here, we need to remove the channel from 
   1.863 +        // the loadgroup.
   1.864 +
   1.865 +        // Remove the channel from its load group (if any)
   1.866 +        nsCOMPtr<nsILoadGroup> loadGroup;
   1.867 +        (void) mPartChannel->GetLoadGroup(getter_AddRefs(loadGroup));
   1.868 +        if (loadGroup) 
   1.869 +            (void) loadGroup->RemoveRequest(mPartChannel, mContext, aStatus);
   1.870 +    }
   1.871 +
   1.872 +    mPartChannel = 0;
   1.873 +    return rv;
   1.874 +}
   1.875 +
   1.876 +nsresult
   1.877 +nsMultiMixedConv::SendData(char *aBuffer, uint32_t aLen) {
   1.878 +
   1.879 +    nsresult rv = NS_OK;
   1.880 +    
   1.881 +    if (!mPartChannel) return NS_ERROR_FAILURE; // something went wrong w/ processing
   1.882 +
   1.883 +    if (mContentLength != UINT64_MAX) {
   1.884 +        // make sure that we don't send more than the mContentLength
   1.885 +        // XXX why? perhaps the Content-Length header was actually wrong!!
   1.886 +        if ((uint64_t(aLen) + mTotalSent) > mContentLength)
   1.887 +            aLen = static_cast<uint32_t>(mContentLength - mTotalSent);
   1.888 +
   1.889 +        if (aLen == 0)
   1.890 +            return NS_OK;
   1.891 +    }
   1.892 +
   1.893 +    uint64_t offset = mTotalSent;
   1.894 +    mTotalSent += aLen;
   1.895 +
   1.896 +    nsCOMPtr<nsIStringInputStream> ss(
   1.897 +            do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv));
   1.898 +    if (NS_FAILED(rv))
   1.899 +        return rv;
   1.900 +
   1.901 +    rv = ss->ShareData(aBuffer, aLen);
   1.902 +    if (NS_FAILED(rv))
   1.903 +        return rv;
   1.904 +
   1.905 +    nsCOMPtr<nsIInputStream> inStream(do_QueryInterface(ss, &rv));
   1.906 +    if (NS_FAILED(rv)) return rv;
   1.907 +
   1.908 +    return mPartChannel->SendOnDataAvailable(mContext, inStream, offset, aLen);
   1.909 +}
   1.910 +
   1.911 +int32_t
   1.912 +nsMultiMixedConv::PushOverLine(char *&aPtr, uint32_t &aLen) {
   1.913 +    int32_t chars = 0;
   1.914 +    if ((aLen > 0) && (*aPtr == nsCRT::CR || *aPtr == nsCRT::LF)) {
   1.915 +        if ((aLen > 1) && (aPtr[1] == nsCRT::LF))
   1.916 +            chars++;
   1.917 +        chars++;
   1.918 +        aPtr += chars;
   1.919 +        aLen -= chars;
   1.920 +    }
   1.921 +    return chars;
   1.922 +}
   1.923 +
   1.924 +nsresult
   1.925 +nsMultiMixedConv::ParseHeaders(nsIChannel *aChannel, char *&aPtr, 
   1.926 +                               uint32_t &aLen, bool *_retval) {
   1.927 +    // NOTE: this data must be ascii.
   1.928 +    // NOTE: aPtr is NOT null terminated!
   1.929 +    nsresult rv = NS_OK;
   1.930 +    char *cursor = aPtr, *newLine = nullptr;
   1.931 +    uint32_t cursorLen = aLen;
   1.932 +    bool done = false;
   1.933 +    uint32_t lineFeedIncrement = 1;
   1.934 +    
   1.935 +    mContentLength = UINT64_MAX; // XXX what if we were already called?
   1.936 +    while (cursorLen && (newLine = (char *) memchr(cursor, nsCRT::LF, cursorLen))) {
   1.937 +        // adjust for linefeeds
   1.938 +        if ((newLine > cursor) && (newLine[-1] == nsCRT::CR) ) { // CRLF
   1.939 +            lineFeedIncrement = 2;
   1.940 +            newLine--;
   1.941 +        }
   1.942 +        else
   1.943 +            lineFeedIncrement = 1; // reset
   1.944 +
   1.945 +        if (newLine == cursor) {
   1.946 +            // move the newLine beyond the linefeed marker
   1.947 +            NS_ASSERTION(cursorLen >= lineFeedIncrement, "oops!");
   1.948 +
   1.949 +            cursor += lineFeedIncrement;
   1.950 +            cursorLen -= lineFeedIncrement;
   1.951 +
   1.952 +            done = true;
   1.953 +            break;
   1.954 +        }
   1.955 +
   1.956 +        char tmpChar = *newLine;
   1.957 +        *newLine = '\0'; // cursor is now null terminated
   1.958 +        char *colon = (char *) strchr(cursor, ':');
   1.959 +        if (colon) {
   1.960 +            *colon = '\0';
   1.961 +            nsAutoCString headerStr(cursor);
   1.962 +            headerStr.CompressWhitespace();
   1.963 +            *colon = ':';
   1.964 +
   1.965 +            nsAutoCString headerVal(colon + 1);
   1.966 +            headerVal.CompressWhitespace();
   1.967 +
   1.968 +            // examine header
   1.969 +            if (headerStr.LowerCaseEqualsLiteral("content-type")) {
   1.970 +                mContentType = headerVal;
   1.971 +            } else if (headerStr.LowerCaseEqualsLiteral("content-length")) {
   1.972 +                mContentLength = nsCRT::atoll(headerVal.get());
   1.973 +            } else if (headerStr.LowerCaseEqualsLiteral("content-disposition")) {
   1.974 +                mContentDisposition = headerVal;
   1.975 +            } else if (headerStr.LowerCaseEqualsLiteral("set-cookie")) {
   1.976 +                nsCOMPtr<nsIHttpChannelInternal> httpInternal =
   1.977 +                    do_QueryInterface(aChannel);
   1.978 +                if (httpInternal) {
   1.979 +                    httpInternal->SetCookie(headerVal.get());
   1.980 +                }
   1.981 +            } else if (headerStr.LowerCaseEqualsLiteral("content-range") || 
   1.982 +                       headerStr.LowerCaseEqualsLiteral("range") ) {
   1.983 +                // something like: Content-range: bytes 7000-7999/8000
   1.984 +                char* tmpPtr;
   1.985 +
   1.986 +                tmpPtr = (char *) strchr(colon + 1, '/');
   1.987 +                if (tmpPtr) 
   1.988 +                    *tmpPtr = '\0';
   1.989 +
   1.990 +                // pass the bytes-unit and the SP
   1.991 +                char *range = (char *) strchr(colon + 2, ' ');
   1.992 +                if (!range)
   1.993 +                    return NS_ERROR_FAILURE;
   1.994 +
   1.995 +                do {
   1.996 +                    range++;
   1.997 +                } while (*range == ' ');
   1.998 +
   1.999 +                if (range[0] == '*'){
  1.1000 +                    mByteRangeStart = mByteRangeEnd = 0;
  1.1001 +                }
  1.1002 +                else {
  1.1003 +                    tmpPtr = (char *) strchr(range, '-');
  1.1004 +                    if (!tmpPtr)
  1.1005 +                        return NS_ERROR_FAILURE;
  1.1006 +                    
  1.1007 +                    tmpPtr[0] = '\0';
  1.1008 +                    
  1.1009 +                    mByteRangeStart = nsCRT::atoll(range);
  1.1010 +                    tmpPtr++;
  1.1011 +                    mByteRangeEnd = nsCRT::atoll(tmpPtr);
  1.1012 +                }
  1.1013 +
  1.1014 +                mIsByteRangeRequest = true;
  1.1015 +                if (mContentLength == UINT64_MAX)
  1.1016 +                    mContentLength = uint64_t(mByteRangeEnd - mByteRangeStart + 1);
  1.1017 +            }
  1.1018 +        }
  1.1019 +        *newLine = tmpChar;
  1.1020 +        newLine += lineFeedIncrement;
  1.1021 +        cursorLen -= (newLine - cursor);
  1.1022 +        cursor = newLine;
  1.1023 +    }
  1.1024 +
  1.1025 +    aPtr = cursor;
  1.1026 +    aLen = cursorLen;
  1.1027 +
  1.1028 +    *_retval = done;
  1.1029 +    return rv;
  1.1030 +}
  1.1031 +
  1.1032 +char *
  1.1033 +nsMultiMixedConv::FindToken(char *aCursor, uint32_t aLen) {
  1.1034 +    // strnstr without looking for null termination
  1.1035 +    const char *token = mToken.get();
  1.1036 +    char *cur = aCursor;
  1.1037 +
  1.1038 +    if (!(token && aCursor && *token)) {
  1.1039 +        NS_WARNING("bad data");
  1.1040 +        return nullptr;
  1.1041 +    }
  1.1042 +
  1.1043 +    for (; aLen >= mTokenLen; aCursor++, aLen--) {
  1.1044 +        if (!memcmp(aCursor, token, mTokenLen) ) {
  1.1045 +            if ((aCursor - cur) >= 2) {
  1.1046 +                // back the cursor up over a double dash for backwards compat.
  1.1047 +                if ((*(aCursor-1) == '-') && (*(aCursor-2) == '-')) {
  1.1048 +                    aCursor -= 2;
  1.1049 +                    aLen += 2;
  1.1050 +
  1.1051 +                    // we're playing w/ double dash tokens, adjust.
  1.1052 +                    mToken.Assign(aCursor, mTokenLen + 2);
  1.1053 +                    mTokenLen = mToken.Length();
  1.1054 +                }
  1.1055 +            }
  1.1056 +            return aCursor;
  1.1057 +        }
  1.1058 +    }
  1.1059 +
  1.1060 +    return nullptr;
  1.1061 +}
  1.1062 +
  1.1063 +nsresult
  1.1064 +NS_NewMultiMixedConv(nsMultiMixedConv** aMultiMixedConv)
  1.1065 +{
  1.1066 +    NS_PRECONDITION(aMultiMixedConv != nullptr, "null ptr");
  1.1067 +    if (! aMultiMixedConv)
  1.1068 +        return NS_ERROR_NULL_POINTER;
  1.1069 +
  1.1070 +    *aMultiMixedConv = new nsMultiMixedConv();
  1.1071 +    if (! *aMultiMixedConv)
  1.1072 +        return NS_ERROR_OUT_OF_MEMORY;
  1.1073 +
  1.1074 +    NS_ADDREF(*aMultiMixedConv);
  1.1075 +    return NS_OK;
  1.1076 +}
  1.1077 +

mercurial