netwerk/base/src/nsBaseChannel.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial