Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/netwerk/base/nsIncrementalStreamLoader.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* This Source Code Form is subject to the terms of the Mozilla Public
3
 * License, v. 2.0. If a copy of the MPL was not distributed with this
4
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6
#include "nsIncrementalStreamLoader.h"
7
#include "nsIInputStream.h"
8
#include "nsIChannel.h"
9
#include "nsError.h"
10
#include "GeckoProfiler.h"
11
12
#include <limits>
13
14
nsIncrementalStreamLoader::nsIncrementalStreamLoader()
15
  : mData(), mBytesConsumed(0)
16
0
{
17
0
}
18
19
NS_IMETHODIMP
20
nsIncrementalStreamLoader::Init(nsIIncrementalStreamLoaderObserver* observer)
21
0
{
22
0
  NS_ENSURE_ARG_POINTER(observer);
23
0
  mObserver = observer;
24
0
  return NS_OK;
25
0
}
26
27
nsresult
28
nsIncrementalStreamLoader::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
29
0
{
30
0
  if (aOuter) return NS_ERROR_NO_AGGREGATION;
31
0
32
0
  nsIncrementalStreamLoader* it = new nsIncrementalStreamLoader();
33
0
  if (it == nullptr)
34
0
    return NS_ERROR_OUT_OF_MEMORY;
35
0
  NS_ADDREF(it);
36
0
  nsresult rv = it->QueryInterface(aIID, aResult);
37
0
  NS_RELEASE(it);
38
0
  return rv;
39
0
}
40
41
NS_IMPL_ISUPPORTS(nsIncrementalStreamLoader, nsIIncrementalStreamLoader,
42
                  nsIRequestObserver, nsIStreamListener,
43
                  nsIThreadRetargetableStreamListener)
44
45
NS_IMETHODIMP
46
nsIncrementalStreamLoader::GetNumBytesRead(uint32_t* aNumBytes)
47
0
{
48
0
  *aNumBytes = mBytesConsumed + mData.length();
49
0
  return NS_OK;
50
0
}
51
52
/* readonly attribute nsIRequest request; */
53
NS_IMETHODIMP
54
nsIncrementalStreamLoader::GetRequest(nsIRequest **aRequest)
55
0
{
56
0
  NS_IF_ADDREF(*aRequest = mRequest);
57
0
  return NS_OK;
58
0
}
59
60
NS_IMETHODIMP
61
nsIncrementalStreamLoader::OnStartRequest(nsIRequest* request, nsISupports *ctxt)
62
0
{
63
0
  nsCOMPtr<nsIChannel> chan( do_QueryInterface(request) );
64
0
  if (chan) {
65
0
    int64_t contentLength = -1;
66
0
    chan->GetContentLength(&contentLength);
67
0
    if (contentLength >= 0) {
68
0
      // On 64bit platforms size of uint64_t coincides with the size of size_t,
69
0
      // so we want to compare with the minimum from size_t and int64_t.
70
0
      if (static_cast<uint64_t>(contentLength) >
71
0
          std::min(std::numeric_limits<size_t>::max(),
72
0
                   static_cast<size_t>(std::numeric_limits<int64_t>::max()))) {
73
0
        // Too big to fit into size_t, so let's bail.
74
0
        return NS_ERROR_OUT_OF_MEMORY;
75
0
      }
76
0
77
0
      // preallocate buffer
78
0
      if (!mData.initCapacity(contentLength)) {
79
0
        return NS_ERROR_OUT_OF_MEMORY;
80
0
      }
81
0
    }
82
0
  }
83
0
  mContext = ctxt;
84
0
  return NS_OK;
85
0
}
86
87
NS_IMETHODIMP
88
nsIncrementalStreamLoader::OnStopRequest(nsIRequest* request, nsISupports *ctxt,
89
                                         nsresult aStatus)
