uriloader/base/nsDocLoader.cpp

Thu, 15 Jan 2015 15:59:08 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:59:08 +0100
branch
TOR_BUG_9701
changeset 10
ac0c01689b40
permissions
-rw-r--r--

Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
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 "nspr.h"
michael@0 7 #include "prlog.h"
michael@0 8
michael@0 9 #include "nsDocLoader.h"
michael@0 10 #include "nsCURILoader.h"
michael@0 11 #include "nsNetUtil.h"
michael@0 12 #include "nsIHttpChannel.h"
michael@0 13 #include "nsIWebProgressListener2.h"
michael@0 14
michael@0 15 #include "nsIServiceManager.h"
michael@0 16 #include "nsXPIDLString.h"
michael@0 17
michael@0 18 #include "nsIURL.h"
michael@0 19 #include "nsCOMPtr.h"
michael@0 20 #include "nscore.h"
michael@0 21 #include "nsWeakPtr.h"
michael@0 22 #include "nsAutoPtr.h"
michael@0 23
michael@0 24 #include "nsIDOMWindow.h"
michael@0 25
michael@0 26 #include "nsIStringBundle.h"
michael@0 27 #include "nsIScriptSecurityManager.h"
michael@0 28
michael@0 29 #include "nsITransport.h"
michael@0 30 #include "nsISocketTransport.h"
michael@0 31
michael@0 32 #include "nsIDOMDocument.h"
michael@0 33 #include "nsIDocument.h"
michael@0 34 #include "nsPresContext.h"
michael@0 35 #include "nsIAsyncVerifyRedirectCallback.h"
michael@0 36
michael@0 37 static NS_DEFINE_CID(kThisImplCID, NS_THIS_DOCLOADER_IMPL_CID);
michael@0 38
michael@0 39 #if defined(PR_LOGGING)
michael@0 40 //
michael@0 41 // Log module for nsIDocumentLoader logging...
michael@0 42 //
michael@0 43 // To enable logging (see prlog.h for full details):
michael@0 44 //
michael@0 45 // set NSPR_LOG_MODULES=DocLoader:5
michael@0 46 // set NSPR_LOG_FILE=nspr.log
michael@0 47 //
michael@0 48 // this enables PR_LOG_DEBUG level information and places all output in
michael@0 49 // the file nspr.log
michael@0 50 //
michael@0 51 PRLogModuleInfo* gDocLoaderLog = nullptr;
michael@0 52 #endif /* PR_LOGGING */
michael@0 53
michael@0 54
michael@0 55 #if defined(DEBUG)
michael@0 56 void GetURIStringFromRequest(nsIRequest* request, nsACString &name)
michael@0 57 {
michael@0 58 if (request)
michael@0 59 request->GetName(name);
michael@0 60 else
michael@0 61 name.AssignLiteral("???");
michael@0 62 }
michael@0 63 #endif /* DEBUG */
michael@0 64
michael@0 65
michael@0 66
michael@0 67 bool
michael@0 68 nsDocLoader::RequestInfoHashInitEntry(PLDHashTable* table,
michael@0 69 PLDHashEntryHdr* entry,
michael@0 70 const void* key)
michael@0 71 {
michael@0 72 // Initialize the entry with placement new
michael@0 73 new (entry) nsRequestInfo(key);
michael@0 74 return true;
michael@0 75 }
michael@0 76
michael@0 77 void
michael@0 78 nsDocLoader::RequestInfoHashClearEntry(PLDHashTable* table,
michael@0 79 PLDHashEntryHdr* entry)
michael@0 80 {
michael@0 81 nsRequestInfo* info = static_cast<nsRequestInfo *>(entry);
michael@0 82 info->~nsRequestInfo();
michael@0 83 }
michael@0 84
michael@0 85 struct nsListenerInfo {
michael@0 86 nsListenerInfo(nsIWeakReference *aListener, unsigned long aNotifyMask)
michael@0 87 : mWeakListener(aListener),
michael@0 88 mNotifyMask(aNotifyMask)
michael@0 89 {
michael@0 90 }
michael@0 91
michael@0 92 // Weak pointer for the nsIWebProgressListener...
michael@0 93 nsWeakPtr mWeakListener;
michael@0 94
michael@0 95 // Mask indicating which notifications the listener wants to receive.
michael@0 96 unsigned long mNotifyMask;
michael@0 97 };
michael@0 98
michael@0 99
michael@0 100 nsDocLoader::nsDocLoader()
michael@0 101 : mParent(nullptr),
michael@0 102 mListenerInfoList(8),
michael@0 103 mCurrentSelfProgress(0),
michael@0 104 mMaxSelfProgress(0),
michael@0 105 mCurrentTotalProgress(0),
michael@0 106 mMaxTotalProgress(0),
michael@0 107 mCompletedTotalProgress(0),
michael@0 108 mIsLoadingDocument(false),
michael@0 109 mIsRestoringDocument(false),
michael@0 110 mDontFlushLayout(false),
michael@0 111 mIsFlushingLayout(false)
michael@0 112 {
michael@0 113 #if defined(PR_LOGGING)
michael@0 114 if (nullptr == gDocLoaderLog) {
michael@0 115 gDocLoaderLog = PR_NewLogModule("DocLoader");
michael@0 116 }
michael@0 117 #endif /* PR_LOGGING */
michael@0 118
michael@0 119 static const PLDHashTableOps hash_table_ops =
michael@0 120 {
michael@0 121 PL_DHashAllocTable,
michael@0 122 PL_DHashFreeTable,
michael@0 123 PL_DHashVoidPtrKeyStub,
michael@0 124 PL_DHashMatchEntryStub,
michael@0 125 PL_DHashMoveEntryStub,
michael@0 126 RequestInfoHashClearEntry,
michael@0 127 PL_DHashFinalizeStub,
michael@0 128 RequestInfoHashInitEntry
michael@0 129 };
michael@0 130
michael@0 131 PL_DHashTableInit(&mRequestInfoHash, &hash_table_ops, nullptr,
michael@0 132 sizeof(nsRequestInfo), 16);
michael@0 133
michael@0 134 ClearInternalProgress();
michael@0 135
michael@0 136 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
michael@0 137 ("DocLoader:%p: created.\n", this));
michael@0 138 }
michael@0 139
michael@0 140 nsresult
michael@0 141 nsDocLoader::SetDocLoaderParent(nsDocLoader *aParent)
michael@0 142 {
michael@0 143 mParent = aParent;
michael@0 144 return NS_OK;
michael@0 145 }
michael@0 146
michael@0 147 nsresult
michael@0 148 nsDocLoader::Init()
michael@0 149 {
michael@0 150 if (!mRequestInfoHash.ops) {
michael@0 151 return NS_ERROR_OUT_OF_MEMORY;
michael@0 152 }
michael@0 153
michael@0 154 nsresult rv = NS_NewLoadGroup(getter_AddRefs(mLoadGroup), this);
michael@0 155 if (NS_FAILED(rv)) return rv;
michael@0 156
michael@0 157 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
michael@0 158 ("DocLoader:%p: load group %x.\n", this, mLoadGroup.get()));
michael@0 159
michael@0 160 return NS_OK;
michael@0 161 }
michael@0 162
michael@0 163 nsDocLoader::~nsDocLoader()
michael@0 164 {
michael@0 165 /*
michael@0 166 |ClearWeakReferences()| here is intended to prevent people holding weak references
michael@0 167 from re-entering this destructor since |QueryReferent()| will |AddRef()| me, and the
michael@0 168 subsequent |Release()| will try to destroy me. At this point there should be only
michael@0 169 weak references remaining (otherwise, we wouldn't be getting destroyed).
michael@0 170
michael@0 171 An alternative would be incrementing our refcount (consider it a compressed flag
michael@0 172 saying "Don't re-destroy."). I haven't yet decided which is better. [scc]
michael@0 173 */
michael@0 174 // XXXbz now that NS_IMPL_RELEASE stabilizes by setting refcount to 1, is
michael@0 175 // this needed?
michael@0 176 ClearWeakReferences();
michael@0 177
michael@0 178 Destroy();
michael@0 179
michael@0 180 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
michael@0 181 ("DocLoader:%p: deleted.\n", this));
michael@0 182
michael@0 183 if (mRequestInfoHash.ops) {
michael@0 184 PL_DHashTableFinish(&mRequestInfoHash);
michael@0 185 }
michael@0 186 }
michael@0 187
michael@0 188
michael@0 189 /*
michael@0 190 * Implementation of ISupports methods...
michael@0 191 */
michael@0 192 NS_IMPL_ADDREF(nsDocLoader)
michael@0 193 NS_IMPL_RELEASE(nsDocLoader)
michael@0 194
michael@0 195 NS_INTERFACE_MAP_BEGIN(nsDocLoader)
michael@0 196 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRequestObserver)
michael@0 197 NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
michael@0 198 NS_INTERFACE_MAP_ENTRY(nsIDocumentLoader)
michael@0 199 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
michael@0 200 NS_INTERFACE_MAP_ENTRY(nsIWebProgress)
michael@0 201 NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink)
michael@0 202 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
michael@0 203 NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
michael@0 204 NS_INTERFACE_MAP_ENTRY(nsISecurityEventSink)
michael@0 205 NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
michael@0 206 if (aIID.Equals(kThisImplCID))
michael@0 207 foundInterface = static_cast<nsIDocumentLoader *>(this);
michael@0 208 else
michael@0 209 NS_INTERFACE_MAP_END
michael@0 210
michael@0 211
michael@0 212 /*
michael@0 213 * Implementation of nsIInterfaceRequestor methods...
michael@0 214 */
michael@0 215 NS_IMETHODIMP nsDocLoader::GetInterface(const nsIID& aIID, void** aSink)
michael@0 216 {
michael@0 217 nsresult rv = NS_ERROR_NO_INTERFACE;
michael@0 218
michael@0 219 NS_ENSURE_ARG_POINTER(aSink);
michael@0 220
michael@0 221 if(aIID.Equals(NS_GET_IID(nsILoadGroup))) {
michael@0 222 *aSink = mLoadGroup;
michael@0 223 NS_IF_ADDREF((nsISupports*)*aSink);
michael@0 224 rv = NS_OK;
michael@0 225 } else {
michael@0 226 rv = QueryInterface(aIID, aSink);
michael@0 227 }
michael@0 228
michael@0 229 return rv;
michael@0 230 }
michael@0 231
michael@0 232 /* static */
michael@0 233 already_AddRefed<nsDocLoader>
michael@0 234 nsDocLoader::GetAsDocLoader(nsISupports* aSupports)
michael@0 235 {
michael@0 236 nsRefPtr<nsDocLoader> ret = do_QueryObject(aSupports);
michael@0 237 return ret.forget();
michael@0 238 }
michael@0 239
michael@0 240 /* static */
michael@0 241 nsresult
michael@0 242 nsDocLoader::AddDocLoaderAsChildOfRoot(nsDocLoader* aDocLoader)
michael@0 243 {
michael@0 244 nsresult rv;
michael@0 245 nsCOMPtr<nsIDocumentLoader> docLoaderService =
michael@0 246 do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID, &rv);
michael@0 247 NS_ENSURE_SUCCESS(rv, rv);
michael@0 248
michael@0 249 nsRefPtr<nsDocLoader> rootDocLoader = GetAsDocLoader(docLoaderService);
michael@0 250 NS_ENSURE_TRUE(rootDocLoader, NS_ERROR_UNEXPECTED);
michael@0 251
michael@0 252 return rootDocLoader->AddChildLoader(aDocLoader);
michael@0 253 }
michael@0 254
michael@0 255 NS_IMETHODIMP
michael@0 256 nsDocLoader::Stop(void)
michael@0 257 {
michael@0 258 nsresult rv = NS_OK;
michael@0 259
michael@0 260 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
michael@0 261 ("DocLoader:%p: Stop() called\n", this));
michael@0 262
michael@0 263 NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(mChildList, nsDocLoader, Stop, ());
michael@0 264
michael@0 265 if (mLoadGroup)
michael@0 266 rv = mLoadGroup->Cancel(NS_BINDING_ABORTED);
michael@0 267
michael@0 268 // Don't report that we're flushing layout so IsBusy returns false after a
michael@0 269 // Stop call.
michael@0 270 mIsFlushingLayout = false;
michael@0 271
michael@0 272 // Clear out mChildrenInOnload. We want to make sure to fire our
michael@0 273 // onload at this point, and there's no issue with mChildrenInOnload
michael@0 274 // after this, since mDocumentRequest will be null after the
michael@0 275 // DocLoaderIsEmpty() call.
michael@0 276 mChildrenInOnload.Clear();
michael@0 277
michael@0 278 // Make sure to call DocLoaderIsEmpty now so that we reset mDocumentRequest,
michael@0 279 // etc, as needed. We could be getting into here from a subframe onload, in
michael@0 280 // which case the call to DocLoaderIsEmpty() is coming but hasn't quite
michael@0 281 // happened yet, Canceling the loadgroup did nothing (because it was already
michael@0 282 // empty), and we're about to start a new load (which is what triggered this
michael@0 283 // Stop() call).
michael@0 284
michael@0 285 // XXXbz If the child frame loadgroups were requests in mLoadgroup, I suspect
michael@0 286 // we wouldn't need the call here....
michael@0 287
michael@0 288 NS_ASSERTION(!IsBusy(), "Shouldn't be busy here");
michael@0 289 DocLoaderIsEmpty(false);
michael@0 290
michael@0 291 return rv;
michael@0 292 }
michael@0 293
michael@0 294
michael@0 295 bool
michael@0 296 nsDocLoader::IsBusy()
michael@0 297 {
michael@0 298 nsresult rv;
michael@0 299
michael@0 300 //
michael@0 301 // A document loader is busy if either:
michael@0 302 //
michael@0 303 // 1. One of its children is in the middle of an onload handler. Note that
michael@0 304 // the handler may have already removed this child from mChildList!
michael@0 305 // 2. It is currently loading a document and either has parts of it still
michael@0 306 // loading, or has a busy child docloader.
michael@0 307 // 3. It's currently flushing layout in DocLoaderIsEmpty().
michael@0 308 //
michael@0 309
michael@0 310 if (mChildrenInOnload.Count() || mIsFlushingLayout) {
michael@0 311 return true;
michael@0 312 }
michael@0 313
michael@0 314 /* Is this document loader busy? */
michael@0 315 if (!mIsLoadingDocument) {
michael@0 316 return false;
michael@0 317 }
michael@0 318
michael@0 319 bool busy;
michael@0 320 rv = mLoadGroup->IsPending(&busy);
michael@0 321 if (NS_FAILED(rv)) {
michael@0 322 return false;
michael@0 323 }
michael@0 324 if (busy) {
michael@0 325 return true;
michael@0 326 }
michael@0 327
michael@0 328 /* check its child document loaders... */
michael@0 329 uint32_t count = mChildList.Length();
michael@0 330 for (uint32_t i=0; i < count; i++) {
michael@0 331 nsIDocumentLoader* loader = ChildAt(i);
michael@0 332
michael@0 333 // This is a safe cast, because we only put nsDocLoader objects into the
michael@0 334 // array
michael@0 335 if (loader && static_cast<nsDocLoader*>(loader)->IsBusy())
michael@0 336 return true;
michael@0 337 }
michael@0 338
michael@0 339 return false;
michael@0 340 }
michael@0 341
michael@0 342 NS_IMETHODIMP
michael@0 343 nsDocLoader::GetContainer(nsISupports** aResult)
michael@0 344 {
michael@0 345 NS_ADDREF(*aResult = static_cast<nsIDocumentLoader*>(this));
michael@0 346
michael@0 347 return NS_OK;
michael@0 348 }
michael@0 349
michael@0 350 NS_IMETHODIMP
michael@0 351 nsDocLoader::GetLoadGroup(nsILoadGroup** aResult)
michael@0 352 {
michael@0 353 nsresult rv = NS_OK;
michael@0 354
michael@0 355 if (nullptr == aResult) {
michael@0 356 rv = NS_ERROR_NULL_POINTER;
michael@0 357 } else {
michael@0 358 *aResult = mLoadGroup;
michael@0 359 NS_IF_ADDREF(*aResult);
michael@0 360 }
michael@0 361 return rv;
michael@0 362 }
michael@0 363
michael@0 364 void
michael@0 365 nsDocLoader::Destroy()
michael@0 366 {
michael@0 367 Stop();
michael@0 368
michael@0 369 // Remove the document loader from the parent list of loaders...
michael@0 370 if (mParent)
michael@0 371 {
michael@0 372 mParent->RemoveChildLoader(this);
michael@0 373 }
michael@0 374
michael@0 375 // Release all the information about network requests...
michael@0 376 ClearRequestInfoHash();
michael@0 377
michael@0 378 // Release all the information about registered listeners...
michael@0 379 int32_t count = mListenerInfoList.Count();
michael@0 380 for(int32_t i = 0; i < count; i++) {
michael@0 381 nsListenerInfo *info =
michael@0 382 static_cast<nsListenerInfo*>(mListenerInfoList.ElementAt(i));
michael@0 383
michael@0 384 delete info;
michael@0 385 }
michael@0 386
michael@0 387 mListenerInfoList.Clear();
michael@0 388 mListenerInfoList.Compact();
michael@0 389
michael@0 390 mDocumentRequest = 0;
michael@0 391
michael@0 392 if (mLoadGroup)
michael@0 393 mLoadGroup->SetGroupObserver(nullptr);
michael@0 394
michael@0 395 DestroyChildren();
michael@0 396 }
michael@0 397
michael@0 398 void
michael@0 399 nsDocLoader::DestroyChildren()
michael@0 400 {
michael@0 401 uint32_t count = mChildList.Length();
michael@0 402 // if the doc loader still has children...we need to enumerate the
michael@0 403 // children and make them null out their back ptr to the parent doc
michael@0 404 // loader
michael@0 405 for (uint32_t i=0; i < count; i++)
michael@0 406 {
michael@0 407 nsIDocumentLoader* loader = ChildAt(i);
michael@0 408
michael@0 409 if (loader) {
michael@0 410 // This is a safe cast, as we only put nsDocLoader objects into the
michael@0 411 // array
michael@0 412 static_cast<nsDocLoader*>(loader)->SetDocLoaderParent(nullptr);
michael@0 413 }
michael@0 414 }
michael@0 415 mChildList.Clear();
michael@0 416 }
michael@0 417
michael@0 418 NS_IMETHODIMP
michael@0 419 nsDocLoader::OnStartRequest(nsIRequest *request, nsISupports *aCtxt)
michael@0 420 {
michael@0 421 // called each time a request is added to the group.
michael@0 422
michael@0 423 #ifdef PR_LOGGING
michael@0 424 if (PR_LOG_TEST(gDocLoaderLog, PR_LOG_DEBUG)) {
michael@0 425 nsAutoCString name;
michael@0 426 request->GetName(name);
michael@0 427
michael@0 428 uint32_t count = 0;
michael@0 429 if (mLoadGroup)
michael@0 430 mLoadGroup->GetActiveCount(&count);
michael@0 431
michael@0 432 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
michael@0 433 ("DocLoader:%p: OnStartRequest[%p](%s) mIsLoadingDocument=%s, %u active URLs",
michael@0 434 this, request, name.get(),
michael@0 435 (mIsLoadingDocument ? "true" : "false"),
michael@0 436 count));
michael@0 437 }
michael@0 438 #endif /* PR_LOGGING */
michael@0 439 bool bJustStartedLoading = false;
michael@0 440
michael@0 441 nsLoadFlags loadFlags = 0;
michael@0 442 request->GetLoadFlags(&loadFlags);
michael@0 443
michael@0 444 if (!mIsLoadingDocument && (loadFlags & nsIChannel::LOAD_DOCUMENT_URI)) {
michael@0 445 bJustStartedLoading = true;
michael@0 446 mIsLoadingDocument = true;
michael@0 447 ClearInternalProgress(); // only clear our progress if we are starting a new load....
michael@0 448 }
michael@0 449
michael@0 450 //
michael@0 451 // Create a new nsRequestInfo for the request that is starting to
michael@0 452 // load...
michael@0 453 //
michael@0 454 AddRequestInfo(request);
michael@0 455
michael@0 456 //
michael@0 457 // Only fire a doStartDocumentLoad(...) if the document loader
michael@0 458 // has initiated a load... Otherwise, this notification has
michael@0 459 // resulted from a request being added to the load group.
michael@0 460 //
michael@0 461 if (mIsLoadingDocument) {
michael@0 462 if (loadFlags & nsIChannel::LOAD_DOCUMENT_URI) {
michael@0 463 //
michael@0 464 // Make sure that the document channel is null at this point...
michael@0 465 // (unless its been redirected)
michael@0 466 //
michael@0 467 NS_ASSERTION((loadFlags & nsIChannel::LOAD_REPLACE) ||
michael@0 468 !(mDocumentRequest.get()),
michael@0 469 "Overwriting an existing document channel!");
michael@0 470
michael@0 471 // This request is associated with the entire document...
michael@0 472 mDocumentRequest = request;
michael@0 473 mLoadGroup->SetDefaultLoadRequest(request);
michael@0 474
michael@0 475 // Only fire the start document load notification for the first
michael@0 476 // document URI... Do not fire it again for redirections
michael@0 477 //
michael@0 478 if (bJustStartedLoading) {
michael@0 479 // Update the progress status state
michael@0 480 mProgressStateFlags = nsIWebProgressListener::STATE_START;
michael@0 481
michael@0 482 // Fire the start document load notification
michael@0 483 doStartDocumentLoad();
michael@0 484 return NS_OK;
michael@0 485 }
michael@0 486 }
michael@0 487 }
michael@0 488
michael@0 489 NS_ASSERTION(!mIsLoadingDocument || mDocumentRequest,
michael@0 490 "mDocumentRequest MUST be set for the duration of a page load!");
michael@0 491
michael@0 492 doStartURLLoad(request);
michael@0 493
michael@0 494 return NS_OK;
michael@0 495 }
michael@0 496
michael@0 497 NS_IMETHODIMP
michael@0 498 nsDocLoader::OnStopRequest(nsIRequest *aRequest,
michael@0 499 nsISupports *aCtxt,
michael@0 500 nsresult aStatus)
michael@0 501 {
michael@0 502 nsresult rv = NS_OK;
michael@0 503
michael@0 504 #ifdef PR_LOGGING
michael@0 505 if (PR_LOG_TEST(gDocLoaderLog, PR_LOG_DEBUG)) {
michael@0 506 nsAutoCString name;
michael@0 507 aRequest->GetName(name);
michael@0 508
michael@0 509 uint32_t count = 0;
michael@0 510 if (mLoadGroup)
michael@0 511 mLoadGroup->GetActiveCount(&count);
michael@0 512
michael@0 513 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
michael@0 514 ("DocLoader:%p: OnStopRequest[%p](%s) status=%x mIsLoadingDocument=%s, %u active URLs",
michael@0 515 this, aRequest, name.get(),
michael@0 516 aStatus, (mIsLoadingDocument ? "true" : "false"),
michael@0 517 count));
michael@0 518 }
michael@0 519 #endif
michael@0 520
michael@0 521 bool bFireTransferring = false;
michael@0 522
michael@0 523 //
michael@0 524 // Set the Maximum progress to the same value as the current progress.
michael@0 525 // Since the URI has finished loading, all the data is there. Also,
michael@0 526 // this will allow a more accurate estimation of the max progress (in case
michael@0 527 // the old value was unknown ie. -1)
michael@0 528 //
michael@0 529 nsRequestInfo *info = GetRequestInfo(aRequest);
michael@0 530 if (info) {
michael@0 531 // Null out mLastStatus now so we don't find it when looking for
michael@0 532 // status from now on. This destroys the nsStatusInfo and hence
michael@0 533 // removes it from our list.
michael@0 534 info->mLastStatus = nullptr;
michael@0 535
michael@0 536 int64_t oldMax = info->mMaxProgress;
michael@0 537
michael@0 538 info->mMaxProgress = info->mCurrentProgress;
michael@0 539
michael@0 540 //
michael@0 541 // If a request whose content-length was previously unknown has just
michael@0 542 // finished loading, then use this new data to try to calculate a
michael@0 543 // mMaxSelfProgress...
michael@0 544 //
michael@0 545 if ((oldMax < int64_t(0)) && (mMaxSelfProgress < int64_t(0))) {
michael@0 546 mMaxSelfProgress = CalculateMaxProgress();
michael@0 547 }
michael@0 548
michael@0 549 // As we know the total progress of this request now, save it to be part
michael@0 550 // of CalculateMaxProgress() result. We need to remove the info from the
michael@0 551 // hash, see bug 480713.
michael@0 552 mCompletedTotalProgress += info->mMaxProgress;
michael@0 553
michael@0 554 //
michael@0 555 // Determine whether a STATE_TRANSFERRING notification should be
michael@0 556 // 'synthesized'.
michael@0 557 //
michael@0 558 // If nsRequestInfo::mMaxProgress (as stored in oldMax) and
michael@0 559 // nsRequestInfo::mCurrentProgress are both 0, then the
michael@0 560 // STATE_TRANSFERRING notification has not been fired yet...
michael@0 561 //
michael@0 562 if ((oldMax == 0) && (info->mCurrentProgress == 0)) {
michael@0 563 nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
michael@0 564
michael@0 565 // Only fire a TRANSFERRING notification if the request is also a
michael@0 566 // channel -- data transfer requires a nsIChannel!
michael@0 567 //
michael@0 568 if (channel) {
michael@0 569 if (NS_SUCCEEDED(aStatus)) {
michael@0 570 bFireTransferring = true;
michael@0 571 }
michael@0 572 //
michael@0 573 // If the request failed (for any reason other than being
michael@0 574 // redirected or retargeted), the TRANSFERRING notification can
michael@0 575 // still be fired if a HTTP connection was established to a server.
michael@0 576 //
michael@0 577 else if (aStatus != NS_BINDING_REDIRECTED &&
michael@0 578 aStatus != NS_BINDING_RETARGETED) {
michael@0 579 //
michael@0 580 // Only if the load has been targeted (see bug 268483)...
michael@0 581 //
michael@0 582 uint32_t lf;
michael@0 583 channel->GetLoadFlags(&lf);
michael@0 584 if (lf & nsIChannel::LOAD_TARGETED) {
michael@0 585 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest));
michael@0 586 if (httpChannel) {
michael@0 587 uint32_t responseCode;
michael@0 588 rv = httpChannel->GetResponseStatus(&responseCode);
michael@0 589 if (NS_SUCCEEDED(rv)) {
michael@0 590 //
michael@0 591 // A valid server status indicates that a connection was
michael@0 592 // established to the server... So, fire the notification
michael@0 593 // even though a failure occurred later...
michael@0 594 //
michael@0 595 bFireTransferring = true;
michael@0 596 }
michael@0 597 }
michael@0 598 }
michael@0 599 }
michael@0 600 }
michael@0 601 }
michael@0 602 }
michael@0 603
michael@0 604 if (bFireTransferring) {
michael@0 605 // Send a STATE_TRANSFERRING notification for the request.
michael@0 606 int32_t flags;
michael@0 607
michael@0 608 flags = nsIWebProgressListener::STATE_TRANSFERRING |
michael@0 609 nsIWebProgressListener::STATE_IS_REQUEST;
michael@0 610 //
michael@0 611 // Move the WebProgress into the STATE_TRANSFERRING state if necessary...
michael@0 612 //
michael@0 613 if (mProgressStateFlags & nsIWebProgressListener::STATE_START) {
michael@0 614 mProgressStateFlags = nsIWebProgressListener::STATE_TRANSFERRING;
michael@0 615
michael@0 616 // Send STATE_TRANSFERRING for the document too...
michael@0 617 flags |= nsIWebProgressListener::STATE_IS_DOCUMENT;
michael@0 618 }
michael@0 619
michael@0 620 FireOnStateChange(this, aRequest, flags, NS_OK);
michael@0 621 }
michael@0 622
michael@0 623 //
michael@0 624 // Fire the OnStateChange(...) notification for stop request
michael@0 625 //
michael@0 626 doStopURLLoad(aRequest, aStatus);
michael@0 627
michael@0 628 // Clear this request out of the hash to avoid bypass of FireOnStateChange
michael@0 629 // when address of the request is reused.
michael@0 630 RemoveRequestInfo(aRequest);
michael@0 631
michael@0 632 //
michael@0 633 // Only fire the DocLoaderIsEmpty(...) if the document loader has initiated a
michael@0 634 // load. This will handle removing the request from our hashtable as needed.
michael@0 635 //
michael@0 636 if (mIsLoadingDocument) {
michael@0 637 DocLoaderIsEmpty(true);
michael@0 638 }
michael@0 639
michael@0 640 return NS_OK;
michael@0 641 }
michael@0 642
michael@0 643
michael@0 644 nsresult nsDocLoader::RemoveChildLoader(nsDocLoader* aChild)
michael@0 645 {
michael@0 646 nsresult rv = mChildList.RemoveElement(aChild) ? NS_OK : NS_ERROR_FAILURE;
michael@0 647 if (NS_SUCCEEDED(rv)) {
michael@0 648 aChild->SetDocLoaderParent(nullptr);
michael@0 649 }
michael@0 650 return rv;
michael@0 651 }
michael@0 652
michael@0 653 nsresult nsDocLoader::AddChildLoader(nsDocLoader* aChild)
michael@0 654 {
michael@0 655 nsresult rv = mChildList.AppendElement(aChild) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
michael@0 656 if (NS_SUCCEEDED(rv)) {
michael@0 657 aChild->SetDocLoaderParent(this);
michael@0 658 }
michael@0 659 return rv;
michael@0 660 }
michael@0 661
michael@0 662 NS_IMETHODIMP nsDocLoader::GetDocumentChannel(nsIChannel ** aChannel)
michael@0 663 {
michael@0 664 if (!mDocumentRequest) {
michael@0 665 *aChannel = nullptr;
michael@0 666 return NS_OK;
michael@0 667 }
michael@0 668
michael@0 669 return CallQueryInterface(mDocumentRequest, aChannel);
michael@0 670 }
michael@0 671
michael@0 672
michael@0 673 void nsDocLoader::DocLoaderIsEmpty(bool aFlushLayout)
michael@0 674 {
michael@0 675 if (mIsLoadingDocument) {
michael@0 676 /* In the unimagineably rude circumstance that onload event handlers
michael@0 677 triggered by this function actually kill the window ... ok, it's
michael@0 678 not unimagineable; it's happened ... this deathgrip keeps this object
michael@0 679 alive long enough to survive this function call. */
michael@0 680 nsCOMPtr<nsIDocumentLoader> kungFuDeathGrip(this);
michael@0 681
michael@0 682 // Don't flush layout if we're still busy.
michael@0 683 if (IsBusy()) {
michael@0 684 return;
michael@0 685 }
michael@0 686
michael@0 687 NS_ASSERTION(!mIsFlushingLayout, "Someone screwed up");
michael@0 688
michael@0 689 // The load group for this DocumentLoader is idle. Flush if we need to.
michael@0 690 if (aFlushLayout && !mDontFlushLayout) {
michael@0 691 nsCOMPtr<nsIDOMDocument> domDoc = do_GetInterface(GetAsSupports(this));
michael@0 692 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
michael@0 693 if (doc) {
michael@0 694 // We start loads from style resolution, so we need to flush out style
michael@0 695 // no matter what. If we have user fonts, we also need to flush layout,
michael@0 696 // since the reflow is what starts font loads.
michael@0 697 mozFlushType flushType = Flush_Style;
michael@0 698 nsIPresShell* shell = doc->GetShell();
michael@0 699 if (shell) {
michael@0 700 // Be safe in case this presshell is in teardown now
michael@0 701 nsPresContext* presContext = shell->GetPresContext();
michael@0 702 if (presContext && presContext->GetUserFontSet()) {
michael@0 703 flushType = Flush_Layout;
michael@0 704 }
michael@0 705 }
michael@0 706 mDontFlushLayout = mIsFlushingLayout = true;
michael@0 707 doc->FlushPendingNotifications(flushType);
michael@0 708 mDontFlushLayout = mIsFlushingLayout = false;
michael@0 709 }
michael@0 710 }
michael@0 711
michael@0 712 // And now check whether we're really busy; that might have changed with
michael@0 713 // the layout flush.
michael@0 714 if (!IsBusy()) {
michael@0 715 // Clear out our request info hash, now that our load really is done and
michael@0 716 // we don't need it anymore to CalculateMaxProgress().
michael@0 717 ClearInternalProgress();
michael@0 718
michael@0 719 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
michael@0 720 ("DocLoader:%p: Is now idle...\n", this));
michael@0 721
michael@0 722 nsCOMPtr<nsIRequest> docRequest = mDocumentRequest;
michael@0 723
michael@0 724 NS_ASSERTION(mDocumentRequest, "No Document Request!");
michael@0 725 mDocumentRequest = 0;
michael@0 726 mIsLoadingDocument = false;
michael@0 727
michael@0 728 // Update the progress status state - the document is done
michael@0 729 mProgressStateFlags = nsIWebProgressListener::STATE_STOP;
michael@0 730
michael@0 731
michael@0 732 nsresult loadGroupStatus = NS_OK;
michael@0 733 mLoadGroup->GetStatus(&loadGroupStatus);
michael@0 734
michael@0 735 //
michael@0 736 // New code to break the circular reference between
michael@0 737 // the load group and the docloader...
michael@0 738 //
michael@0 739 mLoadGroup->SetDefaultLoadRequest(nullptr);
michael@0 740
michael@0 741 // Take a ref to our parent now so that we can call DocLoaderIsEmpty() on
michael@0 742 // it even if our onload handler removes us from the docloader tree.
michael@0 743 nsRefPtr<nsDocLoader> parent = mParent;
michael@0 744
michael@0 745 // Note that if calling ChildEnteringOnload() on the parent returns false
michael@0 746 // then calling our onload handler is not safe. That can only happen on
michael@0 747 // OOM, so that's ok.
michael@0 748 if (!parent || parent->ChildEnteringOnload(this)) {
michael@0 749 // Do nothing with our state after firing the
michael@0 750 // OnEndDocumentLoad(...). The document loader may be loading a *new*
michael@0 751 // document - if LoadDocument() was called from a handler!
michael@0 752 //
michael@0 753 doStopDocumentLoad(docRequest, loadGroupStatus);
michael@0 754
michael@0 755 if (parent) {
michael@0 756 parent->ChildDoneWithOnload(this);
michael@0 757 }
michael@0 758 }
michael@0 759 }
michael@0 760 }
michael@0 761 }
michael@0 762
michael@0 763 void nsDocLoader::doStartDocumentLoad(void)
michael@0 764 {
michael@0 765
michael@0 766 #if defined(DEBUG)
michael@0 767 nsAutoCString buffer;
michael@0 768
michael@0 769 GetURIStringFromRequest(mDocumentRequest, buffer);
michael@0 770 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
michael@0 771 ("DocLoader:%p: ++ Firing OnStateChange for start document load (...)."
michael@0 772 "\tURI: %s \n",
michael@0 773 this, buffer.get()));
michael@0 774 #endif /* DEBUG */
michael@0 775
michael@0 776 // Fire an OnStatus(...) notification STATE_START. This indicates
michael@0 777 // that the document represented by mDocumentRequest has started to
michael@0 778 // load...
michael@0 779 FireOnStateChange(this,
michael@0 780 mDocumentRequest,
michael@0 781 nsIWebProgressListener::STATE_START |
michael@0 782 nsIWebProgressListener::STATE_IS_DOCUMENT |
michael@0 783 nsIWebProgressListener::STATE_IS_REQUEST |
michael@0 784 nsIWebProgressListener::STATE_IS_WINDOW |
michael@0 785 nsIWebProgressListener::STATE_IS_NETWORK,
michael@0 786 NS_OK);
michael@0 787 }
michael@0 788
michael@0 789 void nsDocLoader::doStartURLLoad(nsIRequest *request)
michael@0 790 {
michael@0 791 #if defined(DEBUG)
michael@0 792 nsAutoCString buffer;
michael@0 793
michael@0 794 GetURIStringFromRequest(request, buffer);
michael@0 795 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
michael@0 796 ("DocLoader:%p: ++ Firing OnStateChange start url load (...)."
michael@0 797 "\tURI: %s\n",
michael@0 798 this, buffer.get()));
michael@0 799 #endif /* DEBUG */
michael@0 800
michael@0 801 FireOnStateChange(this,
michael@0 802 request,
michael@0 803 nsIWebProgressListener::STATE_START |
michael@0 804 nsIWebProgressListener::STATE_IS_REQUEST,
michael@0 805 NS_OK);
michael@0 806 }
michael@0 807
michael@0 808 void nsDocLoader::doStopURLLoad(nsIRequest *request, nsresult aStatus)
michael@0 809 {
michael@0 810 #if defined(DEBUG)
michael@0 811 nsAutoCString buffer;
michael@0 812
michael@0 813 GetURIStringFromRequest(request, buffer);
michael@0 814 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
michael@0 815 ("DocLoader:%p: ++ Firing OnStateChange for end url load (...)."
michael@0 816 "\tURI: %s status=%x\n",
michael@0 817 this, buffer.get(), aStatus));
michael@0 818 #endif /* DEBUG */
michael@0 819
michael@0 820 FireOnStateChange(this,
michael@0 821 request,
michael@0 822 nsIWebProgressListener::STATE_STOP |
michael@0 823 nsIWebProgressListener::STATE_IS_REQUEST,
michael@0 824 aStatus);
michael@0 825
michael@0 826 // Fire a status change message for the most recent unfinished
michael@0 827 // request to make sure that the displayed status is not outdated.
michael@0 828 if (!mStatusInfoList.isEmpty()) {
michael@0 829 nsStatusInfo* statusInfo = mStatusInfoList.getFirst();
michael@0 830 FireOnStatusChange(this, statusInfo->mRequest,
michael@0 831 statusInfo->mStatusCode,
michael@0 832 statusInfo->mStatusMessage.get());
michael@0 833 }
michael@0 834 }
michael@0 835
michael@0 836 void nsDocLoader::doStopDocumentLoad(nsIRequest *request,
michael@0 837 nsresult aStatus)
michael@0 838 {
michael@0 839 #if defined(DEBUG)
michael@0 840 nsAutoCString buffer;
michael@0 841
michael@0 842 GetURIStringFromRequest(request, buffer);
michael@0 843 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
michael@0 844 ("DocLoader:%p: ++ Firing OnStateChange for end document load (...)."
michael@0 845 "\tURI: %s Status=%x\n",
michael@0 846 this, buffer.get(), aStatus));
michael@0 847 #endif /* DEBUG */
michael@0 848
michael@0 849 // Firing STATE_STOP|STATE_IS_DOCUMENT will fire onload handlers.
michael@0 850 // Grab our parent chain before doing that so we can still dispatch
michael@0 851 // STATE_STOP|STATE_IS_WINDW_STATE_IS_NETWORK to them all, even if
michael@0 852 // the onload handlers rearrange the docshell tree.
michael@0 853 WebProgressList list;
michael@0 854 GatherAncestorWebProgresses(list);
michael@0 855
michael@0 856 //
michael@0 857 // Fire an OnStateChange(...) notification indicating the the
michael@0 858 // current document has finished loading...
michael@0 859 //
michael@0 860 int32_t flags = nsIWebProgressListener::STATE_STOP |
michael@0 861 nsIWebProgressListener::STATE_IS_DOCUMENT;
michael@0 862 for (uint32_t i = 0; i < list.Length(); ++i) {
michael@0 863 list[i]->DoFireOnStateChange(this, request, flags, aStatus);
michael@0 864 }
michael@0 865
michael@0 866 //
michael@0 867 // Fire a final OnStateChange(...) notification indicating the the
michael@0 868 // current document has finished loading...
michael@0 869 //
michael@0 870 flags = nsIWebProgressListener::STATE_STOP |
michael@0 871 nsIWebProgressListener::STATE_IS_WINDOW |
michael@0 872 nsIWebProgressListener::STATE_IS_NETWORK;
michael@0 873 for (uint32_t i = 0; i < list.Length(); ++i) {
michael@0 874 list[i]->DoFireOnStateChange(this, request, flags, aStatus);
michael@0 875 }
michael@0 876 }
michael@0 877
michael@0 878 ////////////////////////////////////////////////////////////////////////////////////
michael@0 879 // The following section contains support for nsIWebProgress and related stuff
michael@0 880 ////////////////////////////////////////////////////////////////////////////////////
michael@0 881
michael@0 882 NS_IMETHODIMP
michael@0 883 nsDocLoader::AddProgressListener(nsIWebProgressListener *aListener,
michael@0 884 uint32_t aNotifyMask)
michael@0 885 {
michael@0 886 nsresult rv;
michael@0 887
michael@0 888 nsListenerInfo* info = GetListenerInfo(aListener);
michael@0 889 if (info) {
michael@0 890 // The listener is already registered!
michael@0 891 return NS_ERROR_FAILURE;
michael@0 892 }
michael@0 893
michael@0 894 nsWeakPtr listener = do_GetWeakReference(aListener);
michael@0 895 if (!listener) {
michael@0 896 return NS_ERROR_INVALID_ARG;
michael@0 897 }
michael@0 898
michael@0 899 info = new nsListenerInfo(listener, aNotifyMask);
michael@0 900 if (!info) {
michael@0 901 return NS_ERROR_OUT_OF_MEMORY;
michael@0 902 }
michael@0 903
michael@0 904 rv = mListenerInfoList.AppendElement(info) ? NS_OK : NS_ERROR_FAILURE;
michael@0 905 return rv;
michael@0 906 }
michael@0 907
michael@0 908 NS_IMETHODIMP
michael@0 909 nsDocLoader::RemoveProgressListener(nsIWebProgressListener *aListener)
michael@0 910 {
michael@0 911 nsresult rv;
michael@0 912
michael@0 913 nsListenerInfo* info = GetListenerInfo(aListener);
michael@0 914 if (info) {
michael@0 915 rv = mListenerInfoList.RemoveElement(info) ? NS_OK : NS_ERROR_FAILURE;
michael@0 916 delete info;
michael@0 917 } else {
michael@0 918 // The listener is not registered!
michael@0 919 rv = NS_ERROR_FAILURE;
michael@0 920 }
michael@0 921 return rv;
michael@0 922 }
michael@0 923
michael@0 924 NS_IMETHODIMP
michael@0 925 nsDocLoader::GetDOMWindow(nsIDOMWindow **aResult)
michael@0 926 {
michael@0 927 return CallGetInterface(this, aResult);
michael@0 928 }
michael@0 929
michael@0 930 NS_IMETHODIMP
michael@0 931 nsDocLoader::GetDOMWindowID(uint64_t *aResult)
michael@0 932 {
michael@0 933 *aResult = 0;
michael@0 934
michael@0 935 nsCOMPtr<nsIDOMWindow> window;
michael@0 936 nsresult rv = GetDOMWindow(getter_AddRefs(window));
michael@0 937 NS_ENSURE_SUCCESS(rv, rv);
michael@0 938
michael@0 939 nsCOMPtr<nsPIDOMWindow> piwindow = do_QueryInterface(window);
michael@0 940 NS_ENSURE_STATE(piwindow);
michael@0 941
michael@0 942 MOZ_ASSERT(piwindow->IsOuterWindow());
michael@0 943 *aResult = piwindow->WindowID();
michael@0 944 return NS_OK;
michael@0 945 }
michael@0 946
michael@0 947 NS_IMETHODIMP
michael@0 948 nsDocLoader::GetIsTopLevel(bool *aResult)
michael@0 949 {
michael@0 950 *aResult = false;
michael@0 951
michael@0 952 nsCOMPtr<nsIDOMWindow> window;
michael@0 953 GetDOMWindow(getter_AddRefs(window));
michael@0 954 if (window) {
michael@0 955 nsCOMPtr<nsPIDOMWindow> piwindow = do_QueryInterface(window);
michael@0 956 NS_ENSURE_STATE(piwindow);
michael@0 957
michael@0 958 nsCOMPtr<nsIDOMWindow> topWindow;
michael@0 959 nsresult rv = piwindow->GetTop(getter_AddRefs(topWindow));
michael@0 960 NS_ENSURE_SUCCESS(rv, rv);
michael@0 961
michael@0 962 *aResult = piwindow == topWindow;
michael@0 963 }
michael@0 964
michael@0 965 return NS_OK;
michael@0 966 }
michael@0 967
michael@0 968 NS_IMETHODIMP
michael@0 969 nsDocLoader::GetIsLoadingDocument(bool *aIsLoadingDocument)
michael@0 970 {
michael@0 971 *aIsLoadingDocument = mIsLoadingDocument;
michael@0 972
michael@0 973 return NS_OK;
michael@0 974 }
michael@0 975
michael@0 976 NS_IMETHODIMP
michael@0 977 nsDocLoader::GetLoadType(uint32_t *aLoadType)
michael@0 978 {
michael@0 979 *aLoadType = 0;
michael@0 980
michael@0 981 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 982 }
michael@0 983
michael@0 984 int64_t nsDocLoader::GetMaxTotalProgress()
michael@0 985 {
michael@0 986 int64_t newMaxTotal = 0;
michael@0 987
michael@0 988 uint32_t count = mChildList.Length();
michael@0 989 nsCOMPtr<nsIWebProgress> webProgress;
michael@0 990 for (uint32_t i=0; i < count; i++)
michael@0 991 {
michael@0 992 int64_t individualProgress = 0;
michael@0 993 nsIDocumentLoader* docloader = ChildAt(i);
michael@0 994 if (docloader)
michael@0 995 {
michael@0 996 // Cast is safe since all children are nsDocLoader too
michael@0 997 individualProgress = ((nsDocLoader *) docloader)->GetMaxTotalProgress();
michael@0 998 }
michael@0 999 if (individualProgress < int64_t(0)) // if one of the elements doesn't know it's size
michael@0 1000 // then none of them do
michael@0 1001 {
michael@0 1002 newMaxTotal = int64_t(-1);
michael@0 1003 break;
michael@0 1004 }
michael@0 1005 else
michael@0 1006 newMaxTotal += individualProgress;
michael@0 1007 }
michael@0 1008
michael@0 1009 int64_t progress = -1;
michael@0 1010 if (mMaxSelfProgress >= int64_t(0) && newMaxTotal >= int64_t(0))
michael@0 1011 progress = newMaxTotal + mMaxSelfProgress;
michael@0 1012
michael@0 1013 return progress;
michael@0 1014 }
michael@0 1015
michael@0 1016 ////////////////////////////////////////////////////////////////////////////////////
michael@0 1017 // The following section contains support for nsIProgressEventSink which is used to
michael@0 1018 // pass progress and status between the actual request and the doc loader. The doc loader
michael@0 1019 // then turns around and makes the right web progress calls based on this information.
michael@0 1020 ////////////////////////////////////////////////////////////////////////////////////
michael@0 1021
michael@0 1022 NS_IMETHODIMP nsDocLoader::OnProgress(nsIRequest *aRequest, nsISupports* ctxt,
michael@0 1023 uint64_t aProgress, uint64_t aProgressMax)
michael@0 1024 {
michael@0 1025 int64_t progressDelta = 0;
michael@0 1026
michael@0 1027 //
michael@0 1028 // Update the RequestInfo entry with the new progress data
michael@0 1029 //
michael@0 1030 if (nsRequestInfo* info = GetRequestInfo(aRequest)) {
michael@0 1031 // Update info->mCurrentProgress before we call FireOnStateChange,
michael@0 1032 // since that can make the "info" pointer invalid.
michael@0 1033 int64_t oldCurrentProgress = info->mCurrentProgress;
michael@0 1034 progressDelta = int64_t(aProgress) - oldCurrentProgress;
michael@0 1035 info->mCurrentProgress = int64_t(aProgress);
michael@0 1036
michael@0 1037 // suppress sending STATE_TRANSFERRING if this is upload progress (see bug 240053)
michael@0 1038 if (!info->mUploading && (int64_t(0) == oldCurrentProgress) && (int64_t(0) == info->mMaxProgress)) {
michael@0 1039 //
michael@0 1040 // If we receive an OnProgress event from a toplevel channel that the URI Loader
michael@0 1041 // has not yet targeted, then we must suppress the event. This is necessary to
michael@0 1042 // ensure that webprogresslisteners do not get confused when the channel is
michael@0 1043 // finally targeted. See bug 257308.
michael@0 1044 //
michael@0 1045 nsLoadFlags lf = 0;
michael@0 1046 aRequest->GetLoadFlags(&lf);
michael@0 1047 if ((lf & nsIChannel::LOAD_DOCUMENT_URI) && !(lf & nsIChannel::LOAD_TARGETED)) {
michael@0 1048 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
michael@0 1049 ("DocLoader:%p Ignoring OnProgress while load is not targeted\n", this));
michael@0 1050 return NS_OK;
michael@0 1051 }
michael@0 1052
michael@0 1053 //
michael@0 1054 // This is the first progress notification for the entry. If
michael@0 1055 // (aMaxProgress > 0) then the content-length of the data is known,
michael@0 1056 // so update mMaxSelfProgress... Otherwise, set it to -1 to indicate
michael@0 1057 // that the content-length is no longer known.
michael@0 1058 //
michael@0 1059 if (uint64_t(aProgressMax) != UINT64_MAX) {
michael@0 1060 mMaxSelfProgress += int64_t(aProgressMax);
michael@0 1061 info->mMaxProgress = int64_t(aProgressMax);
michael@0 1062 } else {
michael@0 1063 mMaxSelfProgress = int64_t(-1);
michael@0 1064 info->mMaxProgress = int64_t(-1);
michael@0 1065 }
michael@0 1066
michael@0 1067 // Send a STATE_TRANSFERRING notification for the request.
michael@0 1068 int32_t flags;
michael@0 1069
michael@0 1070 flags = nsIWebProgressListener::STATE_TRANSFERRING |
michael@0 1071 nsIWebProgressListener::STATE_IS_REQUEST;
michael@0 1072 //
michael@0 1073 // Move the WebProgress into the STATE_TRANSFERRING state if necessary...
michael@0 1074 //
michael@0 1075 if (mProgressStateFlags & nsIWebProgressListener::STATE_START) {
michael@0 1076 mProgressStateFlags = nsIWebProgressListener::STATE_TRANSFERRING;
michael@0 1077
michael@0 1078 // Send STATE_TRANSFERRING for the document too...
michael@0 1079 flags |= nsIWebProgressListener::STATE_IS_DOCUMENT;
michael@0 1080 }
michael@0 1081
michael@0 1082 FireOnStateChange(this, aRequest, flags, NS_OK);
michael@0 1083 }
michael@0 1084
michael@0 1085 // Update our overall current progress count.
michael@0 1086 mCurrentSelfProgress += progressDelta;
michael@0 1087 }
michael@0 1088 //
michael@0 1089 // The request is not part of the load group, so ignore its progress
michael@0 1090 // information...
michael@0 1091 //
michael@0 1092 else {
michael@0 1093 #if defined(DEBUG)
michael@0 1094 nsAutoCString buffer;
michael@0 1095
michael@0 1096 GetURIStringFromRequest(aRequest, buffer);
michael@0 1097 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
michael@0 1098 ("DocLoader:%p OOPS - No Request Info for: %s\n",
michael@0 1099 this, buffer.get()));
michael@0 1100 #endif /* DEBUG */
michael@0 1101
michael@0 1102 return NS_OK;
michael@0 1103 }
michael@0 1104
michael@0 1105 //
michael@0 1106 // Fire progress notifications out to any registered nsIWebProgressListeners
michael@0 1107 //
michael@0 1108 FireOnProgressChange(this, aRequest, aProgress, aProgressMax, progressDelta,
michael@0 1109 mCurrentTotalProgress, mMaxTotalProgress);
michael@0 1110
michael@0 1111 return NS_OK;
michael@0 1112 }
michael@0 1113
michael@0 1114 NS_IMETHODIMP nsDocLoader::OnStatus(nsIRequest* aRequest, nsISupports* ctxt,
michael@0 1115 nsresult aStatus, const char16_t* aStatusArg)
michael@0 1116 {
michael@0 1117 //
michael@0 1118 // Fire progress notifications out to any registered nsIWebProgressListeners
michael@0 1119 //
michael@0 1120 if (aStatus != NS_OK) {
michael@0 1121 // Remember the current status for this request
michael@0 1122 nsRequestInfo *info;
michael@0 1123 info = GetRequestInfo(aRequest);
michael@0 1124 if (info) {
michael@0 1125 bool uploading = (aStatus == NS_NET_STATUS_WRITING ||
michael@0 1126 aStatus == NS_NET_STATUS_SENDING_TO);
michael@0 1127 // If switching from uploading to downloading (or vice versa), then we
michael@0 1128 // need to reset our progress counts. This is designed with HTTP form
michael@0 1129 // submission in mind, where an upload is performed followed by download
michael@0 1130 // of possibly several documents.
michael@0 1131 if (info->mUploading != uploading) {
michael@0 1132 mCurrentSelfProgress = mMaxSelfProgress = 0;
michael@0 1133 mCurrentTotalProgress = mMaxTotalProgress = 0;
michael@0 1134 mCompletedTotalProgress = 0;
michael@0 1135 info->mUploading = uploading;
michael@0 1136 info->mCurrentProgress = 0;
michael@0 1137 info->mMaxProgress = 0;
michael@0 1138 }
michael@0 1139 }
michael@0 1140
michael@0 1141 nsCOMPtr<nsIStringBundleService> sbs =
michael@0 1142 mozilla::services::GetStringBundleService();
michael@0 1143 if (!sbs)
michael@0 1144 return NS_ERROR_FAILURE;
michael@0 1145 nsXPIDLString msg;
michael@0 1146 nsresult rv = sbs->FormatStatusMessage(aStatus, aStatusArg,
michael@0 1147 getter_Copies(msg));
michael@0 1148 if (NS_FAILED(rv))
michael@0 1149 return rv;
michael@0 1150
michael@0 1151 // Keep around the message. In case a request finishes, we need to make sure
michael@0 1152 // to send the status message of another request to our user to that we
michael@0 1153 // don't display, for example, "Transferring" messages for requests that are
michael@0 1154 // already done.
michael@0 1155 if (info) {
michael@0 1156 if (!info->mLastStatus) {
michael@0 1157 info->mLastStatus = new nsStatusInfo(aRequest);
michael@0 1158 } else {
michael@0 1159 // We're going to move it to the front of the list, so remove
michael@0 1160 // it from wherever it is now.
michael@0 1161 info->mLastStatus->remove();
michael@0 1162 }
michael@0 1163 info->mLastStatus->mStatusMessage = msg;
michael@0 1164 info->mLastStatus->mStatusCode = aStatus;
michael@0 1165 // Put the info at the front of the list
michael@0 1166 mStatusInfoList.insertFront(info->mLastStatus);
michael@0 1167 }
michael@0 1168 FireOnStatusChange(this, aRequest, aStatus, msg);
michael@0 1169 }
michael@0 1170 return NS_OK;
michael@0 1171 }
michael@0 1172
michael@0 1173 void nsDocLoader::ClearInternalProgress()
michael@0 1174 {
michael@0 1175 ClearRequestInfoHash();
michael@0 1176
michael@0 1177 mCurrentSelfProgress = mMaxSelfProgress = 0;
michael@0 1178 mCurrentTotalProgress = mMaxTotalProgress = 0;
michael@0 1179 mCompletedTotalProgress = 0;
michael@0 1180
michael@0 1181 mProgressStateFlags = nsIWebProgressListener::STATE_STOP;
michael@0 1182 }
michael@0 1183
michael@0 1184
michael@0 1185 void nsDocLoader::FireOnProgressChange(nsDocLoader *aLoadInitiator,
michael@0 1186 nsIRequest *request,
michael@0 1187 int64_t aProgress,
michael@0 1188 int64_t aProgressMax,
michael@0 1189 int64_t aProgressDelta,
michael@0 1190 int64_t aTotalProgress,
michael@0 1191 int64_t aMaxTotalProgress)
michael@0 1192 {
michael@0 1193 if (mIsLoadingDocument) {
michael@0 1194 mCurrentTotalProgress += aProgressDelta;
michael@0 1195 mMaxTotalProgress = GetMaxTotalProgress();
michael@0 1196
michael@0 1197 aTotalProgress = mCurrentTotalProgress;
michael@0 1198 aMaxTotalProgress = mMaxTotalProgress;
michael@0 1199 }
michael@0 1200
michael@0 1201 #if defined(DEBUG)
michael@0 1202 nsAutoCString buffer;
michael@0 1203
michael@0 1204 GetURIStringFromRequest(request, buffer);
michael@0 1205 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
michael@0 1206 ("DocLoader:%p: Progress (%s): curSelf: %d maxSelf: %d curTotal: %d maxTotal %d\n",
michael@0 1207 this, buffer.get(), aProgress, aProgressMax, aTotalProgress, aMaxTotalProgress));
michael@0 1208 #endif /* DEBUG */
michael@0 1209
michael@0 1210 /*
michael@0 1211 * First notify any listeners of the new progress info...
michael@0 1212 *
michael@0 1213 * Operate the elements from back to front so that if items get
michael@0 1214 * get removed from the list it won't affect our iteration
michael@0 1215 */
michael@0 1216 nsCOMPtr<nsIWebProgressListener> listener;
michael@0 1217 int32_t count = mListenerInfoList.Count();
michael@0 1218
michael@0 1219 while (--count >= 0) {
michael@0 1220 nsListenerInfo *info;
michael@0 1221
michael@0 1222 info = static_cast<nsListenerInfo*>(mListenerInfoList.SafeElementAt(count));
michael@0 1223 if (!info || !(info->mNotifyMask & nsIWebProgress::NOTIFY_PROGRESS)) {
michael@0 1224 continue;
michael@0 1225 }
michael@0 1226
michael@0 1227 listener = do_QueryReferent(info->mWeakListener);
michael@0 1228 if (!listener) {
michael@0 1229 // the listener went away. gracefully pull it out of the list.
michael@0 1230 mListenerInfoList.RemoveElementAt(count);
michael@0 1231 delete info;
michael@0 1232 continue;
michael@0 1233 }
michael@0 1234
michael@0 1235 // XXX truncates 64-bit to 32-bit
michael@0 1236 listener->OnProgressChange(aLoadInitiator,request,
michael@0 1237 int32_t(aProgress), int32_t(aProgressMax),
michael@0 1238 int32_t(aTotalProgress), int32_t(aMaxTotalProgress));
michael@0 1239 }
michael@0 1240
michael@0 1241 mListenerInfoList.Compact();
michael@0 1242
michael@0 1243 // Pass the notification up to the parent...
michael@0 1244 if (mParent) {
michael@0 1245 mParent->FireOnProgressChange(aLoadInitiator, request,
michael@0 1246 aProgress, aProgressMax,
michael@0 1247 aProgressDelta,
michael@0 1248 aTotalProgress, aMaxTotalProgress);
michael@0 1249 }
michael@0 1250 }
michael@0 1251
michael@0 1252 void nsDocLoader::GatherAncestorWebProgresses(WebProgressList& aList)
michael@0 1253 {
michael@0 1254 for (nsDocLoader* loader = this; loader; loader = loader->mParent) {
michael@0 1255 aList.AppendElement(loader);
michael@0 1256 }
michael@0 1257 }
michael@0 1258
michael@0 1259 void nsDocLoader::FireOnStateChange(nsIWebProgress *aProgress,
michael@0 1260 nsIRequest *aRequest,
michael@0 1261 int32_t aStateFlags,
michael@0 1262 nsresult aStatus)
michael@0 1263 {
michael@0 1264 WebProgressList list;
michael@0 1265 GatherAncestorWebProgresses(list);
michael@0 1266 for (uint32_t i = 0; i < list.Length(); ++i) {
michael@0 1267 list[i]->DoFireOnStateChange(aProgress, aRequest, aStateFlags, aStatus);
michael@0 1268 }
michael@0 1269 }
michael@0 1270
michael@0 1271 void nsDocLoader::DoFireOnStateChange(nsIWebProgress * const aProgress,
michael@0 1272 nsIRequest * const aRequest,
michael@0 1273 int32_t &aStateFlags,
michael@0 1274 const nsresult aStatus)
michael@0 1275 {
michael@0 1276 //
michael@0 1277 // Remove the STATE_IS_NETWORK bit if necessary.
michael@0 1278 //
michael@0 1279 // The rule is to remove this bit, if the notification has been passed
michael@0 1280 // up from a child WebProgress, and the current WebProgress is already
michael@0 1281 // active...
michael@0 1282 //
michael@0 1283 if (mIsLoadingDocument &&
michael@0 1284 (aStateFlags & nsIWebProgressListener::STATE_IS_NETWORK) &&
michael@0 1285 (this != aProgress)) {
michael@0 1286 aStateFlags &= ~nsIWebProgressListener::STATE_IS_NETWORK;
michael@0 1287 }
michael@0 1288
michael@0 1289 // Add the STATE_RESTORING bit if necessary.
michael@0 1290 if (mIsRestoringDocument)
michael@0 1291 aStateFlags |= nsIWebProgressListener::STATE_RESTORING;
michael@0 1292
michael@0 1293 #if defined(DEBUG)
michael@0 1294 nsAutoCString buffer;
michael@0 1295
michael@0 1296 GetURIStringFromRequest(aRequest, buffer);
michael@0 1297 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
michael@0 1298 ("DocLoader:%p: Status (%s): code: %x\n",
michael@0 1299 this, buffer.get(), aStateFlags));
michael@0 1300 #endif /* DEBUG */
michael@0 1301
michael@0 1302 NS_ASSERTION(aRequest, "Firing OnStateChange(...) notification with a NULL request!");
michael@0 1303
michael@0 1304 /*
michael@0 1305 * First notify any listeners of the new state info...
michael@0 1306 *
michael@0 1307 * Operate the elements from back to front so that if items get
michael@0 1308 * get removed from the list it won't affect our iteration
michael@0 1309 */
michael@0 1310 nsCOMPtr<nsIWebProgressListener> listener;
michael@0 1311 int32_t count = mListenerInfoList.Count();
michael@0 1312 int32_t notifyMask = (aStateFlags >> 16) & nsIWebProgress::NOTIFY_STATE_ALL;
michael@0 1313
michael@0 1314 while (--count >= 0) {
michael@0 1315 nsListenerInfo *info;
michael@0 1316
michael@0 1317 info = static_cast<nsListenerInfo*>(mListenerInfoList.SafeElementAt(count));
michael@0 1318 if (!info || !(info->mNotifyMask & notifyMask)) {
michael@0 1319 continue;
michael@0 1320 }
michael@0 1321
michael@0 1322 listener = do_QueryReferent(info->mWeakListener);
michael@0 1323 if (!listener) {
michael@0 1324 // the listener went away. gracefully pull it out of the list.
michael@0 1325 mListenerInfoList.RemoveElementAt(count);
michael@0 1326 delete info;
michael@0 1327 continue;
michael@0 1328 }
michael@0 1329
michael@0 1330 listener->OnStateChange(aProgress, aRequest, aStateFlags, aStatus);
michael@0 1331 }
michael@0 1332
michael@0 1333 mListenerInfoList.Compact();
michael@0 1334 }
michael@0 1335
michael@0 1336
michael@0 1337
michael@0 1338 void
michael@0 1339 nsDocLoader::FireOnLocationChange(nsIWebProgress* aWebProgress,
michael@0 1340 nsIRequest* aRequest,
michael@0 1341 nsIURI *aUri,
michael@0 1342 uint32_t aFlags)
michael@0 1343 {
michael@0 1344 /*
michael@0 1345 * First notify any listeners of the new state info...
michael@0 1346 *
michael@0 1347 * Operate the elements from back to front so that if items get
michael@0 1348 * get removed from the list it won't affect our iteration
michael@0 1349 */
michael@0 1350 nsCOMPtr<nsIWebProgressListener> listener;
michael@0 1351 int32_t count = mListenerInfoList.Count();
michael@0 1352
michael@0 1353 while (--count >= 0) {
michael@0 1354 nsListenerInfo *info;
michael@0 1355
michael@0 1356 info = static_cast<nsListenerInfo*>(mListenerInfoList.SafeElementAt(count));
michael@0 1357 if (!info || !(info->mNotifyMask & nsIWebProgress::NOTIFY_LOCATION)) {
michael@0 1358 continue;
michael@0 1359 }
michael@0 1360
michael@0 1361 listener = do_QueryReferent(info->mWeakListener);
michael@0 1362 if (!listener) {
michael@0 1363 // the listener went away. gracefully pull it out of the list.
michael@0 1364 mListenerInfoList.RemoveElementAt(count);
michael@0 1365 delete info;
michael@0 1366 continue;
michael@0 1367 }
michael@0 1368
michael@0 1369 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG, ("DocLoader [%p] calling %p->OnLocationChange", this, listener.get()));
michael@0 1370 listener->OnLocationChange(aWebProgress, aRequest, aUri, aFlags);
michael@0 1371 }
michael@0 1372
michael@0 1373 mListenerInfoList.Compact();
michael@0 1374
michael@0 1375 // Pass the notification up to the parent...
michael@0 1376 if (mParent) {
michael@0 1377 mParent->FireOnLocationChange(aWebProgress, aRequest, aUri, aFlags);
michael@0 1378 }
michael@0 1379 }
michael@0 1380
michael@0 1381 void
michael@0 1382 nsDocLoader::FireOnStatusChange(nsIWebProgress* aWebProgress,
michael@0 1383 nsIRequest* aRequest,
michael@0 1384 nsresult aStatus,
michael@0 1385 const char16_t* aMessage)
michael@0 1386 {
michael@0 1387 /*
michael@0 1388 * First notify any listeners of the new state info...
michael@0 1389 *
michael@0 1390 * Operate the elements from back to front so that if items get
michael@0 1391 * get removed from the list it won't affect our iteration
michael@0 1392 */
michael@0 1393 nsCOMPtr<nsIWebProgressListener> listener;
michael@0 1394 int32_t count = mListenerInfoList.Count();
michael@0 1395
michael@0 1396 while (--count >= 0) {
michael@0 1397 nsListenerInfo *info;
michael@0 1398
michael@0 1399 info = static_cast<nsListenerInfo*>(mListenerInfoList.SafeElementAt(count));
michael@0 1400 if (!info || !(info->mNotifyMask & nsIWebProgress::NOTIFY_STATUS)) {
michael@0 1401 continue;
michael@0 1402 }
michael@0 1403
michael@0 1404 listener = do_QueryReferent(info->mWeakListener);
michael@0 1405 if (!listener) {
michael@0 1406 // the listener went away. gracefully pull it out of the list.
michael@0 1407 mListenerInfoList.RemoveElementAt(count);
michael@0 1408 delete info;
michael@0 1409 continue;
michael@0 1410 }
michael@0 1411
michael@0 1412 listener->OnStatusChange(aWebProgress, aRequest, aStatus, aMessage);
michael@0 1413 }
michael@0 1414 mListenerInfoList.Compact();
michael@0 1415
michael@0 1416 // Pass the notification up to the parent...
michael@0 1417 if (mParent) {
michael@0 1418 mParent->FireOnStatusChange(aWebProgress, aRequest, aStatus, aMessage);
michael@0 1419 }
michael@0 1420 }
michael@0 1421
michael@0 1422 bool
michael@0 1423 nsDocLoader::RefreshAttempted(nsIWebProgress* aWebProgress,
michael@0 1424 nsIURI *aURI,
michael@0 1425 int32_t aDelay,
michael@0 1426 bool aSameURI)
michael@0 1427 {
michael@0 1428 /*
michael@0 1429 * Returns true if the refresh may proceed,
michael@0 1430 * false if the refresh should be blocked.
michael@0 1431 *
michael@0 1432 * First notify any listeners of the refresh attempt...
michael@0 1433 *
michael@0 1434 * Iterate the elements from back to front so that if items
michael@0 1435 * get removed from the list it won't affect our iteration
michael@0 1436 */
michael@0 1437 bool allowRefresh = true;
michael@0 1438 int32_t count = mListenerInfoList.Count();
michael@0 1439
michael@0 1440 while (--count >= 0) {
michael@0 1441 nsListenerInfo *info;
michael@0 1442
michael@0 1443 info = static_cast<nsListenerInfo*>(mListenerInfoList.SafeElementAt(count));
michael@0 1444 if (!info || !(info->mNotifyMask & nsIWebProgress::NOTIFY_REFRESH)) {
michael@0 1445 continue;
michael@0 1446 }
michael@0 1447
michael@0 1448 nsCOMPtr<nsIWebProgressListener> listener =
michael@0 1449 do_QueryReferent(info->mWeakListener);
michael@0 1450 if (!listener) {
michael@0 1451 // the listener went away. gracefully pull it out of the list.
michael@0 1452 mListenerInfoList.RemoveElementAt(count);
michael@0 1453 delete info;
michael@0 1454 continue;
michael@0 1455 }
michael@0 1456
michael@0 1457 nsCOMPtr<nsIWebProgressListener2> listener2 =
michael@0 1458 do_QueryReferent(info->mWeakListener);
michael@0 1459 if (!listener2)
michael@0 1460 continue;
michael@0 1461
michael@0 1462 bool listenerAllowedRefresh;
michael@0 1463 nsresult listenerRV = listener2->OnRefreshAttempted(
michael@0 1464 aWebProgress, aURI, aDelay, aSameURI, &listenerAllowedRefresh);
michael@0 1465 if (NS_FAILED(listenerRV))
michael@0 1466 continue;
michael@0 1467
michael@0 1468 allowRefresh = allowRefresh && listenerAllowedRefresh;
michael@0 1469 }
michael@0 1470
michael@0 1471 mListenerInfoList.Compact();
michael@0 1472
michael@0 1473 // Pass the notification up to the parent...
michael@0 1474 if (mParent) {
michael@0 1475 allowRefresh = allowRefresh &&
michael@0 1476 mParent->RefreshAttempted(aWebProgress, aURI, aDelay, aSameURI);
michael@0 1477 }
michael@0 1478
michael@0 1479 return allowRefresh;
michael@0 1480 }
michael@0 1481
michael@0 1482 nsListenerInfo *
michael@0 1483 nsDocLoader::GetListenerInfo(nsIWebProgressListener *aListener)
michael@0 1484 {
michael@0 1485 int32_t i, count;
michael@0 1486 nsListenerInfo *info;
michael@0 1487
michael@0 1488 nsCOMPtr<nsISupports> listener1 = do_QueryInterface(aListener);
michael@0 1489 count = mListenerInfoList.Count();
michael@0 1490 for (i=0; i<count; i++) {
michael@0 1491 info = static_cast<nsListenerInfo*>(mListenerInfoList.SafeElementAt(i));
michael@0 1492
michael@0 1493 NS_ASSERTION(info, "There should NEVER be a null listener in the list");
michael@0 1494 if (info) {
michael@0 1495 nsCOMPtr<nsISupports> listener2 = do_QueryReferent(info->mWeakListener);
michael@0 1496 if (listener1 == listener2)
michael@0 1497 return info;
michael@0 1498 }
michael@0 1499 }
michael@0 1500 return nullptr;
michael@0 1501 }
michael@0 1502
michael@0 1503 nsresult nsDocLoader::AddRequestInfo(nsIRequest *aRequest)
michael@0 1504 {
michael@0 1505 if (!PL_DHashTableOperate(&mRequestInfoHash, aRequest, PL_DHASH_ADD)) {
michael@0 1506 return NS_ERROR_OUT_OF_MEMORY;
michael@0 1507 }
michael@0 1508
michael@0 1509 return NS_OK;
michael@0 1510 }
michael@0 1511
michael@0 1512 void nsDocLoader::RemoveRequestInfo(nsIRequest *aRequest)
michael@0 1513 {
michael@0 1514 PL_DHashTableOperate(&mRequestInfoHash, aRequest, PL_DHASH_REMOVE);
michael@0 1515 }
michael@0 1516
michael@0 1517 nsDocLoader::nsRequestInfo* nsDocLoader::GetRequestInfo(nsIRequest* aRequest)
michael@0 1518 {
michael@0 1519 nsRequestInfo* info =
michael@0 1520 static_cast<nsRequestInfo*>
michael@0 1521 (PL_DHashTableOperate(&mRequestInfoHash, aRequest,
michael@0 1522 PL_DHASH_LOOKUP));
michael@0 1523
michael@0 1524 if (PL_DHASH_ENTRY_IS_FREE(info)) {
michael@0 1525 // Nothing found in the hash, return null.
michael@0 1526
michael@0 1527 return nullptr;
michael@0 1528 }
michael@0 1529
michael@0 1530 // Return what we found in the hash...
michael@0 1531
michael@0 1532 return info;
michael@0 1533 }
michael@0 1534
michael@0 1535 // PLDHashTable enumeration callback that just removes every entry
michael@0 1536 // from the hash.
michael@0 1537 static PLDHashOperator
michael@0 1538 RemoveInfoCallback(PLDHashTable *table, PLDHashEntryHdr *hdr, uint32_t number,
michael@0 1539 void *arg)
michael@0 1540 {
michael@0 1541 return PL_DHASH_REMOVE;
michael@0 1542 }
michael@0 1543
michael@0 1544 void nsDocLoader::ClearRequestInfoHash(void)
michael@0 1545 {
michael@0 1546 if (!mRequestInfoHash.ops || !mRequestInfoHash.entryCount) {
michael@0 1547 // No hash, or the hash is empty, nothing to do here then...
michael@0 1548
michael@0 1549 return;
michael@0 1550 }
michael@0 1551
michael@0 1552 PL_DHashTableEnumerate(&mRequestInfoHash, RemoveInfoCallback, nullptr);
michael@0 1553 }
michael@0 1554
michael@0 1555 // PLDHashTable enumeration callback that calculates the max progress.
michael@0 1556 PLDHashOperator
michael@0 1557 nsDocLoader::CalcMaxProgressCallback(PLDHashTable* table, PLDHashEntryHdr* hdr,
michael@0 1558 uint32_t number, void* arg)
michael@0 1559 {
michael@0 1560 const nsRequestInfo* info = static_cast<const nsRequestInfo*>(hdr);
michael@0 1561 int64_t* max = static_cast<int64_t* >(arg);
michael@0 1562
michael@0 1563 if (info->mMaxProgress < info->mCurrentProgress) {
michael@0 1564 *max = int64_t(-1);
michael@0 1565
michael@0 1566 return PL_DHASH_STOP;
michael@0 1567 }
michael@0 1568
michael@0 1569 *max += info->mMaxProgress;
michael@0 1570
michael@0 1571 return PL_DHASH_NEXT;
michael@0 1572 }
michael@0 1573
michael@0 1574 int64_t nsDocLoader::CalculateMaxProgress()
michael@0 1575 {
michael@0 1576 int64_t max = mCompletedTotalProgress;
michael@0 1577 PL_DHashTableEnumerate(&mRequestInfoHash, CalcMaxProgressCallback, &max);
michael@0 1578 return max;
michael@0 1579 }
michael@0 1580
michael@0 1581 NS_IMETHODIMP nsDocLoader::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
michael@0 1582 nsIChannel *aNewChannel,
michael@0 1583 uint32_t aFlags,
michael@0 1584 nsIAsyncVerifyRedirectCallback *cb)
michael@0 1585 {
michael@0 1586 if (aOldChannel)
michael@0 1587 {
michael@0 1588 nsLoadFlags loadFlags = 0;
michael@0 1589 int32_t stateFlags = nsIWebProgressListener::STATE_REDIRECTING |
michael@0 1590 nsIWebProgressListener::STATE_IS_REQUEST;
michael@0 1591
michael@0 1592 aOldChannel->GetLoadFlags(&loadFlags);
michael@0 1593 // If the document channel is being redirected, then indicate that the
michael@0 1594 // document is being redirected in the notification...
michael@0 1595 if (loadFlags & nsIChannel::LOAD_DOCUMENT_URI)
michael@0 1596 {
michael@0 1597 stateFlags |= nsIWebProgressListener::STATE_IS_DOCUMENT;
michael@0 1598
michael@0 1599 #if defined(DEBUG)
michael@0 1600 nsCOMPtr<nsIRequest> request(do_QueryInterface(aOldChannel));
michael@0 1601 NS_ASSERTION(request == mDocumentRequest, "Wrong Document Channel");
michael@0 1602 #endif /* DEBUG */
michael@0 1603 }
michael@0 1604
michael@0 1605 OnRedirectStateChange(aOldChannel, aNewChannel, aFlags, stateFlags);
michael@0 1606 FireOnStateChange(this, aOldChannel, stateFlags, NS_OK);
michael@0 1607 }
michael@0 1608
michael@0 1609 cb->OnRedirectVerifyCallback(NS_OK);
michael@0 1610 return NS_OK;
michael@0 1611 }
michael@0 1612
michael@0 1613 /*
michael@0 1614 * Implementation of nsISecurityEventSink method...
michael@0 1615 */
michael@0 1616
michael@0 1617 NS_IMETHODIMP nsDocLoader::OnSecurityChange(nsISupports * aContext,
michael@0 1618 uint32_t aState)
michael@0 1619 {
michael@0 1620 //
michael@0 1621 // Fire progress notifications out to any registered nsIWebProgressListeners.
michael@0 1622 //
michael@0 1623
michael@0 1624 nsCOMPtr<nsIRequest> request = do_QueryInterface(aContext);
michael@0 1625 nsIWebProgress* webProgress = static_cast<nsIWebProgress*>(this);
michael@0 1626
michael@0 1627 /*
michael@0 1628 * First notify any listeners of the new state info...
michael@0 1629 *
michael@0 1630 * Operate the elements from back to front so that if items get
michael@0 1631 * get removed from the list it won't affect our iteration
michael@0 1632 */
michael@0 1633 nsCOMPtr<nsIWebProgressListener> listener;
michael@0 1634 int32_t count = mListenerInfoList.Count();
michael@0 1635
michael@0 1636 while (--count >= 0) {
michael@0 1637 nsListenerInfo *info;
michael@0 1638
michael@0 1639 info = static_cast<nsListenerInfo*>(mListenerInfoList.SafeElementAt(count));
michael@0 1640 if (!info || !(info->mNotifyMask & nsIWebProgress::NOTIFY_SECURITY)) {
michael@0 1641 continue;
michael@0 1642 }
michael@0 1643
michael@0 1644 listener = do_QueryReferent(info->mWeakListener);
michael@0 1645 if (!listener) {
michael@0 1646 // the listener went away. gracefully pull it out of the list.
michael@0 1647 mListenerInfoList.RemoveElementAt(count);
michael@0 1648 delete info;
michael@0 1649 continue;
michael@0 1650 }
michael@0 1651
michael@0 1652 listener->OnSecurityChange(webProgress, request, aState);
michael@0 1653 }
michael@0 1654
michael@0 1655 mListenerInfoList.Compact();
michael@0 1656
michael@0 1657 // Pass the notification up to the parent...
michael@0 1658 if (mParent) {
michael@0 1659 mParent->OnSecurityChange(aContext, aState);
michael@0 1660 }
michael@0 1661 return NS_OK;
michael@0 1662 }
michael@0 1663
michael@0 1664 /*
michael@0 1665 * Implementation of nsISupportsPriority methods...
michael@0 1666 *
michael@0 1667 * The priority of the DocLoader _is_ the priority of its LoadGroup.
michael@0 1668 *
michael@0 1669 * XXX(darin): Once we start storing loadgroups in loadgroups, this code will
michael@0 1670 * go away.
michael@0 1671 */
michael@0 1672
michael@0 1673 NS_IMETHODIMP nsDocLoader::GetPriority(int32_t *aPriority)
michael@0 1674 {
michael@0 1675 nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mLoadGroup);
michael@0 1676 if (p)
michael@0 1677 return p->GetPriority(aPriority);
michael@0 1678
michael@0 1679 *aPriority = 0;
michael@0 1680 return NS_OK;
michael@0 1681 }
michael@0 1682
michael@0 1683 NS_IMETHODIMP nsDocLoader::SetPriority(int32_t aPriority)
michael@0 1684 {
michael@0 1685 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
michael@0 1686 ("DocLoader:%p: SetPriority(%d) called\n", this, aPriority));
michael@0 1687
michael@0 1688 nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mLoadGroup);
michael@0 1689 if (p)
michael@0 1690 p->SetPriority(aPriority);
michael@0 1691
michael@0 1692 NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(mChildList, nsDocLoader,
michael@0 1693 SetPriority, (aPriority));
michael@0 1694
michael@0 1695 return NS_OK;
michael@0 1696 }
michael@0 1697
michael@0 1698 NS_IMETHODIMP nsDocLoader::AdjustPriority(int32_t aDelta)
michael@0 1699 {
michael@0 1700 PR_LOG(gDocLoaderLog, PR_LOG_DEBUG,
michael@0 1701 ("DocLoader:%p: AdjustPriority(%d) called\n", this, aDelta));
michael@0 1702
michael@0 1703 nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mLoadGroup);
michael@0 1704 if (p)
michael@0 1705 p->AdjustPriority(aDelta);
michael@0 1706
michael@0 1707 NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(mChildList, nsDocLoader,
michael@0 1708 AdjustPriority, (aDelta));
michael@0 1709
michael@0 1710 return NS_OK;
michael@0 1711 }
michael@0 1712
michael@0 1713
michael@0 1714
michael@0 1715
michael@0 1716 #if 0
michael@0 1717 void nsDocLoader::DumpChannelInfo()
michael@0 1718 {
michael@0 1719 nsChannelInfo *info;
michael@0 1720 int32_t i, count;
michael@0 1721 int32_t current=0, max=0;
michael@0 1722
michael@0 1723
michael@0 1724 printf("==== DocLoader=%x\n", this);
michael@0 1725
michael@0 1726 count = mChannelInfoList.Count();
michael@0 1727 for(i=0; i<count; i++) {
michael@0 1728 info = (nsChannelInfo *)mChannelInfoList.ElementAt(i);
michael@0 1729
michael@0 1730 #if defined(DEBUG)
michael@0 1731 nsAutoCString buffer;
michael@0 1732 nsresult rv = NS_OK;
michael@0 1733 if (info->mURI) {
michael@0 1734 rv = info->mURI->GetSpec(buffer);
michael@0 1735 }
michael@0 1736
michael@0 1737 printf(" [%d] current=%d max=%d [%s]\n", i,
michael@0 1738 info->mCurrentProgress,
michael@0 1739 info->mMaxProgress, buffer.get());
michael@0 1740 #endif /* DEBUG */
michael@0 1741
michael@0 1742 current += info->mCurrentProgress;
michael@0 1743 if (max >= 0) {
michael@0 1744 if (info->mMaxProgress < info->mCurrentProgress) {
michael@0 1745 max = -1;
michael@0 1746 } else {
michael@0 1747 max += info->mMaxProgress;
michael@0 1748 }
michael@0 1749 }
michael@0 1750 }
michael@0 1751
michael@0 1752 printf("\nCurrent=%d Total=%d\n====\n", current, max);
michael@0 1753 }
michael@0 1754 #endif /* 0 */

mercurial