netwerk/base/src/nsLoadGroup.cpp

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:f8e81235aa7c
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim: set sw=4 ts=4 sts=4 et cin: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "mozilla/DebugOnly.h"
8
9 #include "nsLoadGroup.h"
10
11 #include "nsArrayEnumerator.h"
12 #include "nsCOMArray.h"
13 #include "nsCOMPtr.h"
14 #include "prlog.h"
15 #include "nsString.h"
16 #include "nsTArray.h"
17 #include "mozilla/Atomics.h"
18 #include "mozilla/Telemetry.h"
19 #include "nsAutoPtr.h"
20 #include "mozilla/net/PSpdyPush.h"
21 #include "nsITimedChannel.h"
22 #include "nsIInterfaceRequestor.h"
23 #include "nsIRequestObserver.h"
24 #include "CacheObserver.h"
25 #include "MainThreadUtils.h"
26
27 using namespace mozilla;
28 using namespace mozilla::net;
29
30 #if defined(PR_LOGGING)
31 //
32 // Log module for nsILoadGroup logging...
33 //
34 // To enable logging (see prlog.h for full details):
35 //
36 // set NSPR_LOG_MODULES=LoadGroup:5
37 // set NSPR_LOG_FILE=nspr.log
38 //
39 // this enables PR_LOG_DEBUG level information and places all output in
40 // the file nspr.log
41 //
42 static PRLogModuleInfo* gLoadGroupLog = nullptr;
43 #endif
44
45 #undef LOG
46 #define LOG(args) PR_LOG(gLoadGroupLog, PR_LOG_DEBUG, args)
47
48 ////////////////////////////////////////////////////////////////////////////////
49
50 class RequestMapEntry : public PLDHashEntryHdr
51 {
52 public:
53 RequestMapEntry(nsIRequest *aRequest) :
54 mKey(aRequest)
55 {
56 }
57
58 nsCOMPtr<nsIRequest> mKey;
59 };
60
61 static bool
62 RequestHashMatchEntry(PLDHashTable *table, const PLDHashEntryHdr *entry,
63 const void *key)
64 {
65 const RequestMapEntry *e =
66 static_cast<const RequestMapEntry *>(entry);
67 const nsIRequest *request = static_cast<const nsIRequest *>(key);
68
69 return e->mKey == request;
70 }
71
72 static void
73 RequestHashClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry)
74 {
75 RequestMapEntry *e = static_cast<RequestMapEntry *>(entry);
76
77 // An entry is being cleared, let the entry do its own cleanup.
78 e->~RequestMapEntry();
79 }
80
81 static bool
82 RequestHashInitEntry(PLDHashTable *table, PLDHashEntryHdr *entry,
83 const void *key)
84 {
85 const nsIRequest *const_request = static_cast<const nsIRequest *>(key);
86 nsIRequest *request = const_cast<nsIRequest *>(const_request);
87
88 // Initialize the entry with placement new
89 new (entry) RequestMapEntry(request);
90 return true;
91 }
92
93
94 static void
95 RescheduleRequest(nsIRequest *aRequest, int32_t delta)
96 {
97 nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(aRequest);
98 if (p)
99 p->AdjustPriority(delta);
100 }
101
102 static PLDHashOperator
103 RescheduleRequests(PLDHashTable *table, PLDHashEntryHdr *hdr,
104 uint32_t number, void *arg)
105 {
106 RequestMapEntry *e = static_cast<RequestMapEntry *>(hdr);
107 int32_t *delta = static_cast<int32_t *>(arg);
108
109 RescheduleRequest(e->mKey, *delta);
110 return PL_DHASH_NEXT;
111 }
112
113
114 nsLoadGroup::nsLoadGroup(nsISupports* outer)
115 : mForegroundCount(0)
116 , mLoadFlags(LOAD_NORMAL)
117 , mDefaultLoadFlags(0)
118 , mStatus(NS_OK)
119 , mPriority(PRIORITY_NORMAL)
120 , mIsCanceling(false)
121 , mDefaultLoadIsTimed(false)
122 , mTimedRequests(0)
123 , mCachedRequests(0)
124 , mTimedNonCachedRequestsUntilOnEndPageLoad(0)
125 {
126 NS_INIT_AGGREGATED(outer);
127
128 #if defined(PR_LOGGING)
129 // Initialize the global PRLogModule for nsILoadGroup logging
130 if (nullptr == gLoadGroupLog)
131 gLoadGroupLog = PR_NewLogModule("LoadGroup");
132 #endif
133
134 LOG(("LOADGROUP [%x]: Created.\n", this));
135
136 // Initialize the ops in the hash to null to make sure we get
137 // consistent errors if someone fails to call ::Init() on an
138 // nsLoadGroup.
139 mRequests.ops = nullptr;
140 }
141
142 nsLoadGroup::~nsLoadGroup()
143 {
144 DebugOnly<nsresult> rv = Cancel(NS_BINDING_ABORTED);
145 NS_ASSERTION(NS_SUCCEEDED(rv), "Cancel failed");
146
147 if (mRequests.ops) {
148 PL_DHashTableFinish(&mRequests);
149 }
150
151 mDefaultLoadRequest = 0;
152
153 LOG(("LOADGROUP [%x]: Destroyed.\n", this));
154 }
155
156
157 ////////////////////////////////////////////////////////////////////////////////
158 // nsISupports methods:
159
160 NS_IMPL_AGGREGATED(nsLoadGroup)
161 NS_INTERFACE_MAP_BEGIN_AGGREGATED(nsLoadGroup)
162 NS_INTERFACE_MAP_ENTRY(nsILoadGroup)
163 NS_INTERFACE_MAP_ENTRY(nsPILoadGroupInternal)
164 NS_INTERFACE_MAP_ENTRY(nsILoadGroupChild)
165 NS_INTERFACE_MAP_ENTRY(nsIRequest)
166 NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
167 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
168 NS_INTERFACE_MAP_END
169
170 ////////////////////////////////////////////////////////////////////////////////
171 // nsIRequest methods:
172
173 NS_IMETHODIMP
174 nsLoadGroup::GetName(nsACString &result)
175 {
176 // XXX is this the right "name" for a load group?
177
178 if (!mDefaultLoadRequest) {
179 result.Truncate();
180 return NS_OK;
181 }
182
183 return mDefaultLoadRequest->GetName(result);
184 }
185
186 NS_IMETHODIMP
187 nsLoadGroup::IsPending(bool *aResult)
188 {
189 *aResult = (mForegroundCount > 0) ? true : false;
190 return NS_OK;
191 }
192
193 NS_IMETHODIMP
194 nsLoadGroup::GetStatus(nsresult *status)
195 {
196 if (NS_SUCCEEDED(mStatus) && mDefaultLoadRequest)
197 return mDefaultLoadRequest->GetStatus(status);
198
199 *status = mStatus;
200 return NS_OK;
201 }
202
203 // PLDHashTable enumeration callback that appends strong references to
204 // all nsIRequest to an nsTArray<nsIRequest*>.
205 static PLDHashOperator
206 AppendRequestsToArray(PLDHashTable *table, PLDHashEntryHdr *hdr,
207 uint32_t number, void *arg)
208 {
209 RequestMapEntry *e = static_cast<RequestMapEntry *>(hdr);
210 nsTArray<nsIRequest*> *array = static_cast<nsTArray<nsIRequest*> *>(arg);
211
212 nsIRequest *request = e->mKey;
213 NS_ASSERTION(request, "What? Null key in pldhash entry?");
214
215 bool ok = array->AppendElement(request) != nullptr;
216
217 if (!ok) {
218 return PL_DHASH_STOP;
219 }
220
221 NS_ADDREF(request);
222
223 return PL_DHASH_NEXT;
224 }
225
226 NS_IMETHODIMP
227 nsLoadGroup::Cancel(nsresult status)
228 {
229 MOZ_ASSERT(NS_IsMainThread());
230
231 NS_ASSERTION(NS_FAILED(status), "shouldn't cancel with a success code");
232 nsresult rv;
233 uint32_t count = mRequests.entryCount;
234
235 nsAutoTArray<nsIRequest*, 8> requests;
236
237 PL_DHashTableEnumerate(&mRequests, AppendRequestsToArray,
238 static_cast<nsTArray<nsIRequest*> *>(&requests));
239
240 if (requests.Length() != count) {
241 for (uint32_t i = 0, len = requests.Length(); i < len; ++i) {
242 NS_RELEASE(requests[i]);
243 }
244
245 return NS_ERROR_OUT_OF_MEMORY;
246 }
247
248 // set the load group status to our cancel status while we cancel
249 // all our requests...once the cancel is done, we'll reset it...
250 //
251 mStatus = status;
252
253 // Set the flag indicating that the loadgroup is being canceled... This
254 // prevents any new channels from being added during the operation.
255 //
256 mIsCanceling = true;
257
258 nsresult firstError = NS_OK;
259
260 while (count > 0) {
261 nsIRequest* request = requests.ElementAt(--count);
262
263 NS_ASSERTION(request, "NULL request found in list.");
264
265 RequestMapEntry *entry =
266 static_cast<RequestMapEntry *>
267 (PL_DHashTableOperate(&mRequests, request,
268 PL_DHASH_LOOKUP));
269
270 if (PL_DHASH_ENTRY_IS_FREE(entry)) {
271 // |request| was removed already
272
273 NS_RELEASE(request);
274
275 continue;
276 }
277
278 #if defined(PR_LOGGING)
279 nsAutoCString nameStr;
280 request->GetName(nameStr);
281 LOG(("LOADGROUP [%x]: Canceling request %x %s.\n",
282 this, request, nameStr.get()));
283 #endif
284
285 //
286 // Remove the request from the load group... This may cause
287 // the OnStopRequest notification to fire...
288 //
289 // XXX: What should the context be?
290 //
291 (void)RemoveRequest(request, nullptr, status);
292
293 // Cancel the request...
294 rv = request->Cancel(status);
295
296 // Remember the first failure and return it...
297 if (NS_FAILED(rv) && NS_SUCCEEDED(firstError))
298 firstError = rv;
299
300 NS_RELEASE(request);
301 }
302
303 #if defined(DEBUG)
304 NS_ASSERTION(mRequests.entryCount == 0, "Request list is not empty.");
305 NS_ASSERTION(mForegroundCount == 0, "Foreground URLs are active.");
306 #endif
307
308 mStatus = NS_OK;
309 mIsCanceling = false;
310
311 return firstError;
312 }
313
314
315 NS_IMETHODIMP
316 nsLoadGroup::Suspend()
317 {
318 nsresult rv, firstError;
319 uint32_t count = mRequests.entryCount;
320
321 nsAutoTArray<nsIRequest*, 8> requests;
322
323 PL_DHashTableEnumerate(&mRequests, AppendRequestsToArray,
324 static_cast<nsTArray<nsIRequest*> *>(&requests));
325
326 if (requests.Length() != count) {
327 for (uint32_t i = 0, len = requests.Length(); i < len; ++i) {
328 NS_RELEASE(requests[i]);
329 }
330
331 return NS_ERROR_OUT_OF_MEMORY;
332 }
333
334 firstError = NS_OK;
335 //
336 // Operate the elements from back to front so that if items get
337 // get removed from the list it won't affect our iteration
338 //
339 while (count > 0) {
340 nsIRequest* request = requests.ElementAt(--count);
341
342 NS_ASSERTION(request, "NULL request found in list.");
343 if (!request)
344 continue;
345
346 #if defined(PR_LOGGING)
347 nsAutoCString nameStr;
348 request->GetName(nameStr);
349 LOG(("LOADGROUP [%x]: Suspending request %x %s.\n",
350 this, request, nameStr.get()));
351 #endif
352
353 // Suspend the request...
354 rv = request->Suspend();
355
356 // Remember the first failure and return it...
357 if (NS_FAILED(rv) && NS_SUCCEEDED(firstError))
358 firstError = rv;
359
360 NS_RELEASE(request);
361 }
362
363 return firstError;
364 }
365
366
367 NS_IMETHODIMP
368 nsLoadGroup::Resume()
369 {
370 nsresult rv, firstError;
371 uint32_t count = mRequests.entryCount;
372
373 nsAutoTArray<nsIRequest*, 8> requests;
374
375 PL_DHashTableEnumerate(&mRequests, AppendRequestsToArray,
376 static_cast<nsTArray<nsIRequest*> *>(&requests));
377
378 if (requests.Length() != count) {
379 for (uint32_t i = 0, len = requests.Length(); i < len; ++i) {
380 NS_RELEASE(requests[i]);
381 }
382
383 return NS_ERROR_OUT_OF_MEMORY;
384 }
385
386 firstError = NS_OK;
387 //
388 // Operate the elements from back to front so that if items get
389 // get removed from the list it won't affect our iteration
390 //
391 while (count > 0) {
392 nsIRequest* request = requests.ElementAt(--count);
393
394 NS_ASSERTION(request, "NULL request found in list.");
395 if (!request)
396 continue;
397
398 #if defined(PR_LOGGING)
399 nsAutoCString nameStr;
400 request->GetName(nameStr);
401 LOG(("LOADGROUP [%x]: Resuming request %x %s.\n",
402 this, request, nameStr.get()));
403 #endif
404
405 // Resume the request...
406 rv = request->Resume();
407
408 // Remember the first failure and return it...
409 if (NS_FAILED(rv) && NS_SUCCEEDED(firstError))
410 firstError = rv;
411
412 NS_RELEASE(request);
413 }
414
415 return firstError;
416 }
417
418 NS_IMETHODIMP
419 nsLoadGroup::GetLoadFlags(uint32_t *aLoadFlags)
420 {
421 *aLoadFlags = mLoadFlags;
422 return NS_OK;
423 }
424
425 NS_IMETHODIMP
426 nsLoadGroup::SetLoadFlags(uint32_t aLoadFlags)
427 {
428 mLoadFlags = aLoadFlags;
429 return NS_OK;
430 }
431
432 NS_IMETHODIMP
433 nsLoadGroup::GetLoadGroup(nsILoadGroup **loadGroup)
434 {
435 *loadGroup = mLoadGroup;
436 NS_IF_ADDREF(*loadGroup);
437 return NS_OK;
438 }
439
440 NS_IMETHODIMP
441 nsLoadGroup::SetLoadGroup(nsILoadGroup *loadGroup)
442 {
443 mLoadGroup = loadGroup;
444 return NS_OK;
445 }
446
447 ////////////////////////////////////////////////////////////////////////////////
448 // nsILoadGroup methods:
449
450 NS_IMETHODIMP
451 nsLoadGroup::GetDefaultLoadRequest(nsIRequest * *aRequest)
452 {
453 *aRequest = mDefaultLoadRequest;
454 NS_IF_ADDREF(*aRequest);
455 return NS_OK;
456 }
457
458 NS_IMETHODIMP
459 nsLoadGroup::SetDefaultLoadRequest(nsIRequest *aRequest)
460 {
461 mDefaultLoadRequest = aRequest;
462 // Inherit the group load flags from the default load request
463 if (mDefaultLoadRequest) {
464 mDefaultLoadRequest->GetLoadFlags(&mLoadFlags);
465 //
466 // Mask off any bits that are not part of the nsIRequest flags.
467 // in particular, nsIChannel::LOAD_DOCUMENT_URI...
468 //
469 mLoadFlags &= nsIRequest::LOAD_REQUESTMASK;
470
471 nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(aRequest);
472 mDefaultLoadIsTimed = timedChannel != nullptr;
473 if (mDefaultLoadIsTimed) {
474 timedChannel->GetChannelCreation(&mDefaultRequestCreationTime);
475 timedChannel->SetTimingEnabled(true);
476 }
477 }
478 // Else, do not change the group's load flags (see bug 95981)
479 return NS_OK;
480 }
481
482 NS_IMETHODIMP
483 nsLoadGroup::AddRequest(nsIRequest *request, nsISupports* ctxt)
484 {
485 nsresult rv;
486
487 #if defined(PR_LOGGING)
488 {
489 nsAutoCString nameStr;
490 request->GetName(nameStr);
491 LOG(("LOADGROUP [%x]: Adding request %x %s (count=%d).\n",
492 this, request, nameStr.get(), mRequests.entryCount));
493 }
494 #endif /* PR_LOGGING */
495
496 #ifdef DEBUG
497 {
498 RequestMapEntry *entry =
499 static_cast<RequestMapEntry *>
500 (PL_DHashTableOperate(&mRequests, request,
501 PL_DHASH_LOOKUP));
502
503 NS_ASSERTION(PL_DHASH_ENTRY_IS_FREE(entry),
504 "Entry added to loadgroup twice, don't do that");
505 }
506 #endif
507
508 //
509 // Do not add the channel, if the loadgroup is being canceled...
510 //
511 if (mIsCanceling) {
512
513 #if defined(PR_LOGGING)
514 LOG(("LOADGROUP [%x]: AddChannel() ABORTED because LoadGroup is"
515 " being canceled!!\n", this));
516 #endif /* PR_LOGGING */
517
518 return NS_BINDING_ABORTED;
519 }
520
521 nsLoadFlags flags;
522 // if the request is the default load request or if the default
523 // load request is null, then the load group should inherit its
524 // load flags from the request.
525 if (mDefaultLoadRequest == request || !mDefaultLoadRequest)
526 rv = request->GetLoadFlags(&flags);
527 else
528 rv = MergeLoadFlags(request, flags);
529 if (NS_FAILED(rv)) return rv;
530
531 //
532 // Add the request to the list of active requests...
533 //
534
535 RequestMapEntry *entry =
536 static_cast<RequestMapEntry *>
537 (PL_DHashTableOperate(&mRequests, request,
538 PL_DHASH_ADD));
539
540 if (!entry) {
541 return NS_ERROR_OUT_OF_MEMORY;
542 }
543
544 if (mPriority != 0)
545 RescheduleRequest(request, mPriority);
546
547 nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(request);
548 if (timedChannel)
549 timedChannel->SetTimingEnabled(true);
550
551 if (!(flags & nsIRequest::LOAD_BACKGROUND)) {
552 // Update the count of foreground URIs..
553 mForegroundCount += 1;
554
555 //
556 // Fire the OnStartRequest notification out to the observer...
557 //
558 // If the notification fails then DO NOT add the request to
559 // the load group.
560 //
561 nsCOMPtr<nsIRequestObserver> observer = do_QueryReferent(mObserver);
562 if (observer) {
563 LOG(("LOADGROUP [%x]: Firing OnStartRequest for request %x."
564 "(foreground count=%d).\n", this, request, mForegroundCount));
565
566 rv = observer->OnStartRequest(request, ctxt);
567 if (NS_FAILED(rv)) {
568 LOG(("LOADGROUP [%x]: OnStartRequest for request %x FAILED.\n",
569 this, request));
570 //
571 // The URI load has been canceled by the observer. Clean up
572 // the damage...
573 //
574
575 PL_DHashTableOperate(&mRequests, request, PL_DHASH_REMOVE);
576
577 rv = NS_OK;
578
579 mForegroundCount -= 1;
580 }
581 }
582
583 // Ensure that we're part of our loadgroup while pending
584 if (mForegroundCount == 1 && mLoadGroup) {
585 mLoadGroup->AddRequest(this, nullptr);
586 }
587
588 }
589
590 return rv;
591 }
592
593 NS_IMETHODIMP
594 nsLoadGroup::RemoveRequest(nsIRequest *request, nsISupports* ctxt,
595 nsresult aStatus)
596 {
597 NS_ENSURE_ARG_POINTER(request);
598 nsresult rv;
599
600 #if defined(PR_LOGGING)
601 {
602 nsAutoCString nameStr;
603 request->GetName(nameStr);
604 LOG(("LOADGROUP [%x]: Removing request %x %s status %x (count=%d).\n",
605 this, request, nameStr.get(), aStatus, mRequests.entryCount-1));
606 }
607 #endif
608
609 // Make sure we have a owning reference to the request we're about
610 // to remove.
611
612 nsCOMPtr<nsIRequest> kungFuDeathGrip(request);
613
614 //
615 // Remove the request from the group. If this fails, it means that
616 // the request was *not* in the group so do not update the foreground
617 // count or it will get messed up...
618 //
619 RequestMapEntry *entry =
620 static_cast<RequestMapEntry *>
621 (PL_DHashTableOperate(&mRequests, request,
622 PL_DHASH_LOOKUP));
623
624 if (PL_DHASH_ENTRY_IS_FREE(entry)) {
625 LOG(("LOADGROUP [%x]: Unable to remove request %x. Not in group!\n",
626 this, request));
627
628 return NS_ERROR_FAILURE;
629 }
630
631 PL_DHashTableRawRemove(&mRequests, entry);
632
633 // Collect telemetry stats only when default request is a timed channel.
634 // Don't include failed requests in the timing statistics.
635 if (mDefaultLoadIsTimed && NS_SUCCEEDED(aStatus)) {
636 nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(request);
637 if (timedChannel) {
638 // Figure out if this request was served from the cache
639 ++mTimedRequests;
640 TimeStamp timeStamp;
641 rv = timedChannel->GetCacheReadStart(&timeStamp);
642 if (NS_SUCCEEDED(rv) && !timeStamp.IsNull()) {
643 ++mCachedRequests;
644 }
645 else {
646 mTimedNonCachedRequestsUntilOnEndPageLoad++;
647 }
648
649 rv = timedChannel->GetAsyncOpen(&timeStamp);
650 if (NS_SUCCEEDED(rv) && !timeStamp.IsNull()) {
651 Telemetry::AccumulateTimeDelta(
652 Telemetry::HTTP_SUBITEM_OPEN_LATENCY_TIME,
653 mDefaultRequestCreationTime, timeStamp);
654 }
655
656 rv = timedChannel->GetResponseStart(&timeStamp);
657 if (NS_SUCCEEDED(rv) && !timeStamp.IsNull()) {
658 Telemetry::AccumulateTimeDelta(
659 Telemetry::HTTP_SUBITEM_FIRST_BYTE_LATENCY_TIME,
660 mDefaultRequestCreationTime, timeStamp);
661 }
662
663 TelemetryReportChannel(timedChannel, false);
664 }
665 }
666
667 if (mRequests.entryCount == 0) {
668 TelemetryReport();
669 }
670
671 // Undo any group priority delta...
672 if (mPriority != 0)
673 RescheduleRequest(request, -mPriority);
674
675 nsLoadFlags flags;
676 rv = request->GetLoadFlags(&flags);
677 if (NS_FAILED(rv)) return rv;
678
679 if (!(flags & nsIRequest::LOAD_BACKGROUND)) {
680 NS_ASSERTION(mForegroundCount > 0, "ForegroundCount messed up");
681 mForegroundCount -= 1;
682
683 // Fire the OnStopRequest out to the observer...
684 nsCOMPtr<nsIRequestObserver> observer = do_QueryReferent(mObserver);
685 if (observer) {
686 LOG(("LOADGROUP [%x]: Firing OnStopRequest for request %x."
687 "(foreground count=%d).\n", this, request, mForegroundCount));
688
689 rv = observer->OnStopRequest(request, ctxt, aStatus);
690
691 #if defined(PR_LOGGING)
692 if (NS_FAILED(rv)) {
693 LOG(("LOADGROUP [%x]: OnStopRequest for request %x FAILED.\n",
694 this, request));
695 }
696 #endif
697 }
698
699 // If that was the last request -> remove ourselves from loadgroup
700 if (mForegroundCount == 0 && mLoadGroup) {
701 mLoadGroup->RemoveRequest(this, nullptr, aStatus);
702 }
703 }
704
705 return rv;
706 }
707
708 // PLDHashTable enumeration callback that appends all items in the
709 // hash to an nsCOMArray
710 static PLDHashOperator
711 AppendRequestsToCOMArray(PLDHashTable *table, PLDHashEntryHdr *hdr,
712 uint32_t number, void *arg)
713 {
714 RequestMapEntry *e = static_cast<RequestMapEntry *>(hdr);
715 static_cast<nsCOMArray<nsIRequest>*>(arg)->AppendObject(e->mKey);
716 return PL_DHASH_NEXT;
717 }
718
719 NS_IMETHODIMP
720 nsLoadGroup::GetRequests(nsISimpleEnumerator * *aRequests)
721 {
722 nsCOMArray<nsIRequest> requests;
723 requests.SetCapacity(mRequests.entryCount);
724
725 PL_DHashTableEnumerate(&mRequests, AppendRequestsToCOMArray, &requests);
726
727 return NS_NewArrayEnumerator(aRequests, requests);
728 }
729
730 NS_IMETHODIMP
731 nsLoadGroup::SetGroupObserver(nsIRequestObserver* aObserver)
732 {
733 mObserver = do_GetWeakReference(aObserver);
734 return NS_OK;
735 }
736
737 NS_IMETHODIMP
738 nsLoadGroup::GetGroupObserver(nsIRequestObserver* *aResult)
739 {
740 nsCOMPtr<nsIRequestObserver> observer = do_QueryReferent(mObserver);
741 *aResult = observer;
742 NS_IF_ADDREF(*aResult);
743 return NS_OK;
744 }
745
746 NS_IMETHODIMP
747 nsLoadGroup::GetActiveCount(uint32_t* aResult)
748 {
749 *aResult = mForegroundCount;
750 return NS_OK;
751 }
752
753 NS_IMETHODIMP
754 nsLoadGroup::GetNotificationCallbacks(nsIInterfaceRequestor **aCallbacks)
755 {
756 NS_ENSURE_ARG_POINTER(aCallbacks);
757 *aCallbacks = mCallbacks;
758 NS_IF_ADDREF(*aCallbacks);
759 return NS_OK;
760 }
761
762 NS_IMETHODIMP
763 nsLoadGroup::SetNotificationCallbacks(nsIInterfaceRequestor *aCallbacks)
764 {
765 mCallbacks = aCallbacks;
766 return NS_OK;
767 }
768
769 NS_IMETHODIMP
770 nsLoadGroup::GetConnectionInfo(nsILoadGroupConnectionInfo **aCI)
771 {
772 NS_ENSURE_ARG_POINTER(aCI);
773 *aCI = mConnectionInfo;
774 NS_IF_ADDREF(*aCI);
775 return NS_OK;
776 }
777
778 ////////////////////////////////////////////////////////////////////////////////
779 // nsILoadGroupChild methods:
780
781 /* attribute nsILoadGroup parentLoadGroup; */
782 NS_IMETHODIMP
783 nsLoadGroup::GetParentLoadGroup(nsILoadGroup * *aParentLoadGroup)
784 {
785 *aParentLoadGroup = nullptr;
786 nsCOMPtr<nsILoadGroup> parent = do_QueryReferent(mParentLoadGroup);
787 if (!parent)
788 return NS_OK;
789 parent.forget(aParentLoadGroup);
790 return NS_OK;
791 }
792
793 NS_IMETHODIMP
794 nsLoadGroup::SetParentLoadGroup(nsILoadGroup *aParentLoadGroup)
795 {
796 mParentLoadGroup = do_GetWeakReference(aParentLoadGroup);
797 return NS_OK;
798 }
799
800 /* readonly attribute nsILoadGroup childLoadGroup; */
801 NS_IMETHODIMP
802 nsLoadGroup::GetChildLoadGroup(nsILoadGroup * *aChildLoadGroup)
803 {
804 NS_ADDREF(*aChildLoadGroup = this);
805 return NS_OK;
806 }
807
808 /* readonly attribute nsILoadGroup rootLoadGroup; */
809 NS_IMETHODIMP
810 nsLoadGroup::GetRootLoadGroup(nsILoadGroup * *aRootLoadGroup)
811 {
812 // first recursively try the root load group of our parent
813 nsCOMPtr<nsILoadGroupChild> ancestor = do_QueryReferent(mParentLoadGroup);
814 if (ancestor)
815 return ancestor->GetRootLoadGroup(aRootLoadGroup);
816
817 // next recursively try the root load group of our own load grop
818 ancestor = do_QueryInterface(mLoadGroup);
819 if (ancestor)
820 return ancestor->GetRootLoadGroup(aRootLoadGroup);
821
822 // finally just return this
823 NS_ADDREF(*aRootLoadGroup = this);
824 return NS_OK;
825 }
826
827 ////////////////////////////////////////////////////////////////////////////////
828 // nsPILoadGroupInternal methods:
829
830 NS_IMETHODIMP
831 nsLoadGroup::OnEndPageLoad(nsIChannel *aDefaultChannel)
832 {
833 // for the moment, nothing to do here.
834 return NS_OK;
835 }
836
837 ////////////////////////////////////////////////////////////////////////////////
838 // nsISupportsPriority methods:
839
840 NS_IMETHODIMP
841 nsLoadGroup::GetPriority(int32_t *aValue)
842 {
843 *aValue = mPriority;
844 return NS_OK;
845 }
846
847 NS_IMETHODIMP
848 nsLoadGroup::SetPriority(int32_t aValue)
849 {
850 return AdjustPriority(aValue - mPriority);
851 }
852
853 NS_IMETHODIMP
854 nsLoadGroup::AdjustPriority(int32_t aDelta)
855 {
856 // Update the priority for each request that supports nsISupportsPriority
857 if (aDelta != 0) {
858 mPriority += aDelta;
859 PL_DHashTableEnumerate(&mRequests, RescheduleRequests, &aDelta);
860 }
861 return NS_OK;
862 }
863
864 NS_IMETHODIMP
865 nsLoadGroup::GetDefaultLoadFlags(uint32_t *aFlags)
866 {
867 *aFlags = mDefaultLoadFlags;
868 return NS_OK;
869 }
870
871 NS_IMETHODIMP
872 nsLoadGroup::SetDefaultLoadFlags(uint32_t aFlags)
873 {
874 mDefaultLoadFlags = aFlags;
875 return NS_OK;
876 }
877
878
879 ////////////////////////////////////////////////////////////////////////////////
880
881 void
882 nsLoadGroup::TelemetryReport()
883 {
884 if (mDefaultLoadIsTimed) {
885 Telemetry::Accumulate(Telemetry::HTTP_REQUEST_PER_PAGE, mTimedRequests);
886 if (mTimedRequests) {
887 Telemetry::Accumulate(Telemetry::HTTP_REQUEST_PER_PAGE_FROM_CACHE,
888 mCachedRequests * 100 / mTimedRequests);
889 }
890
891 nsCOMPtr<nsITimedChannel> timedChannel =
892 do_QueryInterface(mDefaultLoadRequest);
893 if (timedChannel)
894 TelemetryReportChannel(timedChannel, true);
895 }
896
897 mTimedRequests = 0;
898 mCachedRequests = 0;
899 mDefaultLoadIsTimed = false;
900 }
901
902 void
903 nsLoadGroup::TelemetryReportChannel(nsITimedChannel *aTimedChannel,
904 bool aDefaultRequest)
905 {
906 nsresult rv;
907 bool timingEnabled;
908 rv = aTimedChannel->GetTimingEnabled(&timingEnabled);
909 if (NS_FAILED(rv) || !timingEnabled)
910 return;
911
912 TimeStamp asyncOpen;
913 rv = aTimedChannel->GetAsyncOpen(&asyncOpen);
914 // We do not check !asyncOpen.IsNull() bellow, prevent ASSERTIONs this way
915 if (NS_FAILED(rv) || asyncOpen.IsNull())
916 return;
917
918 TimeStamp cacheReadStart;
919 rv = aTimedChannel->GetCacheReadStart(&cacheReadStart);
920 if (NS_FAILED(rv))
921 return;
922
923 TimeStamp cacheReadEnd;
924 rv = aTimedChannel->GetCacheReadEnd(&cacheReadEnd);
925 if (NS_FAILED(rv))
926 return;
927
928 TimeStamp domainLookupStart;
929 rv = aTimedChannel->GetDomainLookupStart(&domainLookupStart);
930 if (NS_FAILED(rv))
931 return;
932
933 TimeStamp domainLookupEnd;
934 rv = aTimedChannel->GetDomainLookupEnd(&domainLookupEnd);
935 if (NS_FAILED(rv))
936 return;
937
938 TimeStamp connectStart;
939 rv = aTimedChannel->GetConnectStart(&connectStart);
940 if (NS_FAILED(rv))
941 return;
942
943 TimeStamp connectEnd;
944 rv = aTimedChannel->GetConnectEnd(&connectEnd);
945 if (NS_FAILED(rv))
946 return;
947
948 TimeStamp requestStart;
949 rv = aTimedChannel->GetRequestStart(&requestStart);
950 if (NS_FAILED(rv))
951 return;
952
953 TimeStamp responseStart;
954 rv = aTimedChannel->GetResponseStart(&responseStart);
955 if (NS_FAILED(rv))
956 return;
957
958 TimeStamp responseEnd;
959 rv = aTimedChannel->GetResponseEnd(&responseEnd);
960 if (NS_FAILED(rv))
961 return;
962
963 #define HTTP_REQUEST_HISTOGRAMS(prefix) \
964 if (!domainLookupStart.IsNull()) { \
965 Telemetry::AccumulateTimeDelta( \
966 Telemetry::HTTP_##prefix##_DNS_ISSUE_TIME, \
967 asyncOpen, domainLookupStart); \
968 } \
969 \
970 if (!domainLookupStart.IsNull() && !domainLookupEnd.IsNull()) { \
971 Telemetry::AccumulateTimeDelta( \
972 Telemetry::HTTP_##prefix##_DNS_LOOKUP_TIME, \
973 domainLookupStart, domainLookupEnd); \
974 } \
975 \
976 if (!connectStart.IsNull() && !connectEnd.IsNull()) { \
977 Telemetry::AccumulateTimeDelta( \
978 Telemetry::HTTP_##prefix##_TCP_CONNECTION, \
979 connectStart, connectEnd); \
980 } \
981 \
982 \
983 if (!requestStart.IsNull() && !responseEnd.IsNull()) { \
984 Telemetry::AccumulateTimeDelta( \
985 Telemetry::HTTP_##prefix##_OPEN_TO_FIRST_SENT, \
986 asyncOpen, requestStart); \
987 \
988 Telemetry::AccumulateTimeDelta( \
989 Telemetry::HTTP_##prefix##_FIRST_SENT_TO_LAST_RECEIVED, \
990 requestStart, responseEnd); \
991 \
992 if (cacheReadStart.IsNull() && !responseStart.IsNull()) { \
993 Telemetry::AccumulateTimeDelta( \
994 Telemetry::HTTP_##prefix##_OPEN_TO_FIRST_RECEIVED, \
995 asyncOpen, responseStart); \
996 } \
997 } \
998 \
999 if (!cacheReadStart.IsNull() && !cacheReadEnd.IsNull()) { \
1000 if (!CacheObserver::UseNewCache()) { \
1001 Telemetry::AccumulateTimeDelta( \
1002 Telemetry::HTTP_##prefix##_OPEN_TO_FIRST_FROM_CACHE, \
1003 asyncOpen, cacheReadStart); \
1004 } else { \
1005 Telemetry::AccumulateTimeDelta( \
1006 Telemetry::HTTP_##prefix##_OPEN_TO_FIRST_FROM_CACHE_V2, \
1007 asyncOpen, cacheReadStart); \
1008 } \
1009 \
1010 if (!CacheObserver::UseNewCache()) { \
1011 Telemetry::AccumulateTimeDelta( \
1012 Telemetry::HTTP_##prefix##_CACHE_READ_TIME, \
1013 cacheReadStart, cacheReadEnd); \
1014 } else { \
1015 Telemetry::AccumulateTimeDelta( \
1016 Telemetry::HTTP_##prefix##_CACHE_READ_TIME_V2, \
1017 cacheReadStart, cacheReadEnd); \
1018 } \
1019 \
1020 if (!requestStart.IsNull() && !responseEnd.IsNull()) { \
1021 Telemetry::AccumulateTimeDelta( \
1022 Telemetry::HTTP_##prefix##_REVALIDATION, \
1023 requestStart, responseEnd); \
1024 } \
1025 } \
1026 \
1027 if (!cacheReadEnd.IsNull()) { \
1028 Telemetry::AccumulateTimeDelta( \
1029 Telemetry::HTTP_##prefix##_COMPLETE_LOAD, \
1030 asyncOpen, cacheReadEnd); \
1031 \
1032 if (!CacheObserver::UseNewCache()) { \
1033 Telemetry::AccumulateTimeDelta( \
1034 Telemetry::HTTP_##prefix##_COMPLETE_LOAD_CACHED, \
1035 asyncOpen, cacheReadEnd); \
1036 } else { \
1037 Telemetry::AccumulateTimeDelta( \
1038 Telemetry::HTTP_##prefix##_COMPLETE_LOAD_CACHED_V2, \
1039 asyncOpen, cacheReadEnd); \
1040 } \
1041 } \
1042 else if (!responseEnd.IsNull()) { \
1043 if (!CacheObserver::UseNewCache()) { \
1044 Telemetry::AccumulateTimeDelta( \
1045 Telemetry::HTTP_##prefix##_COMPLETE_LOAD, \
1046 asyncOpen, responseEnd); \
1047 Telemetry::AccumulateTimeDelta( \
1048 Telemetry::HTTP_##prefix##_COMPLETE_LOAD_NET, \
1049 asyncOpen, responseEnd); \
1050 } else { \
1051 Telemetry::AccumulateTimeDelta( \
1052 Telemetry::HTTP_##prefix##_COMPLETE_LOAD_V2, \
1053 asyncOpen, responseEnd); \
1054 Telemetry::AccumulateTimeDelta( \
1055 Telemetry::HTTP_##prefix##_COMPLETE_LOAD_NET_V2, \
1056 asyncOpen, responseEnd); \
1057 } \
1058 }
1059
1060 if (aDefaultRequest) {
1061 HTTP_REQUEST_HISTOGRAMS(PAGE)
1062 } else {
1063 HTTP_REQUEST_HISTOGRAMS(SUB)
1064 }
1065 #undef HTTP_REQUEST_HISTOGRAMS
1066 }
1067
1068 nsresult nsLoadGroup::MergeLoadFlags(nsIRequest *aRequest, nsLoadFlags& outFlags)
1069 {
1070 nsresult rv;
1071 nsLoadFlags flags, oldFlags;
1072
1073 rv = aRequest->GetLoadFlags(&flags);
1074 if (NS_FAILED(rv))
1075 return rv;
1076
1077 oldFlags = flags;
1078
1079 // Inherit the following bits...
1080 flags |= (mLoadFlags & (LOAD_BACKGROUND |
1081 LOAD_BYPASS_CACHE |
1082 LOAD_FROM_CACHE |
1083 VALIDATE_ALWAYS |
1084 VALIDATE_ONCE_PER_SESSION |
1085 VALIDATE_NEVER));
1086
1087 // ... and force the default flags.
1088 flags |= mDefaultLoadFlags;
1089
1090 if (flags != oldFlags)
1091 rv = aRequest->SetLoadFlags(flags);
1092
1093 outFlags = flags;
1094 return rv;
1095 }
1096
1097 // nsLoadGroupConnectionInfo
1098
1099 class nsLoadGroupConnectionInfo MOZ_FINAL : public nsILoadGroupConnectionInfo
1100 {
1101 public:
1102 NS_DECL_THREADSAFE_ISUPPORTS
1103 NS_DECL_NSILOADGROUPCONNECTIONINFO
1104
1105 nsLoadGroupConnectionInfo();
1106 private:
1107 Atomic<uint32_t> mBlockingTransactionCount;
1108 nsAutoPtr<mozilla::net::SpdyPushCache> mSpdyCache;
1109 };
1110
1111 NS_IMPL_ISUPPORTS(nsLoadGroupConnectionInfo, nsILoadGroupConnectionInfo)
1112
1113 nsLoadGroupConnectionInfo::nsLoadGroupConnectionInfo()
1114 : mBlockingTransactionCount(0)
1115 {
1116 }
1117
1118 NS_IMETHODIMP
1119 nsLoadGroupConnectionInfo::GetBlockingTransactionCount(uint32_t *aBlockingTransactionCount)
1120 {
1121 NS_ENSURE_ARG_POINTER(aBlockingTransactionCount);
1122 *aBlockingTransactionCount = mBlockingTransactionCount;
1123 return NS_OK;
1124 }
1125
1126 NS_IMETHODIMP
1127 nsLoadGroupConnectionInfo::AddBlockingTransaction()
1128 {
1129 mBlockingTransactionCount++;
1130 return NS_OK;
1131 }
1132
1133 NS_IMETHODIMP
1134 nsLoadGroupConnectionInfo::RemoveBlockingTransaction(uint32_t *_retval)
1135 {
1136 NS_ENSURE_ARG_POINTER(_retval);
1137 mBlockingTransactionCount--;
1138 *_retval = mBlockingTransactionCount;
1139 return NS_OK;
1140 }
1141
1142 /* [noscript] attribute SpdyPushCachePtr spdyPushCache; */
1143 NS_IMETHODIMP
1144 nsLoadGroupConnectionInfo::GetSpdyPushCache(mozilla::net::SpdyPushCache **aSpdyPushCache)
1145 {
1146 *aSpdyPushCache = mSpdyCache.get();
1147 return NS_OK;
1148 }
1149
1150 NS_IMETHODIMP
1151 nsLoadGroupConnectionInfo::SetSpdyPushCache(mozilla::net::SpdyPushCache *aSpdyPushCache)
1152 {
1153 mSpdyCache = aSpdyPushCache;
1154 return NS_OK;
1155 }
1156
1157 nsresult nsLoadGroup::Init()
1158 {
1159 static const PLDHashTableOps hash_table_ops =
1160 {
1161 PL_DHashAllocTable,
1162 PL_DHashFreeTable,
1163 PL_DHashVoidPtrKeyStub,
1164 RequestHashMatchEntry,
1165 PL_DHashMoveEntryStub,
1166 RequestHashClearEntry,
1167 PL_DHashFinalizeStub,
1168 RequestHashInitEntry
1169 };
1170
1171 PL_DHashTableInit(&mRequests, &hash_table_ops, nullptr,
1172 sizeof(RequestMapEntry), 16);
1173
1174 mConnectionInfo = new nsLoadGroupConnectionInfo();
1175
1176 return NS_OK;
1177 }
1178
1179 #undef LOG

mercurial