90
0
{
91
0
  AUTO_PROFILER_LABEL("nsIncrementalStreamLoader::OnStopRequest", NETWORK);
92
0
93
0
  if (mObserver) {
94
0
    // provide nsIIncrementalStreamLoader::request during call to OnStreamComplete
95
0
    mRequest = request;
96
0
    size_t length = mData.length();
97
0
    uint8_t* elems = mData.extractOrCopyRawBuffer();
98
0
    nsresult rv = mObserver->OnStreamComplete(this, mContext, aStatus,
99
0
                                              length, elems);
100
0
    if (rv != NS_SUCCESS_ADOPTED_DATA) {
101
0
      // The observer didn't take ownership of the extracted data buffer, so
102
0
      // put it back into mData.
103
0
      mData.replaceRawBuffer(elems, length);
104
0
    }
105
0
    // done.. cleanup
106
0
    ReleaseData();
107
0
    mRequest = nullptr;
108
0
    mObserver = nullptr;
109
0
    mContext = nullptr;
110
0
  }
111
0
  return NS_OK;
112
0
}
113
114
nsresult
115
nsIncrementalStreamLoader::WriteSegmentFun(nsIInputStream *inStr,
116
                                           void *closure,
117
                                           const char *fromSegment,
118
                                           uint32_t toOffset,
119
                                           uint32_t count,
120
                                           uint32_t *writeCount)
121
0
{
122
0
  nsIncrementalStreamLoader *self = (nsIncrementalStreamLoader *) closure;
123
0
124
0
  const uint8_t *data = reinterpret_cast<const uint8_t *>(fromSegment);
125
0
  uint32_t consumedCount = 0;
126
0
  nsresult rv;
127
0
  if (self->mData.empty()) {
128
0
    // Shortcut when observer wants to keep the listener's buffer empty.
129
0
    rv = self->mObserver->OnIncrementalData(self, self->mContext,
130
0
                                            count, data, &consumedCount);
131
0
132
0
    if (rv != NS_OK) {
133
0
      return rv;
134
0
    }
135
0
136
0
    if (consumedCount > count) {
137
0
      return NS_ERROR_INVALID_ARG;
138
0
    }
139
0
140
0
    if (consumedCount < count) {
141
0
      if (!self->mData.append(fromSegment + consumedCount,
142
0
                              count - consumedCount)) {
143
0
        self->mData.clearAndFree();
144
0
        return NS_ERROR_OUT_OF_MEMORY;
145
0
      }
146
0
    }
147
0
  } else {
148
0
    // We have some non-consumed data from previous OnIncrementalData call,
149
0
    // appending new data and reporting combined data.
150
0
    if (!self->mData.append(fromSegment, count)) {
151
0
      self->mData.clearAndFree();
152
0
      return NS_ERROR_OUT_OF_MEMORY;
153
0
    }
154
0
    size_t length = self->mData.length();
155
0
    uint32_t reportCount = length > UINT32_MAX ? UINT32_MAX : (uint32_t)length;
156
0
    uint8_t* elems = self->mData.extractOrCopyRawBuffer();
157
0
158
0
    rv = self->mObserver->OnIncrementalData(self, self->mContext,
159
0
                                            reportCount, elems, &consumedCount);
160
0
161
0
    // We still own elems, freeing its memory when exiting scope.
162
0
    if (rv != NS_OK) {
163
0
      free(elems);
164
0
      return rv;
165
0
    }
166
0
167
0
    if (consumedCount > reportCount) {
168
0
      free(elems);
169
0
      return NS_ERROR_INVALID_ARG;
170
0
    }
171
0
172
0
    if (consumedCount == length) {
173
0
      free(elems); // good case -- fully consumed data
174
0
    } else {
175
0
      // Adopting elems back (at least its portion).
176
0
      self->mData.replaceRawBuffer(elems, length);
177
0
      if (consumedCount > 0) {
178
0
        self->mData.erase(self->mData.begin() + consumedCount);
179
0
      }
180
0
    }
181
0
  }
182
0
183
0
  self->mBytesConsumed += consumedCount;
184
0
  *writeCount = count;
185
0
186
0
  return NS_OK;
187
0
}
188
189
NS_IMETHODIMP
190
nsIncrementalStreamLoader::OnDataAvailable(nsIRequest* request, nsISupports *ctxt,
191
                                nsIInputStream *inStr,
192
                                uint64_t sourceOffset, uint32_t count)
193
0
{
194
0
  if (mObserver) {
195
0
    // provide nsIIncrementalStreamLoader::request during call to OnStreamComplete
196
0
    mRequest = request;
197
0
  }
198
0
  uint32_t countRead;
199
0
  nsresult rv = inStr->ReadSegments(WriteSegmentFun, this, count, &countRead);
200
0
  mRequest = nullptr;
201
0
  return rv;
202
0
}
203
204
void
205
nsIncrementalStreamLoader::ReleaseData()
206
0
{
207
0
  mData.clearAndFree();
208
0
}
209
210
NS_IMETHODIMP
211
nsIncrementalStreamLoader::CheckListenerChain()
212
0
{
213
0
  return NS_OK;
214
0
}