Coverage Report

Created: 2018-09-25 14:53

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