Bug 1909613 - Enable <details name=''> everywhere, r=emilio
[gecko.git] / netwerk / base / nsAsyncStreamCopier.cpp
blobffcfafc9da9c7ac0f0063b8c2a354bcdab434ac5
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "nsAsyncStreamCopier.h"
6 #include "nsComponentManagerUtils.h"
7 #include "nsIOService.h"
8 #include "nsIEventTarget.h"
9 #include "nsStreamUtils.h"
10 #include "nsThreadUtils.h"
11 #include "nsNetUtil.h"
12 #include "nsNetCID.h"
13 #include "nsIBufferedStreams.h"
14 #include "nsIRequestObserver.h"
15 #include "mozilla/Components.h"
16 #include "mozilla/Logging.h"
18 using namespace mozilla;
19 using namespace mozilla::net;
21 #undef LOG
23 // MOZ_LOG=nsStreamCopier:5
25 static LazyLogModule gStreamCopierLog("nsStreamCopier");
26 #define LOG(args) MOZ_LOG(gStreamCopierLog, mozilla::LogLevel::Debug, args)
28 /**
29 * An event used to perform initialization off the main thread.
31 class AsyncApplyBufferingPolicyEvent final : public Runnable {
32 public:
33 /**
34 * @param aCopier
35 * The nsAsyncStreamCopier requesting the information.
37 explicit AsyncApplyBufferingPolicyEvent(nsAsyncStreamCopier* aCopier)
38 : mozilla::Runnable("AsyncApplyBufferingPolicyEvent"),
39 mCopier(aCopier),
40 mTarget(GetCurrentSerialEventTarget()) {}
42 NS_IMETHOD Run() override {
43 nsresult rv = mCopier->ApplyBufferingPolicy();
44 if (NS_FAILED(rv)) {
45 mCopier->Cancel(rv);
46 return NS_OK;
49 rv = mTarget->Dispatch(
50 NewRunnableMethod("nsAsyncStreamCopier::AsyncCopyInternal", mCopier,
51 &nsAsyncStreamCopier::AsyncCopyInternal),
52 NS_DISPATCH_NORMAL);
53 MOZ_ASSERT(NS_SUCCEEDED(rv));
55 if (NS_FAILED(rv)) {
56 mCopier->Cancel(rv);
58 return NS_OK;
61 private:
62 RefPtr<nsAsyncStreamCopier> mCopier;
63 nsCOMPtr<nsIEventTarget> mTarget;
66 //-----------------------------------------------------------------------------
68 nsAsyncStreamCopier::nsAsyncStreamCopier()
69 : mChunkSize(nsIOService::gDefaultSegmentSize) {
70 LOG(("Creating nsAsyncStreamCopier @%p\n", this));
73 nsAsyncStreamCopier::~nsAsyncStreamCopier() {
74 LOG(("Destroying nsAsyncStreamCopier @%p\n", this));
77 bool nsAsyncStreamCopier::IsComplete(nsresult* status) {
78 MutexAutoLock lock(mLock);
79 if (status) *status = mStatus;
80 return !mIsPending;
83 nsIRequest* nsAsyncStreamCopier::AsRequest() {
84 return static_cast<nsIRequest*>(static_cast<nsIAsyncStreamCopier*>(this));
87 void nsAsyncStreamCopier::Complete(nsresult status) {
88 LOG(("nsAsyncStreamCopier::Complete [this=%p status=%" PRIx32 "]\n", this,
89 static_cast<uint32_t>(status)));
91 nsCOMPtr<nsIRequestObserver> observer;
92 nsCOMPtr<nsISupports> ctx;
94 MutexAutoLock lock(mLock);
95 mCopierCtx = nullptr;
97 if (mIsPending) {
98 mIsPending = false;
99 mStatus = status;
101 // setup OnStopRequest callback and release references...
102 observer = mObserver;
103 mObserver = nullptr;
107 if (observer) {
108 LOG((" calling OnStopRequest [status=%" PRIx32 "]\n",
109 static_cast<uint32_t>(status)));
110 observer->OnStopRequest(AsRequest(), status);
114 void nsAsyncStreamCopier::OnAsyncCopyComplete(void* closure, nsresult status) {
115 // AddRef'd in AsyncCopy. Will be released at the end of the method.
116 RefPtr<nsAsyncStreamCopier> self = dont_AddRef((nsAsyncStreamCopier*)closure);
117 self->Complete(status);
120 //-----------------------------------------------------------------------------
121 // nsISupports
123 // We cannot use simply NS_IMPL_ISUPPORTSx as both
124 // nsIAsyncStreamCopier and nsIAsyncStreamCopier2 implement nsIRequest
126 NS_IMPL_ADDREF(nsAsyncStreamCopier)
127 NS_IMPL_RELEASE(nsAsyncStreamCopier)
128 NS_INTERFACE_TABLE_HEAD(nsAsyncStreamCopier)
129 NS_INTERFACE_TABLE_BEGIN
130 NS_INTERFACE_TABLE_ENTRY(nsAsyncStreamCopier, nsIAsyncStreamCopier)
131 NS_INTERFACE_TABLE_ENTRY(nsAsyncStreamCopier, nsIAsyncStreamCopier2)
132 NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(nsAsyncStreamCopier, nsIRequest,
133 nsIAsyncStreamCopier)
134 NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(nsAsyncStreamCopier, nsISupports,
135 nsIAsyncStreamCopier)
136 NS_INTERFACE_TABLE_END
137 NS_INTERFACE_TABLE_TAIL
139 //-----------------------------------------------------------------------------
140 // nsIRequest
142 NS_IMETHODIMP
143 nsAsyncStreamCopier::GetName(nsACString& name) {
144 name.Truncate();
145 return NS_OK;
148 NS_IMETHODIMP
149 nsAsyncStreamCopier::IsPending(bool* result) {
150 *result = !IsComplete();
151 return NS_OK;
154 NS_IMETHODIMP
155 nsAsyncStreamCopier::GetStatus(nsresult* status) {
156 IsComplete(status);
157 return NS_OK;
160 NS_IMETHODIMP nsAsyncStreamCopier::SetCanceledReason(
161 const nsACString& aReason) {
162 return nsIAsyncStreamCopier::SetCanceledReasonImpl(aReason);
165 NS_IMETHODIMP nsAsyncStreamCopier::GetCanceledReason(nsACString& aReason) {
166 return nsIAsyncStreamCopier::GetCanceledReasonImpl(aReason);
169 NS_IMETHODIMP nsAsyncStreamCopier::CancelWithReason(nsresult aStatus,
170 const nsACString& aReason) {
171 return nsIAsyncStreamCopier::CancelWithReasonImpl(aStatus, aReason);
174 NS_IMETHODIMP
175 nsAsyncStreamCopier::Cancel(nsresult status) {
176 nsCOMPtr<nsISupports> copierCtx;
178 MutexAutoLock lock(mLock);
179 if (!mIsPending) {
180 return NS_OK;
182 copierCtx.swap(mCopierCtx);
185 if (NS_SUCCEEDED(status)) {
186 NS_WARNING("cancel with non-failure status code");
187 status = NS_BASE_STREAM_CLOSED;
190 if (copierCtx) NS_CancelAsyncCopy(copierCtx, status);
192 return NS_OK;
195 NS_IMETHODIMP
196 nsAsyncStreamCopier::Suspend() {
197 MOZ_ASSERT_UNREACHABLE("nsAsyncStreamCopier::Suspend");
198 return NS_ERROR_NOT_IMPLEMENTED;
201 NS_IMETHODIMP
202 nsAsyncStreamCopier::Resume() {
203 MOZ_ASSERT_UNREACHABLE("nsAsyncStreamCopier::Resume");
204 return NS_ERROR_NOT_IMPLEMENTED;
207 NS_IMETHODIMP
208 nsAsyncStreamCopier::GetLoadFlags(nsLoadFlags* aLoadFlags) {
209 *aLoadFlags = LOAD_NORMAL;
210 return NS_OK;
213 NS_IMETHODIMP
214 nsAsyncStreamCopier::SetLoadFlags(nsLoadFlags aLoadFlags) { return NS_OK; }
216 NS_IMETHODIMP
217 nsAsyncStreamCopier::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
218 return nsIAsyncStreamCopier::GetTRRModeImpl(aTRRMode);
221 NS_IMETHODIMP
222 nsAsyncStreamCopier::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
223 return nsIAsyncStreamCopier::SetTRRModeImpl(aTRRMode);
226 NS_IMETHODIMP
227 nsAsyncStreamCopier::GetLoadGroup(nsILoadGroup** aLoadGroup) {
228 *aLoadGroup = nullptr;
229 return NS_OK;
232 NS_IMETHODIMP
233 nsAsyncStreamCopier::SetLoadGroup(nsILoadGroup* aLoadGroup) { return NS_OK; }
235 // Can't be accessed by multiple threads yet
236 nsresult nsAsyncStreamCopier::InitInternal(
237 nsIInputStream* source, nsIOutputStream* sink, nsIEventTarget* target,
238 uint32_t chunkSize, bool closeSource,
239 bool closeSink) MOZ_NO_THREAD_SAFETY_ANALYSIS {
240 NS_ASSERTION(!mSource && !mSink, "Init() called more than once");
241 if (chunkSize == 0) {
242 chunkSize = nsIOService::gDefaultSegmentSize;
244 mChunkSize = chunkSize;
246 mSource = source;
247 mSink = sink;
248 mCloseSource = closeSource;
249 mCloseSink = closeSink;
251 if (target) {
252 mTarget = target;
253 } else {
254 nsresult rv;
255 mTarget = mozilla::components::StreamTransport::Service(&rv);
256 if (NS_FAILED(rv)) {
257 return rv;
261 return NS_OK;
264 //-----------------------------------------------------------------------------
265 // nsIAsyncStreamCopier
267 NS_IMETHODIMP
268 nsAsyncStreamCopier::Init(nsIInputStream* source, nsIOutputStream* sink,
269 nsIEventTarget* target, bool sourceBuffered,
270 bool sinkBuffered, uint32_t chunkSize,
271 bool closeSource, bool closeSink) {
272 NS_ASSERTION(sourceBuffered || sinkBuffered,
273 "at least one stream must be buffered");
274 mMode = sourceBuffered ? NS_ASYNCCOPY_VIA_READSEGMENTS
275 : NS_ASYNCCOPY_VIA_WRITESEGMENTS;
277 return InitInternal(source, sink, target, chunkSize, closeSource, closeSink);
280 //-----------------------------------------------------------------------------
281 // nsIAsyncStreamCopier2
283 NS_IMETHODIMP
284 nsAsyncStreamCopier::Init(nsIInputStream* source, nsIOutputStream* sink,
285 nsIEventTarget* target, uint32_t chunkSize,
286 bool closeSource, bool closeSink) {
287 mShouldSniffBuffering = true;
289 return InitInternal(source, sink, target, chunkSize, closeSource, closeSink);
293 * Detect whether the input or the output stream is buffered,
294 * bufferize one of them if neither is buffered.
296 nsresult nsAsyncStreamCopier::ApplyBufferingPolicy() {
297 // This function causes I/O, it must not be executed on the main
298 // thread.
299 MOZ_ASSERT(!NS_IsMainThread());
301 if (NS_OutputStreamIsBuffered(mSink)) {
302 // Sink is buffered, no need to perform additional buffering
303 mMode = NS_ASYNCCOPY_VIA_WRITESEGMENTS;
304 return NS_OK;
306 if (NS_InputStreamIsBuffered(mSource)) {
307 // Source is buffered, no need to perform additional buffering
308 mMode = NS_ASYNCCOPY_VIA_READSEGMENTS;
309 return NS_OK;
312 // No buffering, let's buffer the sink
313 nsresult rv;
314 nsCOMPtr<nsIBufferedOutputStream> sink =
315 do_CreateInstance(NS_BUFFEREDOUTPUTSTREAM_CONTRACTID, &rv);
316 if (NS_FAILED(rv)) {
317 return rv;
320 rv = sink->Init(mSink, mChunkSize);
321 if (NS_FAILED(rv)) {
322 return rv;
325 mMode = NS_ASYNCCOPY_VIA_WRITESEGMENTS;
326 mSink = sink;
327 return NS_OK;
330 //-----------------------------------------------------------------------------
331 // Both nsIAsyncStreamCopier and nsIAsyncStreamCopier2
333 NS_IMETHODIMP
334 nsAsyncStreamCopier::AsyncCopy(nsIRequestObserver* observer, nsISupports* ctx) {
335 LOG(("nsAsyncStreamCopier::AsyncCopy [this=%p observer=%p]\n", this,
336 observer));
338 NS_ASSERTION(mSource && mSink, "not initialized");
339 nsresult rv;
341 if (observer) {
342 // build proxy for observer events
343 rv = NS_NewRequestObserverProxy(getter_AddRefs(mObserver), observer, ctx);
344 if (NS_FAILED(rv)) return rv;
347 // from this point forward, AsyncCopy is going to return NS_OK. any errors
348 // will be reported via OnStopRequest.
350 MutexAutoLock lock(mLock);
351 mIsPending = true;
354 if (mObserver) {
355 rv = mObserver->OnStartRequest(AsRequest());
356 if (NS_FAILED(rv)) Cancel(rv);
359 if (!mShouldSniffBuffering) {
360 // No buffer sniffing required, let's proceed
361 AsyncCopyInternal();
362 return NS_OK;
365 if (NS_IsMainThread()) {
366 // Don't perform buffer sniffing on the main thread
367 nsCOMPtr<nsIRunnable> event = new AsyncApplyBufferingPolicyEvent(this);
368 rv = mTarget->Dispatch(event, NS_DISPATCH_NORMAL);
369 if (NS_FAILED(rv)) {
370 Cancel(rv);
372 return NS_OK;
375 // We're not going to block the main thread, so let's sniff here
376 rv = ApplyBufferingPolicy();
377 if (NS_FAILED(rv)) {
378 Cancel(rv);
380 AsyncCopyInternal();
381 return NS_OK;
384 // Launch async copy.
385 // All errors are reported through the observer.
386 void nsAsyncStreamCopier::AsyncCopyInternal() {
387 MOZ_ASSERT(mMode == NS_ASYNCCOPY_VIA_READSEGMENTS ||
388 mMode == NS_ASYNCCOPY_VIA_WRITESEGMENTS);
390 nsresult rv;
391 // We want to receive progress notifications; release happens in
392 // OnAsyncCopyComplete.
393 RefPtr<nsAsyncStreamCopier> self = this;
395 MutexAutoLock lock(mLock);
396 rv = NS_AsyncCopy(mSource, mSink, mTarget, mMode, mChunkSize,
397 OnAsyncCopyComplete, this, mCloseSource, mCloseSink,
398 getter_AddRefs(mCopierCtx));
400 if (NS_FAILED(rv)) {
401 Cancel(rv);
402 return; // release self
405 Unused << self.forget(); // Will be released in OnAsyncCopyComplete