LCOV - code coverage report
Current view: directory - netwerk/streamconv/converters - nsMultiMixedConv.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 498 308 61.8 %
Date: 2012-06-02 Functions: 69 39 56.5 %

       1                 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2                 : /* ***** BEGIN LICENSE BLOCK *****
       3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       4                 :  *
       5                 :  * The contents of this file are subject to the Mozilla Public License Version
       6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       7                 :  * the License. You may obtain a copy of the License at
       8                 :  * http://www.mozilla.org/MPL/
       9                 :  *
      10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      12                 :  * for the specific language governing rights and limitations under the
      13                 :  * License.
      14                 :  *
      15                 :  * The Original Code is mozilla.org code.
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is
      18                 :  * Netscape Communications Corporation.
      19                 :  * Portions created by the Initial Developer are Copyright (C) 1998
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *
      24                 :  * Alternatively, the contents of this file may be used under the terms of
      25                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      26                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      27                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      28                 :  * of those above. If you wish to allow use of your version of this file only
      29                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      30                 :  * use your version of this file under the terms of the MPL, indicate your
      31                 :  * decision by deleting the provisions above and replace them with the notice
      32                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      33                 :  * the provisions above, a recipient may use your version of this file under
      34                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      35                 :  *
      36                 :  * ***** END LICENSE BLOCK ***** */
      37                 : 
      38                 : #include "nsMultiMixedConv.h"
      39                 : #include "nsMemory.h"
      40                 : #include "plstr.h"
      41                 : #include "nsIHttpChannel.h"
      42                 : #include "nsIServiceManager.h"
      43                 : #include "nsNetUtil.h"
      44                 : #include "nsMimeTypes.h"
      45                 : #include "nsIStringStream.h"
      46                 : #include "nsReadableUtils.h"
      47                 : #include "nsCRT.h"
      48                 : #include "nsIHttpChannelInternal.h"
      49                 : #include "nsURLHelper.h"
      50                 : #include "nsIStreamConverterService.h"
      51                 : 
      52                 : //
      53                 : // Helper function for determining the length of data bytes up to
      54                 : // the next multipart token.  A token is usually preceded by a LF
      55                 : // or CRLF delimiter.
      56                 : // 
      57                 : static PRUint32
      58               4 : LengthToToken(const char *cursor, const char *token)
      59                 : {
      60               4 :     PRUint32 len = token - cursor;
      61                 :     // Trim off any LF or CRLF preceding the token
      62               4 :     if (len && *(token-1) == '\n') {
      63               4 :         --len;
      64               4 :         if (len && *(token-2) == '\r')
      65               4 :             --len;
      66                 :     }
      67               4 :     return len;
      68                 : }
      69                 : 
      70               4 : nsPartChannel::nsPartChannel(nsIChannel *aMultipartChannel, PRUint32 aPartID,
      71                 :                              nsIStreamListener* aListener) :
      72                 :   mMultipartChannel(aMultipartChannel),
      73                 :   mListener(aListener),
      74                 :   mStatus(NS_OK),
      75                 :   mContentLength(LL_MAXUINT),
      76                 :   mIsByteRangeRequest(false),
      77                 :   mByteRangeStart(0),
      78                 :   mByteRangeEnd(0),
      79                 :   mPartID(aPartID),
      80               4 :   mIsLastPart(false)
      81                 : {
      82               4 :     mMultipartChannel = aMultipartChannel;
      83                 : 
      84                 :     // Inherit the load flags from the original channel...
      85               4 :     mMultipartChannel->GetLoadFlags(&mLoadFlags);
      86                 : 
      87               4 :     mMultipartChannel->GetLoadGroup(getter_AddRefs(mLoadGroup));
      88               4 : }
      89                 : 
      90               4 : nsPartChannel::~nsPartChannel()
      91                 : {
      92               4 : }
      93                 : 
      94               0 : void nsPartChannel::InitializeByteRange(PRInt64 aStart, PRInt64 aEnd)
      95                 : {
      96               0 :     mIsByteRangeRequest = true;
      97                 :     
      98               0 :     mByteRangeStart = aStart;
      99               0 :     mByteRangeEnd   = aEnd;
     100               0 : }
     101                 : 
     102               4 : nsresult nsPartChannel::SendOnStartRequest(nsISupports* aContext)
     103                 : {
     104               4 :     return mListener->OnStartRequest(this, aContext);
     105                 : }
     106                 : 
     107               4 : nsresult nsPartChannel::SendOnDataAvailable(nsISupports* aContext,
     108                 :                                             nsIInputStream* aStream,
     109                 :                                             PRUint32 aOffset, PRUint32 aLen)
     110                 : {
     111               4 :     return mListener->OnDataAvailable(this, aContext, aStream, aOffset, aLen);
     112                 : }
     113                 : 
     114               4 : nsresult nsPartChannel::SendOnStopRequest(nsISupports* aContext,
     115                 :                                           nsresult aStatus)
     116                 : {
     117                 :     // Drop the listener
     118               8 :     nsCOMPtr<nsIStreamListener> listener;
     119               4 :     listener.swap(mListener);
     120               4 :     return listener->OnStopRequest(this, aContext, aStatus);
     121                 : }
     122                 : 
     123               4 : void nsPartChannel::SetContentDisposition(const nsACString& aContentDispositionHeader)
     124                 : {
     125               4 :     mContentDispositionHeader = aContentDispositionHeader;
     126               8 :     nsCOMPtr<nsIURI> uri;
     127               4 :     GetURI(getter_AddRefs(uri));
     128                 :     NS_GetFilenameFromDisposition(mContentDispositionFilename,
     129               4 :                                   mContentDispositionHeader, uri);
     130               4 :     mContentDisposition = NS_GetContentDispositionFromHeader(mContentDispositionHeader, this);
     131               4 : }
     132                 : 
     133                 : //
     134                 : // nsISupports implementation...
     135                 : //
     136                 : 
     137              44 : NS_IMPL_ADDREF(nsPartChannel)
     138              48 : NS_IMPL_RELEASE(nsPartChannel)
     139                 : 
     140              88 : NS_INTERFACE_MAP_BEGIN(nsPartChannel)
     141              88 :     NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIChannel)
     142              68 :     NS_INTERFACE_MAP_ENTRY(nsIRequest)
     143              64 :     NS_INTERFACE_MAP_ENTRY(nsIChannel)
     144              48 :     NS_INTERFACE_MAP_ENTRY(nsIByteRangeRequest)
     145              48 :     NS_INTERFACE_MAP_ENTRY(nsIMultiPartChannel)
     146              48 : NS_INTERFACE_MAP_END
     147                 : 
     148                 : //
     149                 : // nsIRequest implementation...
     150                 : //
     151                 : 
     152                 : NS_IMETHODIMP
     153               0 : nsPartChannel::GetName(nsACString &aResult)
     154                 : {
     155               0 :     return mMultipartChannel->GetName(aResult);
     156                 : }
     157                 : 
     158                 : NS_IMETHODIMP
     159               0 : nsPartChannel::IsPending(bool *aResult)
     160                 : {
     161                 :     // For now, consider the active lifetime of each part the same as
     162                 :     // the underlying multipart channel...  This is not exactly right,
     163                 :     // but it is good enough :-)
     164               0 :     return mMultipartChannel->IsPending(aResult);
     165                 : }
     166                 : 
     167                 : NS_IMETHODIMP
     168               4 : nsPartChannel::GetStatus(nsresult *aResult)
     169                 : {
     170               4 :     nsresult rv = NS_OK;
     171                 : 
     172               4 :     if (NS_FAILED(mStatus)) {
     173               0 :         *aResult = mStatus;
     174                 :     } else {
     175               4 :         rv = mMultipartChannel->GetStatus(aResult);
     176                 :     }
     177                 : 
     178               4 :     return rv;
     179                 : }
     180                 : 
     181                 : NS_IMETHODIMP
     182               0 : nsPartChannel::Cancel(nsresult aStatus)
     183                 : {
     184                 :     // Cancelling an individual part must not cancel the underlying
     185                 :     // multipart channel...
     186                 :     // XXX but we should stop sending data for _this_ part channel!
     187               0 :     mStatus = aStatus;
     188               0 :     return NS_OK;
     189                 : }
     190                 : 
     191                 : NS_IMETHODIMP
     192               0 : nsPartChannel::Suspend(void)
     193                 : {
     194                 :     // Suspending an individual part must not suspend the underlying
     195                 :     // multipart channel...
     196                 :     // XXX why not?
     197               0 :     return NS_OK;
     198                 : }
     199                 : 
     200                 : NS_IMETHODIMP
     201               0 : nsPartChannel::Resume(void)
     202                 : {
     203                 :     // Resuming an individual part must not resume the underlying
     204                 :     // multipart channel...
     205                 :     // XXX why not?
     206               0 :     return NS_OK;
     207                 : }
     208                 : 
     209                 : //
     210                 : // nsIChannel implementation
     211                 : //
     212                 : 
     213                 : NS_IMETHODIMP
     214               0 : nsPartChannel::GetOriginalURI(nsIURI * *aURI)
     215                 : {
     216               0 :     return mMultipartChannel->GetOriginalURI(aURI);
     217                 : }
     218                 : 
     219                 : NS_IMETHODIMP
     220               0 : nsPartChannel::SetOriginalURI(nsIURI *aURI)
     221                 : {
     222               0 :     return mMultipartChannel->SetOriginalURI(aURI);
     223                 : }
     224                 : 
     225                 : NS_IMETHODIMP
     226              12 : nsPartChannel::GetURI(nsIURI * *aURI)
     227                 : {
     228              12 :     return mMultipartChannel->GetURI(aURI);
     229                 : }
     230                 : 
     231                 : NS_IMETHODIMP
     232               0 : nsPartChannel::Open(nsIInputStream **result)
     233                 : {
     234                 :     // This channel cannot be opened!
     235               0 :     return NS_ERROR_FAILURE;
     236                 : }
     237                 : 
     238                 : NS_IMETHODIMP
     239               0 : nsPartChannel::AsyncOpen(nsIStreamListener *aListener, nsISupports *aContext)
     240                 : {
     241                 :     // This channel cannot be opened!
     242               0 :     return NS_ERROR_FAILURE;
     243                 : }
     244                 : 
     245                 : NS_IMETHODIMP
     246               4 : nsPartChannel::GetLoadFlags(nsLoadFlags *aLoadFlags)
     247                 : {
     248               4 :     *aLoadFlags = mLoadFlags;
     249               4 :     return NS_OK;
     250                 : }
     251                 : 
     252                 : NS_IMETHODIMP
     253               4 : nsPartChannel::SetLoadFlags(nsLoadFlags aLoadFlags)
     254                 : {
     255               4 :     mLoadFlags = aLoadFlags;
     256               4 :     return NS_OK;
     257                 : }
     258                 : 
     259                 : NS_IMETHODIMP
     260               8 : nsPartChannel::GetLoadGroup(nsILoadGroup* *aLoadGroup)
     261                 : {
     262               8 :     *aLoadGroup = mLoadGroup;
     263               8 :     NS_IF_ADDREF(*aLoadGroup);
     264                 : 
     265               8 :     return NS_OK;
     266                 : }
     267                 : 
     268                 : NS_IMETHODIMP
     269               0 : nsPartChannel::SetLoadGroup(nsILoadGroup* aLoadGroup)
     270                 : {
     271               0 :     mLoadGroup = aLoadGroup;
     272                 : 
     273               0 :     return NS_OK;
     274                 : }
     275                 : 
     276                 : NS_IMETHODIMP
     277               0 : nsPartChannel::GetOwner(nsISupports* *aOwner)
     278                 : {
     279               0 :     return mMultipartChannel->GetOwner(aOwner);
     280                 : }
     281                 : 
     282                 : NS_IMETHODIMP
     283               0 : nsPartChannel::SetOwner(nsISupports* aOwner)
     284                 : {
     285               0 :     return mMultipartChannel->SetOwner(aOwner);
     286                 : }
     287                 : 
     288                 : NS_IMETHODIMP
     289               0 : nsPartChannel::GetNotificationCallbacks(nsIInterfaceRequestor* *aCallbacks)
     290                 : {
     291               0 :     return mMultipartChannel->GetNotificationCallbacks(aCallbacks);
     292                 : }
     293                 : 
     294                 : NS_IMETHODIMP
     295               0 : nsPartChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks)
     296                 : {
     297               0 :     return mMultipartChannel->SetNotificationCallbacks(aCallbacks);
     298                 : }
     299                 : 
     300                 : NS_IMETHODIMP 
     301               0 : nsPartChannel::GetSecurityInfo(nsISupports * *aSecurityInfo)
     302                 : {
     303               0 :     return mMultipartChannel->GetSecurityInfo(aSecurityInfo);
     304                 : }
     305                 : 
     306                 : NS_IMETHODIMP
     307               4 : nsPartChannel::GetContentType(nsACString &aContentType)
     308                 : {
     309               4 :     aContentType = mContentType;
     310               4 :     return NS_OK;
     311                 : }
     312                 : 
     313                 : NS_IMETHODIMP
     314               8 : nsPartChannel::SetContentType(const nsACString &aContentType)
     315                 : {
     316                 :     bool dummy;
     317               8 :     net_ParseContentType(aContentType, mContentType, mContentCharset, &dummy);
     318               8 :     return NS_OK;
     319                 : }
     320                 : 
     321                 : NS_IMETHODIMP
     322               0 : nsPartChannel::GetContentCharset(nsACString &aContentCharset)
     323                 : {
     324               0 :     aContentCharset = mContentCharset;
     325               0 :     return NS_OK;
     326                 : }
     327                 : 
     328                 : NS_IMETHODIMP
     329               0 : nsPartChannel::SetContentCharset(const nsACString &aContentCharset)
     330                 : {
     331               0 :     mContentCharset = aContentCharset;
     332               0 :     return NS_OK;
     333                 : }
     334                 : 
     335                 : NS_IMETHODIMP
     336               0 : nsPartChannel::GetContentLength(PRInt32 *aContentLength)
     337                 : {
     338               0 :     *aContentLength = mContentLength; // XXX truncates 64-bit value
     339               0 :     return NS_OK;
     340                 : }
     341                 : 
     342                 : NS_IMETHODIMP
     343               4 : nsPartChannel::SetContentLength(PRInt32 aContentLength)
     344                 : {
     345               4 :     mContentLength = aContentLength;
     346               4 :     return NS_OK;
     347                 : }
     348                 : 
     349                 : NS_IMETHODIMP
     350               0 : nsPartChannel::GetContentDisposition(PRUint32 *aContentDisposition)
     351                 : {
     352               0 :     if (mContentDispositionHeader.IsEmpty())
     353               0 :         return NS_ERROR_NOT_AVAILABLE;
     354                 : 
     355               0 :     *aContentDisposition = mContentDisposition;
     356               0 :     return NS_OK;
     357                 : }
     358                 : 
     359                 : NS_IMETHODIMP
     360               0 : nsPartChannel::GetContentDispositionFilename(nsAString &aContentDispositionFilename)
     361                 : {
     362               0 :     if (mContentDispositionFilename.IsEmpty())
     363               0 :         return NS_ERROR_NOT_AVAILABLE;
     364                 : 
     365               0 :     aContentDispositionFilename = mContentDispositionFilename;
     366               0 :     return NS_OK;
     367                 : }
     368                 : 
     369                 : NS_IMETHODIMP
     370               0 : nsPartChannel::GetContentDispositionHeader(nsACString &aContentDispositionHeader)
     371                 : {
     372               0 :     if (mContentDispositionHeader.IsEmpty())
     373               0 :         return NS_ERROR_NOT_AVAILABLE;
     374                 : 
     375               0 :     aContentDispositionHeader = mContentDispositionHeader;
     376               0 :     return NS_OK;
     377                 : }
     378                 : 
     379                 : NS_IMETHODIMP
     380               0 : nsPartChannel::GetPartID(PRUint32 *aPartID)
     381                 : {
     382               0 :     *aPartID = mPartID;
     383               0 :     return NS_OK;
     384                 : }
     385                 : 
     386                 : NS_IMETHODIMP
     387               0 : nsPartChannel::GetIsLastPart(bool *aIsLastPart)
     388                 : {
     389               0 :     *aIsLastPart = mIsLastPart;
     390               0 :     return NS_OK;
     391                 : }
     392                 : 
     393                 : //
     394                 : // nsIByteRangeRequest implementation...
     395                 : //
     396                 : 
     397                 : NS_IMETHODIMP 
     398               0 : nsPartChannel::GetIsByteRangeRequest(bool *aIsByteRangeRequest)
     399                 : {
     400               0 :     *aIsByteRangeRequest = mIsByteRangeRequest;
     401                 : 
     402               0 :     return NS_OK;
     403                 : }
     404                 : 
     405                 : 
     406                 : NS_IMETHODIMP 
     407               0 : nsPartChannel::GetStartRange(PRInt64 *aStartRange)
     408                 : {
     409               0 :     *aStartRange = mByteRangeStart;
     410                 : 
     411               0 :     return NS_OK;
     412                 : }
     413                 : 
     414                 : NS_IMETHODIMP 
     415               0 : nsPartChannel::GetEndRange(PRInt64 *aEndRange)
     416                 : {
     417               0 :     *aEndRange = mByteRangeEnd;
     418               0 :     return NS_OK;
     419                 : }
     420                 : 
     421                 : NS_IMETHODIMP
     422               0 : nsPartChannel::GetBaseChannel(nsIChannel ** aReturn)
     423                 : {
     424               0 :     NS_ENSURE_ARG_POINTER(aReturn);
     425                 : 
     426               0 :     *aReturn = mMultipartChannel;
     427               0 :     NS_IF_ADDREF(*aReturn);
     428               0 :     return NS_OK;
     429                 : }
     430                 : 
     431                 : 
     432                 : // nsISupports implementation
     433              70 : NS_IMPL_ISUPPORTS3(nsMultiMixedConv,
     434                 :                    nsIStreamConverter,
     435                 :                    nsIStreamListener,
     436                 :                    nsIRequestObserver)
     437                 : 
     438                 : 
     439                 : // nsIStreamConverter implementation
     440                 : 
     441                 : // No syncronous conversion at this time.
     442                 : NS_IMETHODIMP
     443               0 : nsMultiMixedConv::Convert(nsIInputStream *aFromStream,
     444                 :                           const char *aFromType,
     445                 :                           const char *aToType,
     446                 :                           nsISupports *aCtxt, nsIInputStream **_retval) {
     447               0 :     return NS_ERROR_NOT_IMPLEMENTED;
     448                 : }
     449                 : 
     450                 : // Stream converter service calls this to initialize the actual stream converter (us).
     451                 : NS_IMETHODIMP
     452               2 : nsMultiMixedConv::AsyncConvertData(const char *aFromType, const char *aToType,
     453                 :                                    nsIStreamListener *aListener, nsISupports *aCtxt) {
     454               2 :     NS_ASSERTION(aListener && aFromType && aToType, "null pointer passed into multi mixed converter");
     455                 : 
     456                 :     // hook up our final listener. this guy gets the various On*() calls we want to throw
     457                 :     // at him.
     458                 :     //
     459                 :     // WARNING: this listener must be able to handle multiple OnStartRequest, OnDataAvail()
     460                 :     //  and OnStopRequest() call combinations. We call of series of these for each sub-part
     461                 :     //  in the raw stream.
     462               2 :     mFinalListener = aListener;
     463               2 :     return NS_OK;
     464                 : }
     465                 : 
     466                 : // AutoFree implementation to prevent memory leaks
     467                 : class AutoFree
     468                 : {
     469                 : public:
     470                 :   AutoFree() : mBuffer(NULL) {}
     471                 : 
     472               2 :   AutoFree(char *buffer) : mBuffer(buffer) {}
     473                 : 
     474               2 :   ~AutoFree() {
     475               2 :     free(mBuffer);
     476               2 :   }
     477                 : 
     478               3 :   AutoFree& operator=(char *buffer) {
     479               3 :     mBuffer = buffer;
     480               3 :     return *this;
     481                 :   }
     482                 : 
     483              13 :   operator char*() const {
     484              13 :     return mBuffer;
     485                 :   }
     486                 : private:
     487                 :   char *mBuffer;
     488                 : };
     489                 : 
     490                 : // nsIStreamListener implementation
     491                 : NS_IMETHODIMP
     492               2 : nsMultiMixedConv::OnDataAvailable(nsIRequest *request, nsISupports *context,
     493                 :                                   nsIInputStream *inStr, PRUint32 sourceOffset, PRUint32 count) {
     494                 : 
     495               2 :     if (mToken.IsEmpty()) // no token, no love.
     496               0 :         return NS_ERROR_FAILURE;
     497                 : 
     498               2 :     nsresult rv = NS_OK;
     499               4 :     AutoFree buffer = nsnull;
     500               2 :     PRUint32 bufLen = 0, read = 0;
     501                 : 
     502               2 :     NS_ASSERTION(request, "multimixed converter needs a request");
     503                 : 
     504               4 :     nsCOMPtr<nsIChannel> channel = do_QueryInterface(request, &rv);
     505               2 :     if (NS_FAILED(rv)) return rv;
     506                 : 
     507                 :     // fill buffer
     508                 :     {
     509               2 :         bufLen = count + mBufLen;
     510               2 :         NS_ENSURE_TRUE((bufLen >= count) && (bufLen >= mBufLen),
     511                 :                        NS_ERROR_FAILURE);
     512               2 :         buffer = (char *) malloc(bufLen);
     513               2 :         if (!buffer)
     514               0 :             return NS_ERROR_OUT_OF_MEMORY;
     515                 : 
     516               2 :         if (mBufLen) {
     517                 :             // incorporate any buffered data into the parsing
     518               0 :             memcpy(buffer, mBuffer, mBufLen);
     519               0 :             free(mBuffer);
     520               0 :             mBuffer = 0;
     521               0 :             mBufLen = 0;
     522                 :         }
     523                 :         
     524               2 :         rv = inStr->Read(buffer + (bufLen - count), count, &read);
     525                 : 
     526               2 :         if (NS_FAILED(rv) || read == 0) return rv;
     527               2 :         NS_ASSERTION(read == count, "poor data size assumption");
     528                 :     }
     529                 : 
     530               2 :     char *cursor = buffer;
     531                 : 
     532               2 :     if (mFirstOnData) {
     533                 :         // this is the first OnData() for this request. some servers
     534                 :         // don't bother sending a token in the first "part." This is
     535                 :         // illegal, but we'll handle the case anyway by shoving the
     536                 :         // boundary token in for the server.
     537               2 :         mFirstOnData = false;
     538               2 :         NS_ASSERTION(!mBufLen, "this is our first time through, we can't have buffered data");
     539               2 :         const char * token = mToken.get();
     540                 :            
     541               2 :         PushOverLine(cursor, bufLen);
     542                 : 
     543               2 :         if (bufLen < mTokenLen+2) {
     544                 :             // we don't have enough data yet to make this comparison.
     545                 :             // skip this check, and try again the next time OnData()
     546                 :             // is called.
     547               0 :             mFirstOnData = true;
     548                 :         }
     549               2 :         else if (!PL_strnstr(cursor, token, mTokenLen+2)) {
     550               1 :             buffer = (char *) realloc(buffer, bufLen + mTokenLen + 1);
     551               1 :             if (!buffer)
     552               0 :                 return NS_ERROR_OUT_OF_MEMORY;
     553                 : 
     554               1 :             memmove(buffer + mTokenLen + 1, buffer, bufLen);
     555               1 :             memcpy(buffer, token, mTokenLen);
     556               1 :             buffer[mTokenLen] = '\n';
     557                 : 
     558               1 :             bufLen += (mTokenLen + 1);
     559                 : 
     560                 :             // need to reset cursor to the buffer again (bug 100595)
     561               1 :             cursor = buffer;
     562                 :         }
     563                 :     }
     564                 : 
     565               2 :     char *token = nsnull;
     566                 : 
     567               2 :     if (mProcessingHeaders) {
     568                 :         // we were not able to process all the headers
     569                 :         // for this "part" given the previous buffer given to 
     570                 :         // us in the previous OnDataAvailable callback.
     571               0 :         bool done = false;
     572               0 :         rv = ParseHeaders(channel, cursor, bufLen, &done);
     573               0 :         if (NS_FAILED(rv)) return rv;
     574                 : 
     575               0 :         if (done) {
     576               0 :             mProcessingHeaders = false;
     577               0 :             rv = SendStart(channel);
     578               0 :             if (NS_FAILED(rv)) return rv;
     579                 :         }
     580                 :     }
     581                 : 
     582               2 :     PRInt32 tokenLinefeed = 1;
     583              11 :     while ( (token = FindToken(cursor, bufLen)) ) {
     584                 : 
     585               9 :         if (*(token+mTokenLen+1) == '-') {
     586                 :             // This was the last delimiter so we can stop processing
     587               1 :             rv = SendData(cursor, LengthToToken(cursor, token));
     588               1 :             if (NS_FAILED(rv)) return rv;
     589               1 :             return SendStop(NS_OK);
     590                 :         }
     591                 : 
     592               8 :         if (!mNewPart && token > cursor) {
     593                 :             // headers are processed, we're pushing data now.
     594               3 :             NS_ASSERTION(!mProcessingHeaders, "we should be pushing raw data");
     595               3 :             rv = SendData(cursor, LengthToToken(cursor, token));
     596               3 :             bufLen -= token - cursor;
     597               3 :             if (NS_FAILED(rv)) return rv;
     598                 :         }
     599                 :         // XXX else NS_ASSERTION(token == cursor, "?");
     600               8 :         token += mTokenLen;
     601               8 :         bufLen -= mTokenLen;
     602               8 :         tokenLinefeed = PushOverLine(token, bufLen);
     603                 : 
     604               8 :         if (mNewPart) {
     605                 :             // parse headers
     606               5 :             mNewPart = false;
     607               5 :             cursor = token;
     608               5 :             bool done = false; 
     609               5 :             rv = ParseHeaders(channel, cursor, bufLen, &done);
     610               5 :             if (NS_FAILED(rv)) return rv;
     611               5 :             if (done) {
     612               4 :                 rv = SendStart(channel);
     613               4 :                 if (NS_FAILED(rv)) return rv;
     614                 :             }
     615                 :             else {
     616                 :                 // we haven't finished processing header info.
     617                 :                 // we'll break out and try to process later.
     618               1 :                 mProcessingHeaders = true;
     619               1 :                 break;
     620                 :             }
     621                 :         }
     622                 :         else {
     623               3 :             mNewPart = true;
     624                 :             // Reset state so we don't carry it over from part to part
     625               3 :             mContentType.Truncate();
     626               3 :             mContentLength = LL_MAXUINT;
     627               3 :             mContentDisposition.Truncate();
     628               3 :             mIsByteRangeRequest = false;
     629               3 :             mByteRangeStart = 0;
     630               3 :             mByteRangeEnd = 0;
     631                 :             
     632               3 :             rv = SendStop(NS_OK);
     633               3 :             if (NS_FAILED(rv)) return rv;
     634                 :             // reset the token to front. this allows us to treat
     635                 :             // the token as a starting token.
     636               3 :             token -= mTokenLen + tokenLinefeed;
     637               3 :             bufLen += mTokenLen + tokenLinefeed;
     638               3 :             cursor = token;
     639                 :         }
     640                 :     }
     641                 : 
     642                 :     // at this point, we want to buffer up whatever amount (bufLen)
     643                 :     // we have leftover. However, we *always* want to ensure that
     644                 :     // we buffer enough data to handle a broken token.
     645                 : 
     646                 :     // carry over
     647               1 :     PRUint32 bufAmt = 0;
     648               1 :     if (mProcessingHeaders)
     649               1 :         bufAmt = bufLen;
     650               0 :     else if (bufLen) {
     651                 :         // if the data ends in a linefeed, and we're in the middle
     652                 :         // of a "part" (ie. mPartChannel exists) don't bother
     653                 :         // buffering, go ahead and send the data we have. Otherwise
     654                 :         // if we don't have a channel already, then we don't even
     655                 :         // have enough info to start a part, go ahead and buffer
     656                 :         // enough to collect a boundary token.
     657               0 :         if (!mPartChannel || !(cursor[bufLen-1] == nsCRT::LF) )
     658               0 :             bufAmt = NS_MIN(mTokenLen - 1, bufLen);
     659                 :     }
     660                 : 
     661               1 :     if (bufAmt) {
     662               0 :         rv = BufferData(cursor + (bufLen - bufAmt), bufAmt);
     663               0 :         if (NS_FAILED(rv)) return rv;
     664               0 :         bufLen -= bufAmt;
     665                 :     }
     666                 : 
     667               1 :     if (bufLen) {
     668               0 :         rv = SendData(cursor, bufLen);
     669               0 :         if (NS_FAILED(rv)) return rv;
     670                 :     }
     671                 : 
     672               1 :     return rv;
     673                 : }
     674                 : 
     675                 : 
     676                 : // nsIRequestObserver implementation
     677                 : NS_IMETHODIMP
     678               2 : nsMultiMixedConv::OnStartRequest(nsIRequest *request, nsISupports *ctxt) {
     679                 :     // we're assuming the content-type is available at this stage
     680               2 :     NS_ASSERTION(mToken.IsEmpty(), "a second on start???");
     681               2 :     const char *bndry = nsnull;
     682               4 :     nsCAutoString delimiter;
     683               2 :     nsresult rv = NS_OK;
     684               2 :     mContext = ctxt;
     685                 : 
     686               2 :     mFirstOnData = true;
     687               2 :     mTotalSent   = 0;
     688                 : 
     689               4 :     nsCOMPtr<nsIChannel> channel = do_QueryInterface(request, &rv);
     690               2 :     if (NS_FAILED(rv)) return rv;
     691                 :     
     692                 :     // ask the HTTP channel for the content-type and extract the boundary from it.
     693               4 :     nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel, &rv);
     694               2 :     if (NS_SUCCEEDED(rv)) {
     695               2 :         rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("content-type"), delimiter);
     696               2 :         if (NS_FAILED(rv)) return rv;
     697                 :     } else {
     698                 :         // try asking the channel directly
     699               0 :         rv = channel->GetContentType(delimiter);
     700               0 :         if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
     701                 :     }
     702                 : 
     703               2 :     bndry = strstr(delimiter.BeginWriting(), "boundary");
     704               2 :     if (!bndry) return NS_ERROR_FAILURE;
     705                 : 
     706               2 :     bndry = strchr(bndry, '=');
     707               2 :     if (!bndry) return NS_ERROR_FAILURE;
     708                 : 
     709               2 :     bndry++; // move past the equals sign
     710                 : 
     711               2 :     char *attrib = (char *) strchr(bndry, ';');
     712               2 :     if (attrib) *attrib = '\0';
     713                 : 
     714               4 :     nsCAutoString boundaryString(bndry);
     715               2 :     if (attrib) *attrib = ';';
     716                 : 
     717               2 :     boundaryString.Trim(" \"");
     718                 : 
     719               2 :     mToken = boundaryString;
     720               2 :     mTokenLen = boundaryString.Length();
     721                 :     
     722               2 :     if (mTokenLen == 0)
     723               0 :         return NS_ERROR_FAILURE;
     724                 : 
     725               2 :     return NS_OK;
     726                 : }
     727                 : 
     728                 : NS_IMETHODIMP
     729               2 : nsMultiMixedConv::OnStopRequest(nsIRequest *request, nsISupports *ctxt,
     730                 :                                 nsresult aStatus) {
     731                 : 
     732               2 :     if (mToken.IsEmpty())  // no token, no love.
     733               0 :         return NS_ERROR_FAILURE;
     734                 : 
     735               2 :     if (mPartChannel) {
     736               0 :         mPartChannel->SetIsLastPart();
     737                 : 
     738                 :         // we've already called SendStart() (which sets up the mPartChannel,
     739                 :         // and fires an OnStart()) send any data left over, and then fire the stop.
     740               0 :         if (mBufLen > 0 && mBuffer) {
     741               0 :             (void) SendData(mBuffer, mBufLen);
     742                 :             // don't bother checking the return value here, if the send failed
     743                 :             // we're done anyway as we're in the OnStop() callback.
     744               0 :             free(mBuffer);
     745               0 :             mBuffer = nsnull;
     746               0 :             mBufLen = 0;
     747                 :         }
     748               0 :         (void) SendStop(aStatus);
     749               2 :     } else if (NS_FAILED(aStatus)) {
     750                 :         // underlying data production problem. we should not be in
     751                 :         // the middle of sending data. if we were, mPartChannel,
     752                 :         // above, would have been true.
     753                 :         
     754                 :         // if we send the start, the URI Loader's m_targetStreamListener, may
     755                 :         // be pointing at us causing a nice stack overflow.  So, don't call 
     756                 :         // OnStartRequest!  -  This breaks necko's semantecs. 
     757                 :         //(void) mFinalListener->OnStartRequest(request, ctxt);
     758                 :         
     759               0 :         (void) mFinalListener->OnStopRequest(request, ctxt, aStatus);
     760                 :     }
     761                 : 
     762               2 :     return NS_OK;
     763                 : }
     764                 : 
     765                 : 
     766                 : // nsMultiMixedConv methods
     767               2 : nsMultiMixedConv::nsMultiMixedConv() :
     768               2 :   mCurrentPartID(0)
     769                 : {
     770               2 :     mTokenLen           = 0;
     771               2 :     mNewPart            = true;
     772               2 :     mContentLength      = LL_MAXUINT;
     773               2 :     mBuffer             = nsnull;
     774               2 :     mBufLen             = 0;
     775               2 :     mProcessingHeaders  = false;
     776               2 :     mByteRangeStart     = 0;
     777               2 :     mByteRangeEnd       = 0;
     778               2 :     mTotalSent          = 0;
     779               2 :     mIsByteRangeRequest = false;
     780               2 : }
     781                 : 
     782               6 : nsMultiMixedConv::~nsMultiMixedConv() {
     783               2 :     NS_ASSERTION(!mBuffer, "all buffered data should be gone");
     784               2 :     if (mBuffer) {
     785               0 :         free(mBuffer);
     786               0 :         mBuffer = nsnull;
     787                 :     }
     788               8 : }
     789                 : 
     790                 : nsresult
     791               0 : nsMultiMixedConv::BufferData(char *aData, PRUint32 aLen) {
     792               0 :     NS_ASSERTION(!mBuffer, "trying to over-write buffer");
     793                 : 
     794               0 :     char *buffer = (char *) malloc(aLen);
     795               0 :     if (!buffer) return NS_ERROR_OUT_OF_MEMORY;
     796                 : 
     797               0 :     memcpy(buffer, aData, aLen);
     798               0 :     mBuffer = buffer;
     799               0 :     mBufLen = aLen;
     800               0 :     return NS_OK;
     801                 : }
     802                 : 
     803                 : 
     804                 : nsresult
     805               4 : nsMultiMixedConv::SendStart(nsIChannel *aChannel) {
     806               4 :     nsresult rv = NS_OK;
     807                 : 
     808               8 :     nsCOMPtr<nsIStreamListener> partListener(mFinalListener);
     809               4 :     if (mContentType.IsEmpty()) {
     810               4 :         mContentType.AssignLiteral(UNKNOWN_CONTENT_TYPE);
     811                 :         nsCOMPtr<nsIStreamConverterService> serv =
     812               8 :             do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv);
     813               4 :         if (NS_SUCCEEDED(rv)) {
     814               8 :             nsCOMPtr<nsIStreamListener> converter;
     815               4 :             rv = serv->AsyncConvertData(UNKNOWN_CONTENT_TYPE,
     816                 :                                         "*/*",
     817                 :                                         mFinalListener,
     818                 :                                         mContext,
     819               4 :                                         getter_AddRefs(converter));
     820               4 :             if (NS_SUCCEEDED(rv)) {
     821               4 :                 partListener = converter;
     822                 :             }
     823                 :         }
     824                 :     }
     825                 : 
     826                 :     // if we already have an mPartChannel, that means we never sent a Stop()
     827                 :     // before starting up another "part." that would be bad.
     828               4 :     NS_ASSERTION(!mPartChannel, "tisk tisk, shouldn't be overwriting a channel");
     829                 : 
     830                 :     nsPartChannel *newChannel;
     831               8 :     newChannel = new nsPartChannel(aChannel, mCurrentPartID++, partListener);
     832               4 :     if (!newChannel)
     833               0 :         return NS_ERROR_OUT_OF_MEMORY;
     834                 : 
     835               4 :     if (mIsByteRangeRequest) {
     836               0 :         newChannel->InitializeByteRange(mByteRangeStart, mByteRangeEnd);
     837                 :     }
     838                 : 
     839               4 :     mTotalSent = 0;
     840                 : 
     841                 :     // Set up the new part channel...
     842               4 :     mPartChannel = newChannel;
     843                 : 
     844               4 :     rv = mPartChannel->SetContentType(mContentType);
     845               4 :     if (NS_FAILED(rv)) return rv;
     846                 : 
     847               4 :     rv = mPartChannel->SetContentLength(mContentLength); // XXX Truncates 64-bit!
     848               4 :     if (NS_FAILED(rv)) return rv;
     849                 : 
     850               4 :     mPartChannel->SetContentDisposition(mContentDisposition);
     851                 : 
     852               4 :     nsLoadFlags loadFlags = 0;
     853               4 :     mPartChannel->GetLoadFlags(&loadFlags);
     854               4 :     loadFlags |= nsIChannel::LOAD_REPLACE;
     855               4 :     mPartChannel->SetLoadFlags(loadFlags);
     856                 : 
     857               8 :     nsCOMPtr<nsILoadGroup> loadGroup;
     858               4 :     (void)mPartChannel->GetLoadGroup(getter_AddRefs(loadGroup));
     859                 : 
     860                 :     // Add the new channel to the load group (if any)
     861               4 :     if (loadGroup) {
     862               0 :         rv = loadGroup->AddRequest(mPartChannel, nsnull);
     863               0 :         if (NS_FAILED(rv)) return rv;
     864                 :     }
     865                 : 
     866                 :     // Let's start off the load. NOTE: we don't forward on the channel passed
     867                 :     // into our OnDataAvailable() as it's the root channel for the raw stream.
     868               4 :     return mPartChannel->SendOnStartRequest(mContext);
     869                 : }
     870                 : 
     871                 : 
     872                 : nsresult
     873               4 : nsMultiMixedConv::SendStop(nsresult aStatus) {
     874                 :     
     875               4 :     nsresult rv = NS_OK;
     876               4 :     if (mPartChannel) {
     877               4 :         rv = mPartChannel->SendOnStopRequest(mContext, aStatus);
     878                 :         // don't check for failure here, we need to remove the channel from 
     879                 :         // the loadgroup.
     880                 : 
     881                 :         // Remove the channel from its load group (if any)
     882               8 :         nsCOMPtr<nsILoadGroup> loadGroup;
     883               4 :         (void) mPartChannel->GetLoadGroup(getter_AddRefs(loadGroup));
     884               4 :         if (loadGroup) 
     885               0 :             (void) loadGroup->RemoveRequest(mPartChannel, mContext, aStatus);
     886                 :     }
     887                 : 
     888               4 :     mPartChannel = 0;
     889               4 :     return rv;
     890                 : }
     891                 : 
     892                 : nsresult
     893               4 : nsMultiMixedConv::SendData(char *aBuffer, PRUint32 aLen) {
     894                 : 
     895               4 :     nsresult rv = NS_OK;
     896                 :     
     897               4 :     if (!mPartChannel) return NS_ERROR_FAILURE; // something went wrong w/ processing
     898                 : 
     899               4 :     if (mContentLength != LL_MAXUINT) {
     900                 :         // make sure that we don't send more than the mContentLength
     901                 :         // XXX why? perhaps the Content-Length header was actually wrong!!
     902               0 :         if ((PRUint64(aLen) + mTotalSent) > mContentLength)
     903               0 :             aLen = mContentLength - mTotalSent;
     904                 : 
     905               0 :         if (aLen == 0)
     906               0 :             return NS_OK;
     907                 :     }
     908                 : 
     909               4 :     PRUint32 offset = mTotalSent;
     910               4 :     mTotalSent += aLen;
     911                 : 
     912                 :     nsCOMPtr<nsIStringInputStream> ss(
     913               8 :             do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv));
     914               4 :     if (NS_FAILED(rv))
     915               0 :         return rv;
     916                 : 
     917               4 :     rv = ss->ShareData(aBuffer, aLen);
     918               4 :     if (NS_FAILED(rv))
     919               0 :         return rv;
     920                 : 
     921               8 :     nsCOMPtr<nsIInputStream> inStream(do_QueryInterface(ss, &rv));
     922               4 :     if (NS_FAILED(rv)) return rv;
     923                 : 
     924               4 :     return mPartChannel->SendOnDataAvailable(mContext, inStream, offset, aLen);
     925                 : }
     926                 : 
     927                 : PRInt32
     928              10 : nsMultiMixedConv::PushOverLine(char *&aPtr, PRUint32 &aLen) {
     929              10 :     PRInt32 chars = 0;
     930              10 :     if ((aLen > 0) && (*aPtr == nsCRT::CR || *aPtr == nsCRT::LF)) {
     931               7 :         if ((aLen > 1) && (aPtr[1] == nsCRT::LF))
     932               6 :             chars++;
     933               7 :         chars++;
     934               7 :         aPtr += chars;
     935               7 :         aLen -= chars;
     936                 :     }
     937              10 :     return chars;
     938                 : }
     939                 : 
     940                 : nsresult
     941               5 : nsMultiMixedConv::ParseHeaders(nsIChannel *aChannel, char *&aPtr, 
     942                 :                                PRUint32 &aLen, bool *_retval) {
     943                 :     // NOTE: this data must be ascii.
     944                 :     // NOTE: aPtr is NOT null terminated!
     945               5 :     nsresult rv = NS_OK;
     946               5 :     char *cursor = aPtr, *newLine = nsnull;
     947               5 :     PRUint32 cursorLen = aLen;
     948               5 :     bool done = false;
     949               5 :     PRUint32 lineFeedIncrement = 1;
     950                 :     
     951               5 :     mContentLength = LL_MAXUINT; // XXX what if we were already called?
     952              10 :     while (cursorLen && (newLine = (char *) memchr(cursor, nsCRT::LF, cursorLen))) {
     953                 :         // adjust for linefeeds
     954               4 :         if ((newLine > cursor) && (newLine[-1] == nsCRT::CR) ) { // CRLF
     955               4 :             lineFeedIncrement = 2;
     956               4 :             newLine--;
     957                 :         }
     958                 :         else
     959               0 :             lineFeedIncrement = 1; // reset
     960                 : 
     961               4 :         if (newLine == cursor) {
     962                 :             // move the newLine beyond the linefeed marker
     963               4 :             NS_ASSERTION(cursorLen >= lineFeedIncrement, "oops!");
     964                 : 
     965               4 :             cursor += lineFeedIncrement;
     966               4 :             cursorLen -= lineFeedIncrement;
     967                 : 
     968               4 :             done = true;
     969               4 :             break;
     970                 :         }
     971                 : 
     972               0 :         char tmpChar = *newLine;
     973               0 :         *newLine = '\0'; // cursor is now null terminated
     974               0 :         char *colon = (char *) strchr(cursor, ':');
     975               0 :         if (colon) {
     976               0 :             *colon = '\0';
     977               0 :             nsCAutoString headerStr(cursor);
     978               0 :             headerStr.CompressWhitespace();
     979               0 :             *colon = ':';
     980                 : 
     981               0 :             nsCAutoString headerVal(colon + 1);
     982               0 :             headerVal.CompressWhitespace();
     983                 : 
     984                 :             // examine header
     985               0 :             if (headerStr.LowerCaseEqualsLiteral("content-type")) {
     986               0 :                 mContentType = headerVal;
     987               0 :             } else if (headerStr.LowerCaseEqualsLiteral("content-length")) {
     988               0 :                 mContentLength = atoi(headerVal.get()); // XXX 64-bit math?
     989               0 :             } else if (headerStr.LowerCaseEqualsLiteral("content-disposition")) {
     990               0 :                 mContentDisposition = headerVal;
     991               0 :             } else if (headerStr.LowerCaseEqualsLiteral("set-cookie")) {
     992                 :                 nsCOMPtr<nsIHttpChannelInternal> httpInternal =
     993               0 :                     do_QueryInterface(aChannel);
     994               0 :                 if (httpInternal) {
     995               0 :                     httpInternal->SetCookie(headerVal.get());
     996                 :                 }
     997               0 :             } else if (headerStr.LowerCaseEqualsLiteral("content-range") || 
     998               0 :                        headerStr.LowerCaseEqualsLiteral("range") ) {
     999                 :                 // something like: Content-range: bytes 7000-7999/8000
    1000                 :                 char* tmpPtr;
    1001                 : 
    1002               0 :                 tmpPtr = (char *) strchr(colon + 1, '/');
    1003               0 :                 if (tmpPtr) 
    1004               0 :                     *tmpPtr = '\0';
    1005                 : 
    1006                 :                 // pass the bytes-unit and the SP
    1007               0 :                 char *range = (char *) strchr(colon + 2, ' ');
    1008                 : 
    1009               0 :                 if (!range)
    1010               0 :                     return NS_ERROR_FAILURE;
    1011                 : 
    1012               0 :                 if (range[0] == '*'){
    1013               0 :                     mByteRangeStart = mByteRangeEnd = 0;
    1014                 :                 }
    1015                 :                 else {
    1016               0 :                     tmpPtr = (char *) strchr(range, '-');
    1017               0 :                     if (!tmpPtr)
    1018               0 :                         return NS_ERROR_FAILURE;
    1019                 :                     
    1020               0 :                     tmpPtr[0] = '\0';
    1021                 :                     
    1022               0 :                     mByteRangeStart = atoi(range); // XXX want 64-bit conv
    1023               0 :                     tmpPtr++;
    1024               0 :                     mByteRangeEnd = atoi(tmpPtr);
    1025                 :                 }
    1026                 : 
    1027               0 :                 mIsByteRangeRequest = true;
    1028               0 :                 if (mContentLength == LL_MAXUINT)
    1029               0 :                     mContentLength = PRUint64(PRInt64(mByteRangeEnd - mByteRangeStart + PRInt64(1)));
    1030                 :             }
    1031                 :         }
    1032               0 :         *newLine = tmpChar;
    1033               0 :         newLine += lineFeedIncrement;
    1034               0 :         cursorLen -= (newLine - cursor);
    1035               0 :         cursor = newLine;
    1036                 :     }
    1037                 : 
    1038               5 :     aPtr = cursor;
    1039               5 :     aLen = cursorLen;
    1040                 : 
    1041               5 :     *_retval = done;
    1042               5 :     return rv;
    1043                 : }
    1044                 : 
    1045                 : char *
    1046               9 : nsMultiMixedConv::FindToken(char *aCursor, PRUint32 aLen) {
    1047                 :     // strnstr without looking for null termination
    1048               9 :     const char *token = mToken.get();
    1049               9 :     char *cur = aCursor;
    1050                 : 
    1051               9 :     if (!(token && aCursor && *token)) {
    1052               0 :         NS_WARNING("bad data");
    1053               0 :         return nsnull;
    1054                 :     }
    1055                 : 
    1056              95 :     for (; aLen >= mTokenLen; aCursor++, aLen--) {
    1057              95 :         if (!memcmp(aCursor, token, mTokenLen) ) {
    1058               9 :             if ((aCursor - cur) >= 2) {
    1059                 :                 // back the cursor up over a double dash for backwards compat.
    1060               5 :                 if ((*(aCursor-1) == '-') && (*(aCursor-2) == '-')) {
    1061               2 :                     aCursor -= 2;
    1062               2 :                     aLen += 2;
    1063                 : 
    1064                 :                     // we're playing w/ double dash tokens, adjust.
    1065               2 :                     mToken.Assign(aCursor, mTokenLen + 2);
    1066               2 :                     mTokenLen = mToken.Length();
    1067                 :                 }
    1068                 :             }
    1069               9 :             return aCursor;
    1070                 :         }
    1071                 :     }
    1072                 : 
    1073               0 :     return nsnull;
    1074                 : }
    1075                 : 
    1076                 : nsresult
    1077               2 : NS_NewMultiMixedConv(nsMultiMixedConv** aMultiMixedConv)
    1078                 : {
    1079               2 :     NS_PRECONDITION(aMultiMixedConv != nsnull, "null ptr");
    1080               2 :     if (! aMultiMixedConv)
    1081               0 :         return NS_ERROR_NULL_POINTER;
    1082                 : 
    1083               2 :     *aMultiMixedConv = new nsMultiMixedConv();
    1084               2 :     if (! *aMultiMixedConv)
    1085               0 :         return NS_ERROR_OUT_OF_MEMORY;
    1086                 : 
    1087               2 :     NS_ADDREF(*aMultiMixedConv);
    1088               2 :     return NS_OK;
    1089                 : }
    1090                 : 

Generated by: LCOV version 1.7