DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Header

Mercurial (d38398e5144e)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "nsIOService.h"
#include "nsSyncStreamListener.h"
#include "nsIPipe.h"
#include "nsThreadUtils.h"
#include <algorithm>

nsresult
nsSyncStreamListener::Init()
{
    return NS_NewPipe(getter_AddRefs(mPipeIn),
                      getter_AddRefs(mPipeOut),
                      nsIOService::gDefaultSegmentSize,
                      UINT32_MAX, // no size limit
                      false,
                      false);
}

nsresult
nsSyncStreamListener::WaitForData()
{
    mKeepWaiting = true;

    while (mKeepWaiting)
        NS_ENSURE_STATE(NS_ProcessNextEvent(NS_GetCurrentThread()));

    return NS_OK;
}

//-----------------------------------------------------------------------------
// nsSyncStreamListener::nsISupports
//-----------------------------------------------------------------------------

NS_IMPL_ISUPPORTS(nsSyncStreamListener,
                  nsIStreamListener,
                  nsIRequestObserver,
                  nsIInputStream,
                  nsISyncStreamListener)

//-----------------------------------------------------------------------------
// nsSyncStreamListener::nsISyncStreamListener
//-----------------------------------------------------------------------------

NS_IMETHODIMP
nsSyncStreamListener::GetInputStream(nsIInputStream **result)
{
    NS_ADDREF(*result = this);
    return NS_OK;
}

//-----------------------------------------------------------------------------
// nsSyncStreamListener::nsIStreamListener
//-----------------------------------------------------------------------------

NS_IMETHODIMP
nsSyncStreamListener::OnStartRequest(nsIRequest  *request,
                                     nsISupports *context)
{
    return NS_OK;
}

NS_IMETHODIMP
nsSyncStreamListener::OnDataAvailable(nsIRequest     *request,
                                      nsISupports    *context,
                                      nsIInputStream *stream,
                                      uint64_t        offset,
                                      uint32_t        count)
{
    uint32_t bytesWritten;

    nsresult rv = mPipeOut->WriteFrom(stream, count, &bytesWritten);

    // if we get an error, then return failure.  this will cause the
    // channel to be canceled, and as a result our OnStopRequest method
    // will be called immediately.  because of this we do not need to
    // set mStatus or mKeepWaiting here.
    if (NS_FAILED(rv))
        return rv;

    // we expect that all data will be written to the pipe because
    // the pipe was created to have "infinite" room.
    NS_ASSERTION(bytesWritten == count, "did not write all data"); 

    mKeepWaiting = false; // unblock Read
    return NS_OK;
}

NS_IMETHODIMP
nsSyncStreamListener::OnStopRequest(nsIRequest  *request,
                                    nsISupports *context,
                                    nsresult     status)
{
    mStatus = status;
    mKeepWaiting = false; // unblock Read
    mDone = true;
    return NS_OK;
}

//-----------------------------------------------------------------------------
// nsSyncStreamListener::nsIInputStream
//-----------------------------------------------------------------------------

NS_IMETHODIMP
nsSyncStreamListener::Close()
{
    mStatus = NS_BASE_STREAM_CLOSED;
    mDone = true;

    // It'd be nice if we could explicitly cancel the request at this point,
    // but we don't have a reference to it, so the best we can do is close the
    // pipe so that the next OnDataAvailable event will fail.
    if (mPipeIn) {
        mPipeIn->Close();
        mPipeIn = nullptr;
    }
    return NS_OK;
}

NS_IMETHODIMP
nsSyncStreamListener::Available(uint64_t *result)
{
    if (NS_FAILED(mStatus))
        return mStatus;

    mStatus = mPipeIn->Available(result);
    if (NS_SUCCEEDED(mStatus) && (*result == 0) && !mDone) {
        mStatus = WaitForData();
        if (NS_SUCCEEDED(mStatus))
            mStatus = mPipeIn->Available(result);
    }
    return mStatus;
}

NS_IMETHODIMP
nsSyncStreamListener::Read(char     *buf,
                           uint32_t  bufLen,
                           uint32_t *result)
{
    if (mStatus == NS_BASE_STREAM_CLOSED) {
        *result = 0;
        return NS_OK;
    }

    uint64_t avail64;
    if (NS_FAILED(Available(&avail64)))
        return mStatus;

    uint32_t avail = (uint32_t)std::min(avail64, (uint64_t)bufLen);
    mStatus = mPipeIn->Read(buf, avail, result);
    return mStatus;
}

NS_IMETHODIMP
nsSyncStreamListener::ReadSegments(nsWriteSegmentFun  writer,
                                   void              *closure,
                                   uint32_t           count,
                                   uint32_t          *result)
{
    if (mStatus == NS_BASE_STREAM_CLOSED) {
        *result = 0;
        return NS_OK;
    }

    uint64_t avail64;
    if (NS_FAILED(Available(&avail64)))
        return mStatus;

    uint32_t avail = (uint32_t)std::min(avail64, (uint64_t)count);
    mStatus = mPipeIn->ReadSegments(writer, closure, avail, result);
    return mStatus;
}

NS_IMETHODIMP
nsSyncStreamListener::IsNonBlocking(bool *result)
{
    *result = false;
    return NS_OK;
}