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