|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
|
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 "nsBaseChannel.h" |
|
7 #include "nsURLHelper.h" |
|
8 #include "nsNetUtil.h" |
|
9 #include "nsMimeTypes.h" |
|
10 #include "nsIHttpEventSink.h" |
|
11 #include "nsIHttpChannel.h" |
|
12 #include "nsIChannelEventSink.h" |
|
13 #include "nsIStreamConverterService.h" |
|
14 #include "nsChannelClassifier.h" |
|
15 #include "nsAsyncRedirectVerifyHelper.h" |
|
16 |
|
17 static PLDHashOperator |
|
18 CopyProperties(const nsAString &key, nsIVariant *data, void *closure) |
|
19 { |
|
20 nsIWritablePropertyBag *bag = |
|
21 static_cast<nsIWritablePropertyBag *>(closure); |
|
22 |
|
23 bag->SetProperty(key, data); |
|
24 return PL_DHASH_NEXT; |
|
25 } |
|
26 |
|
27 // This class is used to suspend a request across a function scope. |
|
28 class ScopedRequestSuspender { |
|
29 public: |
|
30 ScopedRequestSuspender(nsIRequest *request) |
|
31 : mRequest(request) { |
|
32 if (mRequest && NS_FAILED(mRequest->Suspend())) { |
|
33 NS_WARNING("Couldn't suspend pump"); |
|
34 mRequest = nullptr; |
|
35 } |
|
36 } |
|
37 ~ScopedRequestSuspender() { |
|
38 if (mRequest) |
|
39 mRequest->Resume(); |
|
40 } |
|
41 private: |
|
42 nsIRequest *mRequest; |
|
43 }; |
|
44 |
|
45 // Used to suspend data events from mPump within a function scope. This is |
|
46 // usually needed when a function makes callbacks that could process events. |
|
47 #define SUSPEND_PUMP_FOR_SCOPE() \ |
|
48 ScopedRequestSuspender pump_suspender__(mPump) |
|
49 |
|
50 //----------------------------------------------------------------------------- |
|
51 // nsBaseChannel |
|
52 |
|
53 nsBaseChannel::nsBaseChannel() |
|
54 : mLoadFlags(LOAD_NORMAL) |
|
55 , mQueriedProgressSink(true) |
|
56 , mSynthProgressEvents(false) |
|
57 , mWasOpened(false) |
|
58 , mWaitingOnAsyncRedirect(false) |
|
59 , mStatus(NS_OK) |
|
60 , mContentDispositionHint(UINT32_MAX) |
|
61 , mContentLength(-1) |
|
62 { |
|
63 mContentType.AssignLiteral(UNKNOWN_CONTENT_TYPE); |
|
64 } |
|
65 |
|
66 nsresult |
|
67 nsBaseChannel::Redirect(nsIChannel *newChannel, uint32_t redirectFlags, |
|
68 bool openNewChannel) |
|
69 { |
|
70 SUSPEND_PUMP_FOR_SCOPE(); |
|
71 |
|
72 // Transfer properties |
|
73 |
|
74 newChannel->SetLoadGroup(mLoadGroup); |
|
75 newChannel->SetNotificationCallbacks(mCallbacks); |
|
76 newChannel->SetLoadFlags(mLoadFlags | LOAD_REPLACE); |
|
77 |
|
78 // Try to preserve the privacy bit if it has been overridden |
|
79 if (mPrivateBrowsingOverriden) { |
|
80 nsCOMPtr<nsIPrivateBrowsingChannel> newPBChannel = |
|
81 do_QueryInterface(newChannel); |
|
82 if (newPBChannel) { |
|
83 newPBChannel->SetPrivate(mPrivateBrowsing); |
|
84 } |
|
85 } |
|
86 |
|
87 nsCOMPtr<nsIWritablePropertyBag> bag = ::do_QueryInterface(newChannel); |
|
88 if (bag) |
|
89 mPropertyHash.EnumerateRead(CopyProperties, bag.get()); |
|
90 |
|
91 // Notify consumer, giving chance to cancel redirect. For backwards compat, |
|
92 // we support nsIHttpEventSink if we are an HTTP channel and if this is not |
|
93 // an internal redirect. |
|
94 |
|
95 nsRefPtr<nsAsyncRedirectVerifyHelper> redirectCallbackHelper = |
|
96 new nsAsyncRedirectVerifyHelper(); |
|
97 |
|
98 bool checkRedirectSynchronously = !openNewChannel; |
|
99 |
|
100 mRedirectChannel = newChannel; |
|
101 mRedirectFlags = redirectFlags; |
|
102 mOpenRedirectChannel = openNewChannel; |
|
103 nsresult rv = redirectCallbackHelper->Init(this, newChannel, redirectFlags, |
|
104 checkRedirectSynchronously); |
|
105 if (NS_FAILED(rv)) |
|
106 return rv; |
|
107 |
|
108 if (checkRedirectSynchronously && NS_FAILED(mStatus)) |
|
109 return mStatus; |
|
110 |
|
111 return NS_OK; |
|
112 } |
|
113 |
|
114 nsresult |
|
115 nsBaseChannel::ContinueRedirect() |
|
116 { |
|
117 // Backwards compat for non-internal redirects from a HTTP channel. |
|
118 // XXX Is our http channel implementation going to derive from nsBaseChannel? |
|
119 // If not, this code can be removed. |
|
120 if (!(mRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL)) { |
|
121 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(); |
|
122 if (httpChannel) { |
|
123 nsCOMPtr<nsIHttpEventSink> httpEventSink; |
|
124 GetCallback(httpEventSink); |
|
125 if (httpEventSink) { |
|
126 nsresult rv = httpEventSink->OnRedirect(httpChannel, mRedirectChannel); |
|
127 if (NS_FAILED(rv)) { |
|
128 return rv; |
|
129 } |
|
130 } |
|
131 } |
|
132 } |
|
133 |
|
134 // Make sure to do this _after_ making all the OnChannelRedirect calls |
|
135 mRedirectChannel->SetOriginalURI(OriginalURI()); |
|
136 |
|
137 // If we fail to open the new channel, then we want to leave this channel |
|
138 // unaffected, so we defer tearing down our channel until we have succeeded |
|
139 // with the redirect. |
|
140 |
|
141 if (mOpenRedirectChannel) { |
|
142 nsresult rv = mRedirectChannel->AsyncOpen(mListener, mListenerContext); |
|
143 if (NS_FAILED(rv)) |
|
144 return rv; |
|
145 } |
|
146 |
|
147 mRedirectChannel = nullptr; |
|
148 |
|
149 // close down this channel |
|
150 Cancel(NS_BINDING_REDIRECTED); |
|
151 ChannelDone(); |
|
152 |
|
153 return NS_OK; |
|
154 } |
|
155 |
|
156 bool |
|
157 nsBaseChannel::HasContentTypeHint() const |
|
158 { |
|
159 NS_ASSERTION(!Pending(), "HasContentTypeHint called too late"); |
|
160 return !mContentType.EqualsLiteral(UNKNOWN_CONTENT_TYPE); |
|
161 } |
|
162 |
|
163 nsresult |
|
164 nsBaseChannel::PushStreamConverter(const char *fromType, |
|
165 const char *toType, |
|
166 bool invalidatesContentLength, |
|
167 nsIStreamListener **result) |
|
168 { |
|
169 NS_ASSERTION(mListener, "no listener"); |
|
170 |
|
171 nsresult rv; |
|
172 nsCOMPtr<nsIStreamConverterService> scs = |
|
173 do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv); |
|
174 if (NS_FAILED(rv)) |
|
175 return rv; |
|
176 |
|
177 nsCOMPtr<nsIStreamListener> converter; |
|
178 rv = scs->AsyncConvertData(fromType, toType, mListener, mListenerContext, |
|
179 getter_AddRefs(converter)); |
|
180 if (NS_SUCCEEDED(rv)) { |
|
181 mListener = converter; |
|
182 if (invalidatesContentLength) |
|
183 mContentLength = -1; |
|
184 if (result) { |
|
185 *result = nullptr; |
|
186 converter.swap(*result); |
|
187 } |
|
188 } |
|
189 return rv; |
|
190 } |
|
191 |
|
192 nsresult |
|
193 nsBaseChannel::BeginPumpingData() |
|
194 { |
|
195 nsCOMPtr<nsIInputStream> stream; |
|
196 nsCOMPtr<nsIChannel> channel; |
|
197 nsresult rv = OpenContentStream(true, getter_AddRefs(stream), |
|
198 getter_AddRefs(channel)); |
|
199 if (NS_FAILED(rv)) |
|
200 return rv; |
|
201 |
|
202 NS_ASSERTION(!stream || !channel, "Got both a channel and a stream?"); |
|
203 |
|
204 if (channel) { |
|
205 rv = NS_DispatchToCurrentThread(new RedirectRunnable(this, channel)); |
|
206 if (NS_SUCCEEDED(rv)) |
|
207 mWaitingOnAsyncRedirect = true; |
|
208 return rv; |
|
209 } |
|
210 |
|
211 // By assigning mPump, we flag this channel as pending (see Pending). It's |
|
212 // important that the pending flag is set when we call into the stream (the |
|
213 // call to AsyncRead results in the stream's AsyncWait method being called) |
|
214 // and especially when we call into the loadgroup. Our caller takes care to |
|
215 // release mPump if we return an error. |
|
216 |
|
217 rv = nsInputStreamPump::Create(getter_AddRefs(mPump), stream, -1, -1, 0, 0, |
|
218 true); |
|
219 if (NS_SUCCEEDED(rv)) |
|
220 rv = mPump->AsyncRead(this, nullptr); |
|
221 |
|
222 return rv; |
|
223 } |
|
224 |
|
225 void |
|
226 nsBaseChannel::HandleAsyncRedirect(nsIChannel* newChannel) |
|
227 { |
|
228 NS_ASSERTION(!mPump, "Shouldn't have gotten here"); |
|
229 |
|
230 nsresult rv = mStatus; |
|
231 if (NS_SUCCEEDED(mStatus)) { |
|
232 rv = Redirect(newChannel, |
|
233 nsIChannelEventSink::REDIRECT_TEMPORARY, |
|
234 true); |
|
235 if (NS_SUCCEEDED(rv)) { |
|
236 // OnRedirectVerifyCallback will be called asynchronously |
|
237 return; |
|
238 } |
|
239 } |
|
240 |
|
241 ContinueHandleAsyncRedirect(rv); |
|
242 } |
|
243 |
|
244 void |
|
245 nsBaseChannel::ContinueHandleAsyncRedirect(nsresult result) |
|
246 { |
|
247 mWaitingOnAsyncRedirect = false; |
|
248 |
|
249 if (NS_FAILED(result)) |
|
250 Cancel(result); |
|
251 |
|
252 if (NS_FAILED(result) && mListener) { |
|
253 // Notify our consumer ourselves |
|
254 mListener->OnStartRequest(this, mListenerContext); |
|
255 mListener->OnStopRequest(this, mListenerContext, mStatus); |
|
256 ChannelDone(); |
|
257 } |
|
258 |
|
259 if (mLoadGroup) |
|
260 mLoadGroup->RemoveRequest(this, nullptr, mStatus); |
|
261 |
|
262 // Drop notification callbacks to prevent cycles. |
|
263 mCallbacks = nullptr; |
|
264 CallbacksChanged(); |
|
265 } |
|
266 |
|
267 void |
|
268 nsBaseChannel::ClassifyURI() |
|
269 { |
|
270 nsresult rv; |
|
271 |
|
272 if (mLoadFlags & LOAD_CLASSIFY_URI) { |
|
273 nsRefPtr<nsChannelClassifier> classifier = new nsChannelClassifier(); |
|
274 if (classifier) { |
|
275 rv = classifier->Start(this); |
|
276 if (NS_FAILED(rv)) { |
|
277 Cancel(rv); |
|
278 } |
|
279 } else { |
|
280 Cancel(NS_ERROR_OUT_OF_MEMORY); |
|
281 } |
|
282 } |
|
283 } |
|
284 |
|
285 //----------------------------------------------------------------------------- |
|
286 // nsBaseChannel::nsISupports |
|
287 |
|
288 NS_IMPL_ISUPPORTS_INHERITED(nsBaseChannel, |
|
289 nsHashPropertyBag, |
|
290 nsIRequest, |
|
291 nsIChannel, |
|
292 nsIInterfaceRequestor, |
|
293 nsITransportEventSink, |
|
294 nsIRequestObserver, |
|
295 nsIStreamListener, |
|
296 nsIAsyncVerifyRedirectCallback, |
|
297 nsIPrivateBrowsingChannel) |
|
298 |
|
299 //----------------------------------------------------------------------------- |
|
300 // nsBaseChannel::nsIRequest |
|
301 |
|
302 NS_IMETHODIMP |
|
303 nsBaseChannel::GetName(nsACString &result) |
|
304 { |
|
305 if (!mURI) { |
|
306 result.Truncate(); |
|
307 return NS_OK; |
|
308 } |
|
309 return mURI->GetSpec(result); |
|
310 } |
|
311 |
|
312 NS_IMETHODIMP |
|
313 nsBaseChannel::IsPending(bool *result) |
|
314 { |
|
315 *result = Pending(); |
|
316 return NS_OK; |
|
317 } |
|
318 |
|
319 NS_IMETHODIMP |
|
320 nsBaseChannel::GetStatus(nsresult *status) |
|
321 { |
|
322 if (mPump && NS_SUCCEEDED(mStatus)) { |
|
323 mPump->GetStatus(status); |
|
324 } else { |
|
325 *status = mStatus; |
|
326 } |
|
327 return NS_OK; |
|
328 } |
|
329 |
|
330 NS_IMETHODIMP |
|
331 nsBaseChannel::Cancel(nsresult status) |
|
332 { |
|
333 // Ignore redundant cancelation |
|
334 if (NS_FAILED(mStatus)) |
|
335 return NS_OK; |
|
336 |
|
337 mStatus = status; |
|
338 |
|
339 if (mPump) |
|
340 mPump->Cancel(status); |
|
341 |
|
342 return NS_OK; |
|
343 } |
|
344 |
|
345 NS_IMETHODIMP |
|
346 nsBaseChannel::Suspend() |
|
347 { |
|
348 NS_ENSURE_TRUE(mPump, NS_ERROR_NOT_INITIALIZED); |
|
349 return mPump->Suspend(); |
|
350 } |
|
351 |
|
352 NS_IMETHODIMP |
|
353 nsBaseChannel::Resume() |
|
354 { |
|
355 NS_ENSURE_TRUE(mPump, NS_ERROR_NOT_INITIALIZED); |
|
356 return mPump->Resume(); |
|
357 } |
|
358 |
|
359 NS_IMETHODIMP |
|
360 nsBaseChannel::GetLoadFlags(nsLoadFlags *aLoadFlags) |
|
361 { |
|
362 *aLoadFlags = mLoadFlags; |
|
363 return NS_OK; |
|
364 } |
|
365 |
|
366 NS_IMETHODIMP |
|
367 nsBaseChannel::SetLoadFlags(nsLoadFlags aLoadFlags) |
|
368 { |
|
369 mLoadFlags = aLoadFlags; |
|
370 return NS_OK; |
|
371 } |
|
372 |
|
373 NS_IMETHODIMP |
|
374 nsBaseChannel::GetLoadGroup(nsILoadGroup **aLoadGroup) |
|
375 { |
|
376 NS_IF_ADDREF(*aLoadGroup = mLoadGroup); |
|
377 return NS_OK; |
|
378 } |
|
379 |
|
380 NS_IMETHODIMP |
|
381 nsBaseChannel::SetLoadGroup(nsILoadGroup *aLoadGroup) |
|
382 { |
|
383 if (!CanSetLoadGroup(aLoadGroup)) { |
|
384 return NS_ERROR_FAILURE; |
|
385 } |
|
386 |
|
387 mLoadGroup = aLoadGroup; |
|
388 CallbacksChanged(); |
|
389 return NS_OK; |
|
390 } |
|
391 |
|
392 //----------------------------------------------------------------------------- |
|
393 // nsBaseChannel::nsIChannel |
|
394 |
|
395 NS_IMETHODIMP |
|
396 nsBaseChannel::GetOriginalURI(nsIURI **aURI) |
|
397 { |
|
398 *aURI = OriginalURI(); |
|
399 NS_ADDREF(*aURI); |
|
400 return NS_OK; |
|
401 } |
|
402 |
|
403 NS_IMETHODIMP |
|
404 nsBaseChannel::SetOriginalURI(nsIURI *aURI) |
|
405 { |
|
406 NS_ENSURE_ARG_POINTER(aURI); |
|
407 mOriginalURI = aURI; |
|
408 return NS_OK; |
|
409 } |
|
410 |
|
411 NS_IMETHODIMP |
|
412 nsBaseChannel::GetURI(nsIURI **aURI) |
|
413 { |
|
414 NS_IF_ADDREF(*aURI = mURI); |
|
415 return NS_OK; |
|
416 } |
|
417 |
|
418 NS_IMETHODIMP |
|
419 nsBaseChannel::GetOwner(nsISupports **aOwner) |
|
420 { |
|
421 NS_IF_ADDREF(*aOwner = mOwner); |
|
422 return NS_OK; |
|
423 } |
|
424 |
|
425 NS_IMETHODIMP |
|
426 nsBaseChannel::SetOwner(nsISupports *aOwner) |
|
427 { |
|
428 mOwner = aOwner; |
|
429 return NS_OK; |
|
430 } |
|
431 |
|
432 NS_IMETHODIMP |
|
433 nsBaseChannel::GetNotificationCallbacks(nsIInterfaceRequestor **aCallbacks) |
|
434 { |
|
435 NS_IF_ADDREF(*aCallbacks = mCallbacks); |
|
436 return NS_OK; |
|
437 } |
|
438 |
|
439 NS_IMETHODIMP |
|
440 nsBaseChannel::SetNotificationCallbacks(nsIInterfaceRequestor *aCallbacks) |
|
441 { |
|
442 if (!CanSetCallbacks(aCallbacks)) { |
|
443 return NS_ERROR_FAILURE; |
|
444 } |
|
445 |
|
446 mCallbacks = aCallbacks; |
|
447 CallbacksChanged(); |
|
448 return NS_OK; |
|
449 } |
|
450 |
|
451 NS_IMETHODIMP |
|
452 nsBaseChannel::GetSecurityInfo(nsISupports **aSecurityInfo) |
|
453 { |
|
454 NS_IF_ADDREF(*aSecurityInfo = mSecurityInfo); |
|
455 return NS_OK; |
|
456 } |
|
457 |
|
458 NS_IMETHODIMP |
|
459 nsBaseChannel::GetContentType(nsACString &aContentType) |
|
460 { |
|
461 aContentType = mContentType; |
|
462 return NS_OK; |
|
463 } |
|
464 |
|
465 NS_IMETHODIMP |
|
466 nsBaseChannel::SetContentType(const nsACString &aContentType) |
|
467 { |
|
468 // mContentCharset is unchanged if not parsed |
|
469 bool dummy; |
|
470 net_ParseContentType(aContentType, mContentType, mContentCharset, &dummy); |
|
471 return NS_OK; |
|
472 } |
|
473 |
|
474 NS_IMETHODIMP |
|
475 nsBaseChannel::GetContentCharset(nsACString &aContentCharset) |
|
476 { |
|
477 aContentCharset = mContentCharset; |
|
478 return NS_OK; |
|
479 } |
|
480 |
|
481 NS_IMETHODIMP |
|
482 nsBaseChannel::SetContentCharset(const nsACString &aContentCharset) |
|
483 { |
|
484 mContentCharset = aContentCharset; |
|
485 return NS_OK; |
|
486 } |
|
487 |
|
488 NS_IMETHODIMP |
|
489 nsBaseChannel::GetContentDisposition(uint32_t *aContentDisposition) |
|
490 { |
|
491 // preserve old behavior, fail unless explicitly set. |
|
492 if (mContentDispositionHint == UINT32_MAX) { |
|
493 return NS_ERROR_NOT_AVAILABLE; |
|
494 } |
|
495 |
|
496 *aContentDisposition = mContentDispositionHint; |
|
497 return NS_OK; |
|
498 } |
|
499 |
|
500 NS_IMETHODIMP |
|
501 nsBaseChannel::SetContentDisposition(uint32_t aContentDisposition) |
|
502 { |
|
503 mContentDispositionHint = aContentDisposition; |
|
504 return NS_OK; |
|
505 } |
|
506 |
|
507 NS_IMETHODIMP |
|
508 nsBaseChannel::GetContentDispositionFilename(nsAString &aContentDispositionFilename) |
|
509 { |
|
510 if (!mContentDispositionFilename) { |
|
511 return NS_ERROR_NOT_AVAILABLE; |
|
512 } |
|
513 |
|
514 aContentDispositionFilename = *mContentDispositionFilename; |
|
515 return NS_OK; |
|
516 } |
|
517 |
|
518 NS_IMETHODIMP |
|
519 nsBaseChannel::SetContentDispositionFilename(const nsAString &aContentDispositionFilename) |
|
520 { |
|
521 mContentDispositionFilename = new nsString(aContentDispositionFilename); |
|
522 return NS_OK; |
|
523 } |
|
524 |
|
525 NS_IMETHODIMP |
|
526 nsBaseChannel::GetContentDispositionHeader(nsACString &aContentDispositionHeader) |
|
527 { |
|
528 return NS_ERROR_NOT_AVAILABLE; |
|
529 } |
|
530 |
|
531 NS_IMETHODIMP |
|
532 nsBaseChannel::GetContentLength(int64_t *aContentLength) |
|
533 { |
|
534 *aContentLength = mContentLength; |
|
535 return NS_OK; |
|
536 } |
|
537 |
|
538 NS_IMETHODIMP |
|
539 nsBaseChannel::SetContentLength(int64_t aContentLength) |
|
540 { |
|
541 mContentLength = aContentLength; |
|
542 return NS_OK; |
|
543 } |
|
544 |
|
545 NS_IMETHODIMP |
|
546 nsBaseChannel::Open(nsIInputStream **result) |
|
547 { |
|
548 NS_ENSURE_TRUE(mURI, NS_ERROR_NOT_INITIALIZED); |
|
549 NS_ENSURE_TRUE(!mPump, NS_ERROR_IN_PROGRESS); |
|
550 NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_IN_PROGRESS); |
|
551 |
|
552 nsCOMPtr<nsIChannel> chan; |
|
553 nsresult rv = OpenContentStream(false, result, getter_AddRefs(chan)); |
|
554 NS_ASSERTION(!chan || !*result, "Got both a channel and a stream?"); |
|
555 if (NS_SUCCEEDED(rv) && chan) { |
|
556 rv = Redirect(chan, nsIChannelEventSink::REDIRECT_INTERNAL, false); |
|
557 if (NS_FAILED(rv)) |
|
558 return rv; |
|
559 rv = chan->Open(result); |
|
560 } else if (rv == NS_ERROR_NOT_IMPLEMENTED) |
|
561 return NS_ImplementChannelOpen(this, result); |
|
562 |
|
563 if (NS_SUCCEEDED(rv)) { |
|
564 mWasOpened = true; |
|
565 ClassifyURI(); |
|
566 } |
|
567 |
|
568 return rv; |
|
569 } |
|
570 |
|
571 NS_IMETHODIMP |
|
572 nsBaseChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctxt) |
|
573 { |
|
574 NS_ENSURE_TRUE(mURI, NS_ERROR_NOT_INITIALIZED); |
|
575 NS_ENSURE_TRUE(!mPump, NS_ERROR_IN_PROGRESS); |
|
576 NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED); |
|
577 NS_ENSURE_ARG(listener); |
|
578 |
|
579 // Ensure that this is an allowed port before proceeding. |
|
580 nsresult rv = NS_CheckPortSafety(mURI); |
|
581 if (NS_FAILED(rv)) { |
|
582 mCallbacks = nullptr; |
|
583 return rv; |
|
584 } |
|
585 |
|
586 // Store the listener and context early so that OpenContentStream and the |
|
587 // stream's AsyncWait method (called by AsyncRead) can have access to them |
|
588 // via PushStreamConverter and the StreamListener methods. However, since |
|
589 // this typically introduces a reference cycle between this and the listener, |
|
590 // we need to be sure to break the reference if this method does not succeed. |
|
591 mListener = listener; |
|
592 mListenerContext = ctxt; |
|
593 |
|
594 // This method assigns mPump as a side-effect. We need to clear mPump if |
|
595 // this method fails. |
|
596 rv = BeginPumpingData(); |
|
597 if (NS_FAILED(rv)) { |
|
598 mPump = nullptr; |
|
599 ChannelDone(); |
|
600 mCallbacks = nullptr; |
|
601 return rv; |
|
602 } |
|
603 |
|
604 // At this point, we are going to return success no matter what. |
|
605 |
|
606 mWasOpened = true; |
|
607 |
|
608 SUSPEND_PUMP_FOR_SCOPE(); |
|
609 |
|
610 if (mLoadGroup) |
|
611 mLoadGroup->AddRequest(this, nullptr); |
|
612 |
|
613 ClassifyURI(); |
|
614 |
|
615 return NS_OK; |
|
616 } |
|
617 |
|
618 //----------------------------------------------------------------------------- |
|
619 // nsBaseChannel::nsITransportEventSink |
|
620 |
|
621 NS_IMETHODIMP |
|
622 nsBaseChannel::OnTransportStatus(nsITransport *transport, nsresult status, |
|
623 uint64_t progress, uint64_t progressMax) |
|
624 { |
|
625 // In some cases, we may wish to suppress transport-layer status events. |
|
626 |
|
627 if (!mPump || NS_FAILED(mStatus) || HasLoadFlag(LOAD_BACKGROUND)) |
|
628 return NS_OK; |
|
629 |
|
630 SUSPEND_PUMP_FOR_SCOPE(); |
|
631 |
|
632 // Lazily fetch mProgressSink |
|
633 if (!mProgressSink) { |
|
634 if (mQueriedProgressSink) |
|
635 return NS_OK; |
|
636 GetCallback(mProgressSink); |
|
637 mQueriedProgressSink = true; |
|
638 if (!mProgressSink) |
|
639 return NS_OK; |
|
640 } |
|
641 |
|
642 nsAutoString statusArg; |
|
643 if (GetStatusArg(status, statusArg)) |
|
644 mProgressSink->OnStatus(this, mListenerContext, status, statusArg.get()); |
|
645 |
|
646 if (progress) |
|
647 mProgressSink->OnProgress(this, mListenerContext, progress, progressMax); |
|
648 |
|
649 return NS_OK; |
|
650 } |
|
651 |
|
652 //----------------------------------------------------------------------------- |
|
653 // nsBaseChannel::nsIInterfaceRequestor |
|
654 |
|
655 NS_IMETHODIMP |
|
656 nsBaseChannel::GetInterface(const nsIID &iid, void **result) |
|
657 { |
|
658 NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup, iid, result); |
|
659 return *result ? NS_OK : NS_ERROR_NO_INTERFACE; |
|
660 } |
|
661 |
|
662 //----------------------------------------------------------------------------- |
|
663 // nsBaseChannel::nsIRequestObserver |
|
664 |
|
665 static void |
|
666 CallTypeSniffers(void *aClosure, const uint8_t *aData, uint32_t aCount) |
|
667 { |
|
668 nsIChannel *chan = static_cast<nsIChannel*>(aClosure); |
|
669 |
|
670 nsAutoCString newType; |
|
671 NS_SniffContent(NS_CONTENT_SNIFFER_CATEGORY, chan, aData, aCount, newType); |
|
672 if (!newType.IsEmpty()) { |
|
673 chan->SetContentType(newType); |
|
674 } |
|
675 } |
|
676 |
|
677 static void |
|
678 CallUnknownTypeSniffer(void *aClosure, const uint8_t *aData, uint32_t aCount) |
|
679 { |
|
680 nsIChannel *chan = static_cast<nsIChannel*>(aClosure); |
|
681 |
|
682 nsCOMPtr<nsIContentSniffer> sniffer = |
|
683 do_CreateInstance(NS_GENERIC_CONTENT_SNIFFER); |
|
684 if (!sniffer) |
|
685 return; |
|
686 |
|
687 nsAutoCString detected; |
|
688 nsresult rv = sniffer->GetMIMETypeFromContent(chan, aData, aCount, detected); |
|
689 if (NS_SUCCEEDED(rv)) |
|
690 chan->SetContentType(detected); |
|
691 } |
|
692 |
|
693 NS_IMETHODIMP |
|
694 nsBaseChannel::OnStartRequest(nsIRequest *request, nsISupports *ctxt) |
|
695 { |
|
696 // If our content type is unknown or if the content type is |
|
697 // application/octet-stream and the caller requested it, use the content type |
|
698 // sniffer. If the sniffer is not available for some reason, then we just keep |
|
699 // going as-is. |
|
700 bool shouldSniff = mContentType.EqualsLiteral(UNKNOWN_CONTENT_TYPE) || |
|
701 ((mLoadFlags & LOAD_TREAT_APPLICATION_OCTET_STREAM_AS_UNKNOWN) && |
|
702 mContentType.EqualsLiteral(APPLICATION_OCTET_STREAM)); |
|
703 |
|
704 if (NS_SUCCEEDED(mStatus) && shouldSniff) { |
|
705 mPump->PeekStream(CallUnknownTypeSniffer, static_cast<nsIChannel*>(this)); |
|
706 } |
|
707 |
|
708 // Now, the general type sniffers. Skip this if we have none. |
|
709 if (mLoadFlags & LOAD_CALL_CONTENT_SNIFFERS) |
|
710 mPump->PeekStream(CallTypeSniffers, static_cast<nsIChannel*>(this)); |
|
711 |
|
712 SUSPEND_PUMP_FOR_SCOPE(); |
|
713 |
|
714 if (mListener) // null in case of redirect |
|
715 return mListener->OnStartRequest(this, mListenerContext); |
|
716 return NS_OK; |
|
717 } |
|
718 |
|
719 NS_IMETHODIMP |
|
720 nsBaseChannel::OnStopRequest(nsIRequest *request, nsISupports *ctxt, |
|
721 nsresult status) |
|
722 { |
|
723 // If both mStatus and status are failure codes, we keep mStatus as-is since |
|
724 // that is consistent with our GetStatus and Cancel methods. |
|
725 if (NS_SUCCEEDED(mStatus)) |
|
726 mStatus = status; |
|
727 |
|
728 // Cause Pending to return false. |
|
729 mPump = nullptr; |
|
730 |
|
731 if (mListener) // null in case of redirect |
|
732 mListener->OnStopRequest(this, mListenerContext, mStatus); |
|
733 ChannelDone(); |
|
734 |
|
735 // No need to suspend pump in this scope since we will not be receiving |
|
736 // any more events from it. |
|
737 |
|
738 if (mLoadGroup) |
|
739 mLoadGroup->RemoveRequest(this, nullptr, mStatus); |
|
740 |
|
741 // Drop notification callbacks to prevent cycles. |
|
742 mCallbacks = nullptr; |
|
743 CallbacksChanged(); |
|
744 |
|
745 return NS_OK; |
|
746 } |
|
747 |
|
748 //----------------------------------------------------------------------------- |
|
749 // nsBaseChannel::nsIStreamListener |
|
750 |
|
751 NS_IMETHODIMP |
|
752 nsBaseChannel::OnDataAvailable(nsIRequest *request, nsISupports *ctxt, |
|
753 nsIInputStream *stream, uint64_t offset, |
|
754 uint32_t count) |
|
755 { |
|
756 SUSPEND_PUMP_FOR_SCOPE(); |
|
757 |
|
758 nsresult rv = mListener->OnDataAvailable(this, mListenerContext, stream, |
|
759 offset, count); |
|
760 if (mSynthProgressEvents && NS_SUCCEEDED(rv)) { |
|
761 uint64_t prog = offset + count; |
|
762 OnTransportStatus(nullptr, NS_NET_STATUS_READING, prog, mContentLength); |
|
763 } |
|
764 |
|
765 return rv; |
|
766 } |
|
767 |
|
768 NS_IMETHODIMP |
|
769 nsBaseChannel::OnRedirectVerifyCallback(nsresult result) |
|
770 { |
|
771 if (NS_SUCCEEDED(result)) |
|
772 result = ContinueRedirect(); |
|
773 |
|
774 if (NS_FAILED(result) && !mWaitingOnAsyncRedirect) { |
|
775 if (NS_SUCCEEDED(mStatus)) |
|
776 mStatus = result; |
|
777 return NS_OK; |
|
778 } |
|
779 |
|
780 if (mWaitingOnAsyncRedirect) |
|
781 ContinueHandleAsyncRedirect(result); |
|
782 |
|
783 return NS_OK; |
|
784 } |