netwerk/base/src/nsLoadGroup.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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/. */
     7 #include "mozilla/DebugOnly.h"
     9 #include "nsLoadGroup.h"
    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"
    27 using namespace mozilla;
    28 using namespace mozilla::net;
    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
    45 #undef LOG
    46 #define LOG(args) PR_LOG(gLoadGroupLog, PR_LOG_DEBUG, args)
    48 ////////////////////////////////////////////////////////////////////////////////
    50 class RequestMapEntry : public PLDHashEntryHdr
    51 {
    52 public:
    53     RequestMapEntry(nsIRequest *aRequest) :
    54         mKey(aRequest)
    55     {
    56     }
    58     nsCOMPtr<nsIRequest> mKey;
    59 };
    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);
    69     return e->mKey == request;
    70 }
    72 static void
    73 RequestHashClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry)
    74 {
    75     RequestMapEntry *e = static_cast<RequestMapEntry *>(entry);
    77     // An entry is being cleared, let the entry do its own cleanup.
    78     e->~RequestMapEntry();
    79 }
    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);
    88     // Initialize the entry with placement new
    89     new (entry) RequestMapEntry(request);
    90     return true;
    91 }
    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 }
   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);
   109     RescheduleRequest(e->mKey, *delta);
   110     return PL_DHASH_NEXT;
   111 }
   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);
   128 #if defined(PR_LOGGING)
   129     // Initialize the global PRLogModule for nsILoadGroup logging
   130     if (nullptr == gLoadGroupLog)
   131         gLoadGroupLog = PR_NewLogModule("LoadGroup");
   132 #endif
   134     LOG(("LOADGROUP [%x]: Created.\n", this));
   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 }
   142 nsLoadGroup::~nsLoadGroup()
   143 {
   144     DebugOnly<nsresult> rv = Cancel(NS_BINDING_ABORTED);
   145     NS_ASSERTION(NS_SUCCEEDED(rv), "Cancel failed");
   147     if (mRequests.ops) {
   148         PL_DHashTableFinish(&mRequests);
   149     }
   151     mDefaultLoadRequest = 0;
   153     LOG(("LOADGROUP [%x]: Destroyed.\n", this));
   154 }
   157 ////////////////////////////////////////////////////////////////////////////////
   158 // nsISupports methods:
   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
   170 ////////////////////////////////////////////////////////////////////////////////
   171 // nsIRequest methods:
   173 NS_IMETHODIMP
   174 nsLoadGroup::GetName(nsACString &result)
   175 {
   176     // XXX is this the right "name" for a load group?
   178     if (!mDefaultLoadRequest) {
   179         result.Truncate();
   180         return NS_OK;
   181     }
   183     return mDefaultLoadRequest->GetName(result);
   184 }
   186 NS_IMETHODIMP
   187 nsLoadGroup::IsPending(bool *aResult)
   188 {
   189     *aResult = (mForegroundCount > 0) ? true : false;
   190     return NS_OK;
   191 }
   193 NS_IMETHODIMP
   194 nsLoadGroup::GetStatus(nsresult *status)
   195 {
   196     if (NS_SUCCEEDED(mStatus) && mDefaultLoadRequest)
   197         return mDefaultLoadRequest->GetStatus(status);
   199     *status = mStatus;
   200     return NS_OK; 
   201 }
   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);
   212     nsIRequest *request = e->mKey;
   213     NS_ASSERTION(request, "What? Null key in pldhash entry?");
   215     bool ok = array->AppendElement(request) != nullptr;
   217     if (!ok) {
   218         return PL_DHASH_STOP;
   219     }
   221     NS_ADDREF(request);
   223     return PL_DHASH_NEXT;
   224 }
   226 NS_IMETHODIMP
   227 nsLoadGroup::Cancel(nsresult status)
   228 {
   229     MOZ_ASSERT(NS_IsMainThread());
   231     NS_ASSERTION(NS_FAILED(status), "shouldn't cancel with a success code");
   232     nsresult rv;
   233     uint32_t count = mRequests.entryCount;
   235     nsAutoTArray<nsIRequest*, 8> requests;
   237     PL_DHashTableEnumerate(&mRequests, AppendRequestsToArray,
   238                            static_cast<nsTArray<nsIRequest*> *>(&requests));
   240     if (requests.Length() != count) {
   241         for (uint32_t i = 0, len = requests.Length(); i < len; ++i) {
   242             NS_RELEASE(requests[i]);
   243         }
   245         return NS_ERROR_OUT_OF_MEMORY;
   246     }
   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;
   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;
   258     nsresult firstError = NS_OK;
   260     while (count > 0) {
   261         nsIRequest* request = requests.ElementAt(--count);
   263         NS_ASSERTION(request, "NULL request found in list.");
   265         RequestMapEntry *entry =
   266             static_cast<RequestMapEntry *>
   267                        (PL_DHashTableOperate(&mRequests, request,
   268                                                 PL_DHASH_LOOKUP));
   270         if (PL_DHASH_ENTRY_IS_FREE(entry)) {
   271             // |request| was removed already
   273             NS_RELEASE(request);
   275             continue;
   276         }
   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
   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);
   293         // Cancel the request...
   294         rv = request->Cancel(status);
   296         // Remember the first failure and return it...
   297         if (NS_FAILED(rv) && NS_SUCCEEDED(firstError))
   298             firstError = rv;
   300         NS_RELEASE(request);
   301     }
   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
   308     mStatus = NS_OK;
   309     mIsCanceling = false;
   311     return firstError;
   312 }
   315 NS_IMETHODIMP
   316 nsLoadGroup::Suspend()
   317 {
   318     nsresult rv, firstError;
   319     uint32_t count = mRequests.entryCount;
   321     nsAutoTArray<nsIRequest*, 8> requests;
   323     PL_DHashTableEnumerate(&mRequests, AppendRequestsToArray,
   324                            static_cast<nsTArray<nsIRequest*> *>(&requests));
   326     if (requests.Length() != count) {
   327         for (uint32_t i = 0, len = requests.Length(); i < len; ++i) {
   328             NS_RELEASE(requests[i]);
   329         }
   331         return NS_ERROR_OUT_OF_MEMORY;
   332     }
   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);
   342         NS_ASSERTION(request, "NULL request found in list.");
   343         if (!request)
   344             continue;
   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
   353         // Suspend the request...
   354         rv = request->Suspend();
   356         // Remember the first failure and return it...
   357         if (NS_FAILED(rv) && NS_SUCCEEDED(firstError))
   358             firstError = rv;
   360         NS_RELEASE(request);
   361     }
   363     return firstError;
   364 }
   367 NS_IMETHODIMP
   368 nsLoadGroup::Resume()
   369 {
   370     nsresult rv, firstError;
   371     uint32_t count = mRequests.entryCount;
   373     nsAutoTArray<nsIRequest*, 8> requests;
   375     PL_DHashTableEnumerate(&mRequests, AppendRequestsToArray,
   376                            static_cast<nsTArray<nsIRequest*> *>(&requests));
   378     if (requests.Length() != count) {
   379         for (uint32_t i = 0, len = requests.Length(); i < len; ++i) {
   380             NS_RELEASE(requests[i]);
   381         }
   383         return NS_ERROR_OUT_OF_MEMORY;
   384     }
   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);
   394         NS_ASSERTION(request, "NULL request found in list.");
   395         if (!request)
   396             continue;
   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
   405         // Resume the request...
   406         rv = request->Resume();
   408         // Remember the first failure and return it...
   409         if (NS_FAILED(rv) && NS_SUCCEEDED(firstError))
   410             firstError = rv;
   412         NS_RELEASE(request);
   413     }
   415     return firstError;
   416 }
   418 NS_IMETHODIMP
   419 nsLoadGroup::GetLoadFlags(uint32_t *aLoadFlags)
   420 {
   421     *aLoadFlags = mLoadFlags;
   422     return NS_OK;
   423 }
   425 NS_IMETHODIMP
   426 nsLoadGroup::SetLoadFlags(uint32_t aLoadFlags)
   427 {
   428     mLoadFlags = aLoadFlags;
   429     return NS_OK;
   430 }
   432 NS_IMETHODIMP
   433 nsLoadGroup::GetLoadGroup(nsILoadGroup **loadGroup)
   434 {
   435     *loadGroup = mLoadGroup;
   436     NS_IF_ADDREF(*loadGroup);
   437     return NS_OK;
   438 }
   440 NS_IMETHODIMP
   441 nsLoadGroup::SetLoadGroup(nsILoadGroup *loadGroup)
   442 {
   443     mLoadGroup = loadGroup;
   444     return NS_OK;
   445 }
   447 ////////////////////////////////////////////////////////////////////////////////
   448 // nsILoadGroup methods:
   450 NS_IMETHODIMP
   451 nsLoadGroup::GetDefaultLoadRequest(nsIRequest * *aRequest)
   452 {
   453     *aRequest = mDefaultLoadRequest;
   454     NS_IF_ADDREF(*aRequest);
   455     return NS_OK;
   456 }
   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;
   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 }
   482 NS_IMETHODIMP
   483 nsLoadGroup::AddRequest(nsIRequest *request, nsISupports* ctxt)
   484 {
   485     nsresult rv;
   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 */
   496 #ifdef DEBUG
   497     {
   498       RequestMapEntry *entry =
   499           static_cast<RequestMapEntry *>
   500                      (PL_DHashTableOperate(&mRequests, request,
   501                                           PL_DHASH_LOOKUP));
   503       NS_ASSERTION(PL_DHASH_ENTRY_IS_FREE(entry),
   504                    "Entry added to loadgroup twice, don't do that");
   505     }
   506 #endif
   508     //
   509     // Do not add the channel, if the loadgroup is being canceled...
   510     //
   511     if (mIsCanceling) {
   513 #if defined(PR_LOGGING)
   514         LOG(("LOADGROUP [%x]: AddChannel() ABORTED because LoadGroup is"
   515              " being canceled!!\n", this));
   516 #endif /* PR_LOGGING */
   518         return NS_BINDING_ABORTED;
   519     }
   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;
   531     //
   532     // Add the request to the list of active requests...
   533     //
   535     RequestMapEntry *entry =
   536         static_cast<RequestMapEntry *>
   537                    (PL_DHashTableOperate(&mRequests, request,
   538                                         PL_DHASH_ADD));
   540     if (!entry) {
   541         return NS_ERROR_OUT_OF_MEMORY;
   542     }
   544     if (mPriority != 0)
   545         RescheduleRequest(request, mPriority);
   547     nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(request);
   548     if (timedChannel)
   549         timedChannel->SetTimingEnabled(true);
   551     if (!(flags & nsIRequest::LOAD_BACKGROUND)) {
   552         // Update the count of foreground URIs..
   553         mForegroundCount += 1;
   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));
   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                 //
   575                 PL_DHashTableOperate(&mRequests, request, PL_DHASH_REMOVE);
   577                 rv = NS_OK;
   579                 mForegroundCount -= 1;
   580             }
   581         }
   583         // Ensure that we're part of our loadgroup while pending
   584         if (mForegroundCount == 1 && mLoadGroup) {
   585             mLoadGroup->AddRequest(this, nullptr);
   586         }
   588     }
   590     return rv;
   591 }
   593 NS_IMETHODIMP
   594 nsLoadGroup::RemoveRequest(nsIRequest *request, nsISupports* ctxt,
   595                            nsresult aStatus)
   596 {
   597     NS_ENSURE_ARG_POINTER(request);
   598     nsresult rv;
   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
   609     // Make sure we have a owning reference to the request we're about
   610     // to remove.
   612     nsCOMPtr<nsIRequest> kungFuDeathGrip(request);
   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));
   624     if (PL_DHASH_ENTRY_IS_FREE(entry)) {
   625         LOG(("LOADGROUP [%x]: Unable to remove request %x. Not in group!\n",
   626             this, request));
   628         return NS_ERROR_FAILURE;
   629     }
   631     PL_DHashTableRawRemove(&mRequests, entry);
   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             }
   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             }
   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             }
   663             TelemetryReportChannel(timedChannel, false);
   664         }
   665     }
   667     if (mRequests.entryCount == 0) {
   668         TelemetryReport();
   669     }
   671     // Undo any group priority delta...
   672     if (mPriority != 0)
   673         RescheduleRequest(request, -mPriority);
   675     nsLoadFlags flags;
   676     rv = request->GetLoadFlags(&flags);
   677     if (NS_FAILED(rv)) return rv;
   679     if (!(flags & nsIRequest::LOAD_BACKGROUND)) {
   680         NS_ASSERTION(mForegroundCount > 0, "ForegroundCount messed up");
   681         mForegroundCount -= 1;
   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));
   689             rv = observer->OnStopRequest(request, ctxt, aStatus);
   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         }
   699         // If that was the last request -> remove ourselves from loadgroup
   700         if (mForegroundCount == 0 && mLoadGroup) {
   701             mLoadGroup->RemoveRequest(this, nullptr, aStatus);
   702         }
   703     }
   705     return rv;
   706 }
   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 }
   719 NS_IMETHODIMP
   720 nsLoadGroup::GetRequests(nsISimpleEnumerator * *aRequests)
   721 {
   722     nsCOMArray<nsIRequest> requests;
   723     requests.SetCapacity(mRequests.entryCount);
   725     PL_DHashTableEnumerate(&mRequests, AppendRequestsToCOMArray, &requests);
   727     return NS_NewArrayEnumerator(aRequests, requests);
   728 }
   730 NS_IMETHODIMP
   731 nsLoadGroup::SetGroupObserver(nsIRequestObserver* aObserver)
   732 {
   733     mObserver = do_GetWeakReference(aObserver);
   734     return NS_OK;
   735 }
   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 }
   746 NS_IMETHODIMP
   747 nsLoadGroup::GetActiveCount(uint32_t* aResult)
   748 {
   749     *aResult = mForegroundCount;
   750     return NS_OK;
   751 }
   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 }
   762 NS_IMETHODIMP
   763 nsLoadGroup::SetNotificationCallbacks(nsIInterfaceRequestor *aCallbacks)
   764 {
   765     mCallbacks = aCallbacks;
   766     return NS_OK;
   767 }
   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 }
   778 ////////////////////////////////////////////////////////////////////////////////
   779 // nsILoadGroupChild methods:
   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 }
   793 NS_IMETHODIMP
   794 nsLoadGroup::SetParentLoadGroup(nsILoadGroup *aParentLoadGroup)
   795 {
   796     mParentLoadGroup = do_GetWeakReference(aParentLoadGroup);
   797     return NS_OK;
   798 }
   800 /* readonly attribute nsILoadGroup childLoadGroup; */
   801 NS_IMETHODIMP
   802 nsLoadGroup::GetChildLoadGroup(nsILoadGroup * *aChildLoadGroup)
   803 {
   804     NS_ADDREF(*aChildLoadGroup = this);
   805     return NS_OK;
   806 }
   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);
   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);
   822     // finally just return this
   823     NS_ADDREF(*aRootLoadGroup = this);
   824     return NS_OK;
   825 }
   827 ////////////////////////////////////////////////////////////////////////////////
   828 // nsPILoadGroupInternal methods:
   830 NS_IMETHODIMP
   831 nsLoadGroup::OnEndPageLoad(nsIChannel *aDefaultChannel)
   832 {
   833     // for the moment, nothing to do here.
   834     return NS_OK;
   835 }
   837 ////////////////////////////////////////////////////////////////////////////////
   838 // nsISupportsPriority methods:
   840 NS_IMETHODIMP
   841 nsLoadGroup::GetPriority(int32_t *aValue)
   842 {
   843     *aValue = mPriority;
   844     return NS_OK;
   845 }
   847 NS_IMETHODIMP
   848 nsLoadGroup::SetPriority(int32_t aValue)
   849 {
   850     return AdjustPriority(aValue - mPriority);
   851 }
   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 }
   864 NS_IMETHODIMP
   865 nsLoadGroup::GetDefaultLoadFlags(uint32_t *aFlags)
   866 {
   867     *aFlags = mDefaultLoadFlags;
   868     return NS_OK;
   869 }
   871 NS_IMETHODIMP
   872 nsLoadGroup::SetDefaultLoadFlags(uint32_t aFlags)
   873 {
   874     mDefaultLoadFlags = aFlags;
   875     return NS_OK;
   876 }
   879 ////////////////////////////////////////////////////////////////////////////////
   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         }
   891         nsCOMPtr<nsITimedChannel> timedChannel =
   892             do_QueryInterface(mDefaultLoadRequest);
   893         if (timedChannel)
   894             TelemetryReportChannel(timedChannel, true);
   895     }
   897     mTimedRequests = 0;
   898     mCachedRequests = 0;
   899     mDefaultLoadIsTimed = false;
   900 }
   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;
   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;
   918     TimeStamp cacheReadStart;
   919     rv = aTimedChannel->GetCacheReadStart(&cacheReadStart);
   920     if (NS_FAILED(rv))
   921         return;
   923     TimeStamp cacheReadEnd;
   924     rv = aTimedChannel->GetCacheReadEnd(&cacheReadEnd);
   925     if (NS_FAILED(rv))
   926         return;
   928     TimeStamp domainLookupStart;
   929     rv = aTimedChannel->GetDomainLookupStart(&domainLookupStart);
   930     if (NS_FAILED(rv))
   931         return;
   933     TimeStamp domainLookupEnd;
   934     rv = aTimedChannel->GetDomainLookupEnd(&domainLookupEnd);
   935     if (NS_FAILED(rv))
   936         return;
   938     TimeStamp connectStart;
   939     rv = aTimedChannel->GetConnectStart(&connectStart);
   940     if (NS_FAILED(rv))
   941         return;
   943     TimeStamp connectEnd;
   944     rv = aTimedChannel->GetConnectEnd(&connectEnd);
   945     if (NS_FAILED(rv))
   946         return;
   948     TimeStamp requestStart;
   949     rv = aTimedChannel->GetRequestStart(&requestStart);
   950     if (NS_FAILED(rv))
   951         return;
   953     TimeStamp responseStart;
   954     rv = aTimedChannel->GetResponseStart(&responseStart);
   955     if (NS_FAILED(rv))
   956         return;
   958     TimeStamp responseEnd;
   959     rv = aTimedChannel->GetResponseEnd(&responseEnd);
   960     if (NS_FAILED(rv))
   961         return;
   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         }                                                                      \
  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         }                                                                      \
  1020         if (!requestStart.IsNull() && !responseEnd.IsNull()) {                 \
  1021             Telemetry::AccumulateTimeDelta(                                    \
  1022                 Telemetry::HTTP_##prefix##_REVALIDATION,                       \
  1023                 requestStart, responseEnd);                                    \
  1024         }                                                                      \
  1025     }                                                                          \
  1027     if (!cacheReadEnd.IsNull()) {                                              \
  1028         Telemetry::AccumulateTimeDelta(                                        \
  1029             Telemetry::HTTP_##prefix##_COMPLETE_LOAD,                          \
  1030             asyncOpen, cacheReadEnd);                                          \
  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         }                                                                      \
  1060     if (aDefaultRequest) {
  1061         HTTP_REQUEST_HISTOGRAMS(PAGE)
  1062     } else {
  1063         HTTP_REQUEST_HISTOGRAMS(SUB)
  1065 #undef HTTP_REQUEST_HISTOGRAMS
  1068 nsresult nsLoadGroup::MergeLoadFlags(nsIRequest *aRequest, nsLoadFlags& outFlags)
  1070     nsresult rv;
  1071     nsLoadFlags flags, oldFlags;
  1073     rv = aRequest->GetLoadFlags(&flags);
  1074     if (NS_FAILED(rv)) 
  1075         return rv;
  1077     oldFlags = flags;
  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));
  1087     // ... and force the default flags.
  1088     flags |= mDefaultLoadFlags;
  1090     if (flags != oldFlags)
  1091         rv = aRequest->SetLoadFlags(flags);
  1093     outFlags = flags;
  1094     return rv;
  1097 // nsLoadGroupConnectionInfo
  1099 class nsLoadGroupConnectionInfo MOZ_FINAL : public nsILoadGroupConnectionInfo
  1101 public:
  1102     NS_DECL_THREADSAFE_ISUPPORTS
  1103     NS_DECL_NSILOADGROUPCONNECTIONINFO
  1105     nsLoadGroupConnectionInfo();
  1106 private:
  1107     Atomic<uint32_t>       mBlockingTransactionCount;
  1108     nsAutoPtr<mozilla::net::SpdyPushCache> mSpdyCache;
  1109 };
  1111 NS_IMPL_ISUPPORTS(nsLoadGroupConnectionInfo, nsILoadGroupConnectionInfo)
  1113 nsLoadGroupConnectionInfo::nsLoadGroupConnectionInfo()
  1114     : mBlockingTransactionCount(0)
  1118 NS_IMETHODIMP
  1119 nsLoadGroupConnectionInfo::GetBlockingTransactionCount(uint32_t *aBlockingTransactionCount)
  1121     NS_ENSURE_ARG_POINTER(aBlockingTransactionCount);
  1122     *aBlockingTransactionCount = mBlockingTransactionCount;
  1123     return NS_OK;
  1126 NS_IMETHODIMP
  1127 nsLoadGroupConnectionInfo::AddBlockingTransaction()
  1129     mBlockingTransactionCount++;
  1130     return NS_OK;
  1133 NS_IMETHODIMP
  1134 nsLoadGroupConnectionInfo::RemoveBlockingTransaction(uint32_t *_retval)
  1136     NS_ENSURE_ARG_POINTER(_retval);
  1137         mBlockingTransactionCount--;
  1138         *_retval = mBlockingTransactionCount;
  1139     return NS_OK;
  1142 /* [noscript] attribute SpdyPushCachePtr spdyPushCache; */
  1143 NS_IMETHODIMP
  1144 nsLoadGroupConnectionInfo::GetSpdyPushCache(mozilla::net::SpdyPushCache **aSpdyPushCache)
  1146     *aSpdyPushCache = mSpdyCache.get();
  1147     return NS_OK;
  1150 NS_IMETHODIMP
  1151 nsLoadGroupConnectionInfo::SetSpdyPushCache(mozilla::net::SpdyPushCache *aSpdyPushCache)
  1153     mSpdyCache = aSpdyPushCache;
  1154     return NS_OK;
  1157 nsresult nsLoadGroup::Init()
  1159     static const PLDHashTableOps hash_table_ops =
  1161         PL_DHashAllocTable,
  1162         PL_DHashFreeTable,
  1163         PL_DHashVoidPtrKeyStub,
  1164         RequestHashMatchEntry,
  1165         PL_DHashMoveEntryStub,
  1166         RequestHashClearEntry,
  1167         PL_DHashFinalizeStub,
  1168         RequestHashInitEntry
  1169     };
  1171     PL_DHashTableInit(&mRequests, &hash_table_ops, nullptr,
  1172                       sizeof(RequestMapEntry), 16);
  1174     mConnectionInfo = new nsLoadGroupConnectionInfo();
  1176     return NS_OK;
  1179 #undef LOG

mercurial