LCOV - code coverage report
Current view: top level - uriloader/prefetch - nsPrefetchService.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 462 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 45 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : #include "nsPrefetchService.h"
       7             : 
       8             : #include "mozilla/AsyncEventDispatcher.h"
       9             : #include "mozilla/Attributes.h"
      10             : #include "mozilla/CORSMode.h"
      11             : #include "mozilla/dom/HTMLLinkElement.h"
      12             : #include "mozilla/Preferences.h"
      13             : 
      14             : #include "nsICacheEntry.h"
      15             : #include "nsIServiceManager.h"
      16             : #include "nsICategoryManager.h"
      17             : #include "nsIObserverService.h"
      18             : #include "nsIWebProgress.h"
      19             : #include "nsCURILoader.h"
      20             : #include "nsICacheInfoChannel.h"
      21             : #include "nsIHttpChannel.h"
      22             : #include "nsIURL.h"
      23             : #include "nsISimpleEnumerator.h"
      24             : #include "nsISupportsPriority.h"
      25             : #include "nsNetUtil.h"
      26             : #include "nsString.h"
      27             : #include "nsXPIDLString.h"
      28             : #include "nsReadableUtils.h"
      29             : #include "nsStreamUtils.h"
      30             : #include "nsAutoPtr.h"
      31             : #include "prtime.h"
      32             : #include "mozilla/Logging.h"
      33             : #include "plstr.h"
      34             : #include "nsIAsyncVerifyRedirectCallback.h"
      35             : #include "nsIDOMNode.h"
      36             : #include "nsINode.h"
      37             : #include "nsIDocument.h"
      38             : #include "nsContentUtils.h"
      39             : #include "nsStyleLinkElement.h"
      40             : #include "mozilla/AsyncEventDispatcher.h"
      41             : 
      42             : using namespace mozilla;
      43             : 
      44             : //
      45             : // To enable logging (see mozilla/Logging.h for full details):
      46             : //
      47             : //    set MOZ_LOG=nsPrefetch:5
      48             : //    set MOZ_LOG_FILE=prefetch.log
      49             : //
      50             : // this enables LogLevel::Debug level information and places all output in
      51             : // the file prefetch.log
      52             : //
      53             : static LazyLogModule gPrefetchLog("nsPrefetch");
      54             : 
      55             : #undef LOG
      56             : #define LOG(args) MOZ_LOG(gPrefetchLog, mozilla::LogLevel::Debug, args)
      57             : 
      58             : #undef LOG_ENABLED
      59             : #define LOG_ENABLED() MOZ_LOG_TEST(gPrefetchLog, mozilla::LogLevel::Debug)
      60             : 
      61             : #define PREFETCH_PREF "network.prefetch-next"
      62             : #define PRELOAD_PREF "network.preload"
      63             : #define PARALLELISM_PREF "network.prefetch-next.parallelism"
      64             : #define AGGRESSIVE_PREF "network.prefetch-next.aggressive"
      65             : 
      66             : //-----------------------------------------------------------------------------
      67             : // helpers
      68             : //-----------------------------------------------------------------------------
      69             : 
      70             : static inline uint32_t
      71           0 : PRTimeToSeconds(PRTime t_usec)
      72             : {
      73           0 :     PRTime usec_per_sec = PR_USEC_PER_SEC;
      74           0 :     return uint32_t(t_usec /= usec_per_sec);
      75             : }
      76             : 
      77             : #define NowInSeconds() PRTimeToSeconds(PR_Now())
      78             : 
      79             : //-----------------------------------------------------------------------------
      80             : // nsPrefetchNode <public>
      81             : //-----------------------------------------------------------------------------
      82             : 
      83           0 : nsPrefetchNode::nsPrefetchNode(nsPrefetchService *aService,
      84             :                                nsIURI *aURI,
      85             :                                nsIURI *aReferrerURI,
      86             :                                nsIDOMNode *aSource,
      87             :                                nsContentPolicyType aPolicyType,
      88           0 :                                bool aPreload)
      89             :     : mURI(aURI)
      90             :     , mReferrerURI(aReferrerURI)
      91             :     , mPolicyType(aPolicyType)
      92             :     , mPreload(aPreload)
      93             :     , mService(aService)
      94             :     , mChannel(nullptr)
      95             :     , mBytesRead(0)
      96           0 :     , mShouldFireLoadEvent(false)
      97             : {
      98           0 :     nsCOMPtr<nsIWeakReference> source = do_GetWeakReference(aSource);
      99           0 :     mSources.AppendElement(source);
     100           0 : }
     101             : 
     102             : nsresult
     103           0 : nsPrefetchNode::OpenChannel()
     104             : {
     105           0 :     if (mSources.IsEmpty()) {
     106             :         // Don't attempt to prefetch if we don't have a source node
     107             :         // (which should never happen).
     108           0 :         return NS_ERROR_FAILURE;
     109             :     }
     110           0 :     nsCOMPtr<nsINode> source;
     111           0 :     while (!mSources.IsEmpty() && !(source = do_QueryReferent(mSources.ElementAt(0)))) {
     112             :         // If source is null remove it.
     113             :         // (which should never happen).
     114           0 :         mSources.RemoveElementAt(0);
     115             :     }
     116             : 
     117           0 :     if (!source) {
     118             :         // Don't attempt to prefetch if we don't have a source node
     119             :         // (which should never happen).
     120             : 
     121           0 :         return NS_ERROR_FAILURE;
     122             :     }
     123           0 :     nsCOMPtr<nsILoadGroup> loadGroup = source->OwnerDoc()->GetDocumentLoadGroup();
     124           0 :     CORSMode corsMode = CORS_NONE;
     125           0 :     net::ReferrerPolicy referrerPolicy = net::RP_Unset;
     126           0 :     if (source->IsHTMLElement(nsGkAtoms::link)) {
     127           0 :       dom::HTMLLinkElement* link = static_cast<dom::HTMLLinkElement*>(source.get());
     128           0 :       corsMode = link->GetCORSMode();
     129           0 :       referrerPolicy = link->GetLinkReferrerPolicy();
     130             :     }
     131             : 
     132           0 :     if (referrerPolicy == net::RP_Unset) {
     133           0 :       referrerPolicy = source->OwnerDoc()->GetReferrerPolicy();
     134             :     }
     135             : 
     136             :     uint32_t securityFlags;
     137           0 :     if (corsMode == CORS_NONE) {
     138           0 :       securityFlags = nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS;
     139             :     } else {
     140           0 :       securityFlags = nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS;
     141           0 :       if (corsMode == CORS_USE_CREDENTIALS) {
     142           0 :         securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE;
     143             :       }
     144             :     }
     145           0 :     nsresult rv = NS_NewChannelInternal(getter_AddRefs(mChannel),
     146             :                                         mURI,
     147             :                                         source,
     148             :                                         source->NodePrincipal(),
     149             :                                         nullptr,   //aTriggeringPrincipal
     150             :                                         securityFlags,
     151             :                                         mPolicyType,
     152             :                                         loadGroup, // aLoadGroup
     153             :                                         this,      // aCallbacks
     154             :                                         nsIRequest::LOAD_BACKGROUND |
     155           0 :                                         nsICachingChannel::LOAD_ONLY_IF_MODIFIED);
     156             : 
     157           0 :     NS_ENSURE_SUCCESS(rv, rv);
     158             : 
     159             :     // configure HTTP specific stuff
     160             :     nsCOMPtr<nsIHttpChannel> httpChannel =
     161           0 :         do_QueryInterface(mChannel);
     162           0 :     if (httpChannel) {
     163           0 :         rv = httpChannel->SetReferrerWithPolicy(mReferrerURI, referrerPolicy);
     164           0 :         MOZ_ASSERT(NS_SUCCEEDED(rv));
     165           0 :         rv = httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("X-Moz"),
     166           0 :                                            NS_LITERAL_CSTRING("prefetch"),
     167           0 :                                            false);
     168           0 :         MOZ_ASSERT(NS_SUCCEEDED(rv));
     169             :     }
     170             : 
     171             :     // Reduce the priority of prefetch network requests.
     172           0 :     nsCOMPtr<nsISupportsPriority> priorityChannel = do_QueryInterface(mChannel);
     173           0 :     if (priorityChannel) {
     174           0 :       priorityChannel->AdjustPriority(nsISupportsPriority::PRIORITY_LOWEST);
     175             :     }
     176             : 
     177           0 :     rv = mChannel->AsyncOpen2(this);
     178           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
     179             :       // Drop the ref to the channel, because we don't want to end up with
     180             :       // cycles through it.
     181           0 :       mChannel = nullptr;
     182             :     }
     183           0 :     return rv;
     184             : }
     185             : 
     186             : nsresult
     187           0 : nsPrefetchNode::CancelChannel(nsresult error)
     188             : {
     189           0 :     mChannel->Cancel(error);
     190           0 :     mChannel = nullptr;
     191             : 
     192           0 :     return NS_OK;
     193             : }
     194             : 
     195             : //-----------------------------------------------------------------------------
     196             : // nsPrefetchNode::nsISupports
     197             : //-----------------------------------------------------------------------------
     198             : 
     199           0 : NS_IMPL_ISUPPORTS(nsPrefetchNode,
     200             :                   nsIRequestObserver,
     201             :                   nsIStreamListener,
     202             :                   nsIInterfaceRequestor,
     203             :                   nsIChannelEventSink,
     204             :                   nsIRedirectResultListener)
     205             : 
     206             : //-----------------------------------------------------------------------------
     207             : // nsPrefetchNode::nsIStreamListener
     208             : //-----------------------------------------------------------------------------
     209             : 
     210             : NS_IMETHODIMP
     211           0 : nsPrefetchNode::OnStartRequest(nsIRequest *aRequest,
     212             :                                nsISupports *aContext)
     213             : {
     214             :     nsresult rv;
     215             : 
     216             :     nsCOMPtr<nsIHttpChannel> httpChannel =
     217           0 :         do_QueryInterface(aRequest, &rv);
     218           0 :     if (NS_FAILED(rv)) return rv;
     219             : 
     220             :     // if the load is cross origin without CORS, or the CORS access is rejected,
     221             :     // always fire load event to avoid leaking site information.
     222           0 :     nsCOMPtr<nsILoadInfo> loadInfo = httpChannel->GetLoadInfo();
     223           0 :     if (loadInfo) {
     224           0 :         mShouldFireLoadEvent = loadInfo->GetTainting() == LoadTainting::Opaque ||
     225           0 :                                (loadInfo->GetTainting() == LoadTainting::CORS &&
     226           0 :                                 (NS_FAILED(httpChannel->GetStatus(&rv)) ||
     227           0 :                                  NS_FAILED(rv)));
     228             :     }
     229             : 
     230             :     // no need to prefetch http error page
     231             :     bool requestSucceeded;
     232           0 :     if (NS_FAILED(httpChannel->GetRequestSucceeded(&requestSucceeded)) ||
     233           0 :         !requestSucceeded) {
     234           0 :       return NS_BINDING_ABORTED;
     235             :     }
     236             : 
     237             :     nsCOMPtr<nsICacheInfoChannel> cacheInfoChannel =
     238           0 :         do_QueryInterface(aRequest, &rv);
     239           0 :     if (NS_FAILED(rv)) return rv;
     240             :  
     241             :     // no need to prefetch a document that is already in the cache
     242             :     bool fromCache;
     243           0 :     if (NS_SUCCEEDED(cacheInfoChannel->IsFromCache(&fromCache)) &&
     244             :         fromCache) {
     245           0 :         LOG(("document is already in the cache; canceling prefetch\n"));
     246             :         // although it's canceled we still want to fire load event
     247           0 :         mShouldFireLoadEvent = true;
     248           0 :         return NS_BINDING_ABORTED;
     249             :     }
     250             : 
     251             :     //
     252             :     // no need to prefetch a document that must be requested fresh each
     253             :     // and every time.
     254             :     //
     255             :     uint32_t expTime;
     256           0 :     if (NS_SUCCEEDED(cacheInfoChannel->GetCacheTokenExpirationTime(&expTime))) {
     257           0 :         if (NowInSeconds() >= expTime) {
     258           0 :             LOG(("document cannot be reused from cache; "
     259             :                  "canceling prefetch\n"));
     260           0 :             return NS_BINDING_ABORTED;
     261             :         }
     262             :     }
     263             : 
     264           0 :     return NS_OK;
     265             : }
     266             : 
     267             : NS_IMETHODIMP
     268           0 : nsPrefetchNode::OnDataAvailable(nsIRequest *aRequest,
     269             :                                 nsISupports *aContext,
     270             :                                 nsIInputStream *aStream,
     271             :                                 uint64_t aOffset,
     272             :                                 uint32_t aCount)
     273             : {
     274           0 :     uint32_t bytesRead = 0;
     275           0 :     aStream->ReadSegments(NS_DiscardSegment, nullptr, aCount, &bytesRead);
     276           0 :     mBytesRead += bytesRead;
     277           0 :     LOG(("prefetched %u bytes [offset=%" PRIu64 "]\n", bytesRead, aOffset));
     278           0 :     return NS_OK;
     279             : }
     280             : 
     281             : 
     282             : NS_IMETHODIMP
     283           0 : nsPrefetchNode::OnStopRequest(nsIRequest *aRequest,
     284             :                               nsISupports *aContext,
     285             :                               nsresult aStatus)
     286             : {
     287           0 :     LOG(("done prefetching [status=%" PRIx32 "]\n", static_cast<uint32_t>(aStatus)));
     288             : 
     289           0 :     if (mBytesRead == 0 && aStatus == NS_OK && mChannel) {
     290             :         // we didn't need to read (because LOAD_ONLY_IF_MODIFIED was
     291             :         // specified), but the object should report loadedSize as if it
     292             :         // did.
     293           0 :         mChannel->GetContentLength(&mBytesRead);
     294             :     }
     295             : 
     296           0 :     mService->NotifyLoadCompleted(this);
     297           0 :     mService->DispatchEvent(this, mShouldFireLoadEvent || NS_SUCCEEDED(aStatus));
     298           0 :     mService->RemoveNodeAndMaybeStartNextPrefetchURI(this);
     299           0 :     return NS_OK;
     300             : }
     301             : 
     302             : //-----------------------------------------------------------------------------
     303             : // nsPrefetchNode::nsIInterfaceRequestor
     304             : //-----------------------------------------------------------------------------
     305             : 
     306             : NS_IMETHODIMP
     307           0 : nsPrefetchNode::GetInterface(const nsIID &aIID, void **aResult)
     308             : {
     309           0 :     if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
     310           0 :         NS_ADDREF_THIS();
     311           0 :         *aResult = static_cast<nsIChannelEventSink *>(this);
     312           0 :         return NS_OK;
     313             :     }
     314             : 
     315           0 :     if (aIID.Equals(NS_GET_IID(nsIRedirectResultListener))) {
     316           0 :         NS_ADDREF_THIS();
     317           0 :         *aResult = static_cast<nsIRedirectResultListener *>(this);
     318           0 :         return NS_OK;
     319             :     }
     320             : 
     321           0 :     return NS_ERROR_NO_INTERFACE;
     322             : }
     323             : 
     324             : //-----------------------------------------------------------------------------
     325             : // nsPrefetchNode::nsIChannelEventSink
     326             : //-----------------------------------------------------------------------------
     327             : 
     328             : NS_IMETHODIMP
     329           0 : nsPrefetchNode::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
     330             :                                        nsIChannel *aNewChannel,
     331             :                                        uint32_t aFlags,
     332             :                                        nsIAsyncVerifyRedirectCallback *callback)
     333             : {
     334           0 :     nsCOMPtr<nsIURI> newURI;
     335           0 :     nsresult rv = aNewChannel->GetURI(getter_AddRefs(newURI));
     336           0 :     if (NS_FAILED(rv))
     337           0 :         return rv;
     338             : 
     339             :     bool match;
     340           0 :     rv = newURI->SchemeIs("http", &match); 
     341           0 :     if (NS_FAILED(rv) || !match) {
     342           0 :         rv = newURI->SchemeIs("https", &match); 
     343           0 :         if (NS_FAILED(rv) || !match) {
     344           0 :             LOG(("rejected: URL is not of type http/https\n"));
     345           0 :             return NS_ERROR_ABORT;
     346             :         }
     347             :     }
     348             : 
     349             :     // HTTP request headers are not automatically forwarded to the new channel.
     350           0 :     nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aNewChannel);
     351           0 :     NS_ENSURE_STATE(httpChannel);
     352             : 
     353           0 :     rv = httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("X-Moz"),
     354           0 :                                        NS_LITERAL_CSTRING("prefetch"),
     355           0 :                                        false);
     356           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
     357             : 
     358             :     // Assign to mChannel after we get notification about success of the
     359             :     // redirect in OnRedirectResult.
     360           0 :     mRedirectChannel = aNewChannel;
     361             : 
     362           0 :     callback->OnRedirectVerifyCallback(NS_OK);
     363           0 :     return NS_OK;
     364             : }
     365             : 
     366             : //-----------------------------------------------------------------------------
     367             : // nsPrefetchNode::nsIRedirectResultListener
     368             : //-----------------------------------------------------------------------------
     369             : 
     370             : NS_IMETHODIMP
     371           0 : nsPrefetchNode::OnRedirectResult(bool proceeding)
     372             : {
     373           0 :     if (proceeding && mRedirectChannel)
     374           0 :         mChannel = mRedirectChannel;
     375             : 
     376           0 :     mRedirectChannel = nullptr;
     377             : 
     378           0 :     return NS_OK;
     379             : }
     380             : 
     381             : //-----------------------------------------------------------------------------
     382             : // nsPrefetchService <public>
     383             : //-----------------------------------------------------------------------------
     384             : 
     385           0 : nsPrefetchService::nsPrefetchService()
     386             :     : mMaxParallelism(6)
     387             :     , mStopCount(0)
     388             :     , mHaveProcessed(false)
     389             :     , mPrefetchDisabled(true)
     390             :     , mPreloadDisabled(true)
     391           0 :     , mAggressive(false)
     392             : {
     393           0 : }
     394             : 
     395           0 : nsPrefetchService::~nsPrefetchService()
     396             : {
     397           0 :     Preferences::RemoveObserver(this, PREFETCH_PREF);
     398           0 :     Preferences::RemoveObserver(this, PRELOAD_PREF);
     399           0 :     Preferences::RemoveObserver(this, PARALLELISM_PREF);
     400           0 :     Preferences::RemoveObserver(this, AGGRESSIVE_PREF);
     401             :     // cannot reach destructor if prefetch in progress (listener owns reference
     402             :     // to this service)
     403           0 :     EmptyPrefetchQueue();
     404           0 : }
     405             : 
     406             : nsresult
     407           0 : nsPrefetchService::Init()
     408             : {
     409             :     nsresult rv;
     410             : 
     411             :     // read prefs and hook up pref observer
     412           0 :     mPrefetchDisabled = !Preferences::GetBool(PREFETCH_PREF, !mPrefetchDisabled);
     413           0 :     Preferences::AddWeakObserver(this, PREFETCH_PREF);
     414             : 
     415           0 :     mPreloadDisabled = !Preferences::GetBool(PRELOAD_PREF, !mPreloadDisabled);
     416           0 :     Preferences::AddWeakObserver(this, PRELOAD_PREF);
     417             : 
     418           0 :     mMaxParallelism = Preferences::GetInt(PARALLELISM_PREF, mMaxParallelism);
     419           0 :     if (mMaxParallelism < 1) {
     420           0 :         mMaxParallelism = 1;
     421             :     }
     422           0 :     Preferences::AddWeakObserver(this, PARALLELISM_PREF);
     423             : 
     424           0 :     mAggressive = Preferences::GetBool(AGGRESSIVE_PREF, false);
     425           0 :     Preferences::AddWeakObserver(this, AGGRESSIVE_PREF);
     426             : 
     427             :     // Observe xpcom-shutdown event
     428             :     nsCOMPtr<nsIObserverService> observerService =
     429           0 :       mozilla::services::GetObserverService();
     430           0 :     if (!observerService)
     431           0 :       return NS_ERROR_FAILURE;
     432             : 
     433           0 :     rv = observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, true);
     434           0 :     NS_ENSURE_SUCCESS(rv, rv);
     435             : 
     436           0 :     if (!mPrefetchDisabled || !mPreloadDisabled) {
     437           0 :         AddProgressListener();
     438             :     }
     439             : 
     440           0 :     return NS_OK;
     441             : }
     442             : 
     443             : void
     444           0 : nsPrefetchService::RemoveNodeAndMaybeStartNextPrefetchURI(nsPrefetchNode *aFinished)
     445             : {
     446           0 :     if (aFinished) {
     447           0 :         mCurrentNodes.RemoveElement(aFinished);
     448             :     }
     449             : 
     450           0 :     if ((!mStopCount && mHaveProcessed) || mAggressive) {
     451           0 :         ProcessNextPrefetchURI();
     452             :     }
     453           0 : }
     454             : 
     455             : void
     456           0 : nsPrefetchService::ProcessNextPrefetchURI()
     457             : {
     458           0 :     if (mCurrentNodes.Length() >= static_cast<uint32_t>(mMaxParallelism)) {
     459             :         // We already have enough prefetches going on, so hold off
     460             :         // for now.
     461           0 :         return;
     462             :     }
     463             : 
     464             :     nsresult rv;
     465             : 
     466           0 :     do {
     467           0 :         if (mPrefetchQueue.empty()) {
     468           0 :           break;
     469             :         }
     470           0 :         RefPtr<nsPrefetchNode> node = mPrefetchQueue.front().forget();
     471           0 :         mPrefetchQueue.pop_front();
     472             : 
     473           0 :         if (LOG_ENABLED()) {
     474           0 :             LOG(("ProcessNextPrefetchURI [%s]\n",
     475             :                  node->mURI->GetSpecOrDefault().get())); }
     476             : 
     477             :         //
     478             :         // if opening the channel fails (e.g. security check returns an error),
     479             :         // send an error event and then just skip to the next uri
     480             :         //
     481           0 :         rv = node->OpenChannel();
     482           0 :         if (NS_SUCCEEDED(rv)) {
     483           0 :             mCurrentNodes.AppendElement(node);
     484             :         } else {
     485           0 :           DispatchEvent(node, false);
     486             :         }
     487             :     }
     488           0 :     while (NS_FAILED(rv));
     489             : }
     490             : 
     491             : void
     492           0 : nsPrefetchService::NotifyLoadRequested(nsPrefetchNode *node)
     493             : {
     494             :     nsCOMPtr<nsIObserverService> observerService =
     495           0 :       mozilla::services::GetObserverService();
     496           0 :     if (!observerService)
     497           0 :       return;
     498             : 
     499           0 :     observerService->NotifyObservers(static_cast<nsIStreamListener*>(node),
     500           0 :                                      (node->mPreload) ? "preload-load-requested"
     501             :                                                       : "prefetch-load-requested",
     502           0 :                                      nullptr);
     503             : }
     504             : 
     505             : void
     506           0 : nsPrefetchService::NotifyLoadCompleted(nsPrefetchNode *node)
     507             : {
     508             :     nsCOMPtr<nsIObserverService> observerService =
     509           0 :       mozilla::services::GetObserverService();
     510           0 :     if (!observerService)
     511           0 :       return;
     512             : 
     513           0 :     observerService->NotifyObservers(static_cast<nsIStreamListener*>(node),
     514           0 :                                      (node->mPreload) ? "preload-load-completed"
     515             :                                                       : "prefetch-load-completed",
     516           0 :                                      nullptr);
     517             : }
     518             : 
     519             : void
     520           0 : nsPrefetchService::DispatchEvent(nsPrefetchNode *node, bool aSuccess)
     521             : {
     522           0 :     for (uint32_t i = 0; i < node->mSources.Length(); i++) {
     523           0 :       nsCOMPtr<nsINode> domNode = do_QueryReferent(node->mSources.ElementAt(i));
     524           0 :       if (domNode && domNode->IsInComposedDoc()) {
     525             :         // We don't dispatch synchronously since |node| might be in a DocGroup
     526             :         // that we're not allowed to touch. (Our network request happens in the
     527             :         // DocGroup of one of the mSources nodes--not necessarily this one).
     528             :         RefPtr<AsyncEventDispatcher> dispatcher =
     529             :           new AsyncEventDispatcher(domNode,
     530             :                                    aSuccess ?
     531           0 :                                     NS_LITERAL_STRING("load") :
     532           0 :                                     NS_LITERAL_STRING("error"),
     533           0 :                                    /* aCanBubble = */ false);
     534           0 :         dispatcher->RequireNodeInDocument();
     535           0 :         dispatcher->PostDOMEvent();
     536             :       }
     537             :     }
     538           0 : }
     539             : 
     540             : //-----------------------------------------------------------------------------
     541             : // nsPrefetchService <private>
     542             : //-----------------------------------------------------------------------------
     543             : 
     544             : void
     545           0 : nsPrefetchService::AddProgressListener()
     546             : {
     547             :     // Register as an observer for the document loader  
     548             :     nsCOMPtr<nsIWebProgress> progress = 
     549           0 :         do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID);
     550           0 :     if (progress)
     551           0 :         progress->AddProgressListener(this, nsIWebProgress::NOTIFY_STATE_DOCUMENT);
     552           0 : }
     553             : 
     554             : void
     555           0 : nsPrefetchService::RemoveProgressListener()
     556             : {
     557             :     // Register as an observer for the document loader  
     558             :     nsCOMPtr<nsIWebProgress> progress =
     559           0 :         do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID);
     560           0 :     if (progress)
     561           0 :         progress->RemoveProgressListener(this);
     562           0 : }
     563             : 
     564             : nsresult
     565           0 : nsPrefetchService::EnqueueURI(nsIURI *aURI,
     566             :                               nsIURI *aReferrerURI,
     567             :                               nsIDOMNode *aSource,
     568             :                               nsPrefetchNode **aNode)
     569             : {
     570             :     RefPtr<nsPrefetchNode> node = new nsPrefetchNode(this, aURI, aReferrerURI,
     571             :                                                      aSource,
     572             :                                                      nsIContentPolicy::TYPE_OTHER,
     573           0 :                                                      false);
     574           0 :     mPrefetchQueue.push_back(node);
     575           0 :     node.forget(aNode);
     576           0 :     return NS_OK;
     577             : }
     578             : 
     579             : void
     580           0 : nsPrefetchService::EmptyPrefetchQueue()
     581             : {
     582           0 :     while (!mPrefetchQueue.empty()) {
     583           0 :         mPrefetchQueue.pop_back();
     584             :     }
     585           0 : }
     586             : 
     587             : void
     588           0 : nsPrefetchService::StartPrefetching()
     589             : {
     590             :     //
     591             :     // at initialization time we might miss the first DOCUMENT START
     592             :     // notification, so we have to be careful to avoid letting our
     593             :     // stop count go negative.
     594             :     //
     595           0 :     if (mStopCount > 0)
     596           0 :         mStopCount--;
     597             : 
     598           0 :     LOG(("StartPrefetching [stopcount=%d]\n", mStopCount));
     599             : 
     600             :     // only start prefetching after we've received enough DOCUMENT
     601             :     // STOP notifications.  we do this inorder to defer prefetching
     602             :     // until after all sub-frames have finished loading.
     603           0 :     if (!mStopCount) {
     604           0 :         mHaveProcessed = true;
     605           0 :         while (!mPrefetchQueue.empty() &&
     606           0 :                mCurrentNodes.Length() < static_cast<uint32_t>(mMaxParallelism)) {
     607           0 :             ProcessNextPrefetchURI();
     608             :         }
     609             :     }
     610           0 : }
     611             : 
     612             : void
     613           0 : nsPrefetchService::StopPrefetching()
     614             : {
     615           0 :     mStopCount++;
     616             : 
     617           0 :     LOG(("StopPrefetching [stopcount=%d]\n", mStopCount));
     618             : 
     619             :     // When we start a load, we need to stop all prefetches that has been
     620             :     // added by the old load, therefore call StopAll only at the moment we
     621             :     // switch to a new page load (i.e. mStopCount == 1).
     622             :     // TODO: do not stop prefetches that are relevant for the new load.
     623           0 :     if (mStopCount == 1) {
     624           0 :         StopAll();
     625             :     }
     626           0 : }
     627             : 
     628             : void
     629           0 : nsPrefetchService::StopCurrentPrefetchsPreloads(bool aPreload)
     630             : {
     631           0 :     for (int32_t i = mCurrentNodes.Length() - 1; i >= 0; --i) {
     632           0 :         if (mCurrentNodes[i]->mPreload == aPreload) {
     633           0 :             mCurrentNodes[i]->CancelChannel(NS_BINDING_ABORTED);
     634           0 :             mCurrentNodes.RemoveElementAt(i);
     635             :         }
     636             :     }
     637             : 
     638           0 :     if (!aPreload) {
     639           0 :         EmptyPrefetchQueue();
     640             :     }
     641           0 : }
     642             : 
     643             : void
     644           0 : nsPrefetchService::StopAll()
     645             : {
     646           0 :     for (uint32_t i = 0; i < mCurrentNodes.Length(); ++i) {
     647           0 :         mCurrentNodes[i]->CancelChannel(NS_BINDING_ABORTED);
     648             :     }
     649           0 :     mCurrentNodes.Clear();
     650           0 :     EmptyPrefetchQueue();
     651           0 : }
     652             : 
     653             : nsresult
     654           0 : nsPrefetchService::CheckURIScheme(nsIURI *aURI, nsIURI *aReferrerURI)
     655             : {
     656             :     //
     657             :     // XXX we should really be asking the protocol handler if it supports
     658             :     // caching, so we can determine if there is any value to prefetching.
     659             :     // for now, we'll only prefetch http and https links since we know that's
     660             :     // the most common case.
     661             :     //
     662             :     bool match;
     663           0 :     nsresult rv = aURI->SchemeIs("http", &match);
     664           0 :     if (NS_FAILED(rv) || !match) {
     665           0 :         rv = aURI->SchemeIs("https", &match);
     666           0 :         if (NS_FAILED(rv) || !match) {
     667           0 :             LOG(("rejected: URL is not of type http/https\n"));
     668           0 :             return NS_ERROR_ABORT;
     669             :         }
     670             :     }
     671             : 
     672             :     // 
     673             :     // the referrer URI must be http:
     674             :     //
     675           0 :     rv = aReferrerURI->SchemeIs("http", &match);
     676           0 :     if (NS_FAILED(rv) || !match) {
     677           0 :         rv = aReferrerURI->SchemeIs("https", &match);
     678           0 :         if (NS_FAILED(rv) || !match) {
     679           0 :             LOG(("rejected: referrer URL is neither http nor https\n"));
     680           0 :             return NS_ERROR_ABORT;
     681             :         }
     682             :     }
     683             : 
     684           0 :     return NS_OK;
     685             : }
     686             : 
     687             : //-----------------------------------------------------------------------------
     688             : // nsPrefetchService::nsISupports
     689             : //-----------------------------------------------------------------------------
     690             : 
     691           0 : NS_IMPL_ISUPPORTS(nsPrefetchService,
     692             :                   nsIPrefetchService,
     693             :                   nsIWebProgressListener,
     694             :                   nsIObserver,
     695             :                   nsISupportsWeakReference)
     696             : 
     697             : //-----------------------------------------------------------------------------
     698             : // nsPrefetchService::nsIPrefetchService
     699             : //-----------------------------------------------------------------------------
     700             : 
     701             : nsresult
     702           0 : nsPrefetchService::Preload(nsIURI *aURI,
     703             :                            nsIURI *aReferrerURI,
     704             :                            nsIDOMNode *aSource,
     705             :                            nsContentPolicyType aPolicyType)
     706             : {
     707           0 :     NS_ENSURE_ARG_POINTER(aURI);
     708           0 :     NS_ENSURE_ARG_POINTER(aReferrerURI);
     709           0 :     if (LOG_ENABLED()) {
     710           0 :         LOG(("PreloadURI [%s]\n", aURI->GetSpecOrDefault().get()));
     711             :     }
     712             : 
     713           0 :     if (mPreloadDisabled) {
     714           0 :         LOG(("rejected: preload service is disabled\n"));
     715           0 :         return NS_ERROR_ABORT;
     716             :     }
     717             : 
     718           0 :     nsresult rv = CheckURIScheme(aURI, aReferrerURI);
     719           0 :     if (NS_FAILED(rv)) {
     720           0 :         return rv;
     721             :     }
     722             : 
     723             :     // XXX we might want to either leverage nsIProtocolHandler::protocolFlags
     724             :     // or possibly nsIRequest::loadFlags to determine if this URI should be
     725             :     // prefetched.
     726             :     //
     727             : 
     728           0 :     if (aPolicyType == nsIContentPolicy::TYPE_INVALID) {
     729           0 :         nsCOMPtr<nsINode> domNode = do_QueryInterface(aSource);
     730           0 :         if (domNode && domNode->IsInComposedDoc()) {
     731             :             RefPtr<AsyncEventDispatcher> asyncDispatcher =
     732             :                 new AsyncEventDispatcher(//domNode->OwnerDoc(),
     733             :                                          domNode,
     734           0 :                                          NS_LITERAL_STRING("error"),
     735             :                                          /* aCanBubble = */ false,
     736           0 :                                          /* aCancelable = */ false);
     737           0 :             asyncDispatcher->RunDOMEventWhenSafe();
     738             :         }
     739           0 :         return NS_OK;
     740             :     }
     741             : 
     742             :     //
     743             :     // Check whether it is being preloaded.
     744             :     //
     745           0 :     for (uint32_t i = 0; i < mCurrentNodes.Length(); ++i) {
     746             :         bool equals;
     747           0 :         if ((mCurrentNodes[i]->mPolicyType == aPolicyType) &&
     748           0 :             NS_SUCCEEDED(mCurrentNodes[i]->mURI->Equals(aURI, &equals)) &&
     749             :             equals) {
     750           0 :             nsCOMPtr<nsIWeakReference> source = do_GetWeakReference(aSource);
     751           0 :             if (mCurrentNodes[i]->mSources.IndexOf(source) ==
     752           0 :                 mCurrentNodes[i]->mSources.NoIndex) {
     753           0 :                 LOG(("URL is already being preloaded, add a new reference "
     754             :                      "document\n"));
     755           0 :                 mCurrentNodes[i]->mSources.AppendElement(source);
     756           0 :                 return NS_OK;
     757             :             } else {
     758           0 :                 LOG(("URL is already being preloaded by this document"));
     759           0 :                 return NS_ERROR_ABORT;
     760             :             }
     761             :         }
     762             :     }
     763             : 
     764           0 :     LOG(("This is a preload, so start loading immediately.\n"));
     765           0 :     RefPtr<nsPrefetchNode> enqueuedNode;
     766             :     enqueuedNode = new nsPrefetchNode(this, aURI, aReferrerURI,
     767           0 :                                       aSource, aPolicyType, true);
     768             : 
     769           0 :     NotifyLoadRequested(enqueuedNode);
     770           0 :     rv = enqueuedNode->OpenChannel();
     771           0 :     if (NS_SUCCEEDED(rv)) {
     772           0 :         mCurrentNodes.AppendElement(enqueuedNode);
     773             :     } else {
     774           0 :         nsCOMPtr<nsINode> domNode = do_QueryInterface(aSource);
     775           0 :         if (domNode && domNode->IsInComposedDoc()) {
     776             :             RefPtr<AsyncEventDispatcher> asyncDispatcher =
     777             :                 new AsyncEventDispatcher(domNode,
     778           0 :                                          NS_LITERAL_STRING("error"),
     779             :                                          /* aCanBubble = */ false,
     780           0 :                                          /* aCancelable = */ false);
     781           0 :             asyncDispatcher->RunDOMEventWhenSafe();
     782             :         }
     783             :     }
     784           0 :     return NS_OK;
     785             : }
     786             : 
     787             : nsresult
     788           0 : nsPrefetchService::Prefetch(nsIURI *aURI,
     789             :                             nsIURI *aReferrerURI,
     790             :                             nsIDOMNode *aSource,
     791             :                             bool aExplicit)
     792             : {
     793           0 :     NS_ENSURE_ARG_POINTER(aURI);
     794           0 :     NS_ENSURE_ARG_POINTER(aReferrerURI);
     795             : 
     796           0 :     if (LOG_ENABLED()) {
     797           0 :         LOG(("PrefetchURI [%s]\n", aURI->GetSpecOrDefault().get()));
     798             :     }
     799             : 
     800           0 :     if (mPrefetchDisabled) {
     801           0 :         LOG(("rejected: prefetch service is disabled\n"));
     802           0 :         return NS_ERROR_ABORT;
     803             :     }
     804             : 
     805           0 :     nsresult rv = CheckURIScheme(aURI, aReferrerURI);
     806           0 :     if (NS_FAILED(rv)) {
     807           0 :         return rv;
     808             :     }
     809             : 
     810             :     // XXX we might want to either leverage nsIProtocolHandler::protocolFlags
     811             :     // or possibly nsIRequest::loadFlags to determine if this URI should be
     812             :     // prefetched.
     813             :     //
     814             : 
     815             :     // skip URLs that contain query strings, except URLs for which prefetching
     816             :     // has been explicitly requested.
     817           0 :     if (!aExplicit) {
     818           0 :         nsCOMPtr<nsIURL> url(do_QueryInterface(aURI, &rv));
     819           0 :         if (NS_FAILED(rv)) return rv;
     820           0 :         nsAutoCString query;
     821           0 :         rv = url->GetQuery(query);
     822           0 :         if (NS_FAILED(rv) || !query.IsEmpty()) {
     823           0 :             LOG(("rejected: URL has a query string\n"));
     824           0 :             return NS_ERROR_ABORT;
     825             :         }
     826             :     }
     827             : 
     828             :     //
     829             :     // Check whether it is being prefetched.
     830             :     //
     831           0 :     for (uint32_t i = 0; i < mCurrentNodes.Length(); ++i) {
     832             :         bool equals;
     833           0 :         if (NS_SUCCEEDED(mCurrentNodes[i]->mURI->Equals(aURI, &equals)) &&
     834             :             equals) {
     835           0 :             nsCOMPtr<nsIWeakReference> source = do_GetWeakReference(aSource);
     836           0 :             if (mCurrentNodes[i]->mSources.IndexOf(source) ==
     837           0 :                 mCurrentNodes[i]->mSources.NoIndex) {
     838           0 :                 LOG(("URL is already being prefetched, add a new reference "
     839             :                      "document\n"));
     840           0 :                 mCurrentNodes[i]->mSources.AppendElement(source);
     841           0 :                 return NS_OK;
     842             :             } else {
     843           0 :                 LOG(("URL is already being prefetched by this document"));
     844           0 :                 return NS_ERROR_ABORT;
     845             :             }
     846             :         }
     847             :     }
     848             : 
     849             :     //
     850             :     // Check whether it is on the prefetch queue.
     851             :     //
     852           0 :     for (std::deque<RefPtr<nsPrefetchNode>>::iterator nodeIt = mPrefetchQueue.begin();
     853           0 :          nodeIt != mPrefetchQueue.end(); nodeIt++) {
     854             :         bool equals;
     855           0 :         RefPtr<nsPrefetchNode> node = nodeIt->get();
     856           0 :         if (NS_SUCCEEDED(node->mURI->Equals(aURI, &equals)) && equals) {
     857           0 :             nsCOMPtr<nsIWeakReference> source = do_GetWeakReference(aSource);
     858           0 :             if (node->mSources.IndexOf(source) ==
     859           0 :                 node->mSources.NoIndex) {
     860           0 :                 LOG(("URL is already being prefetched, add a new reference "
     861             :                      "document\n"));
     862           0 :                 node->mSources.AppendElement(do_GetWeakReference(aSource));
     863           0 :                 return NS_OK;
     864             :             } else {
     865           0 :                 LOG(("URL is already being prefetched by this document"));
     866           0 :                 return NS_ERROR_ABORT;
     867             :             }
     868             : 
     869             :         }
     870             :     }
     871             : 
     872           0 :     RefPtr<nsPrefetchNode> enqueuedNode;
     873           0 :     rv = EnqueueURI(aURI, aReferrerURI, aSource,
     874           0 :                     getter_AddRefs(enqueuedNode));
     875           0 :     NS_ENSURE_SUCCESS(rv, rv);
     876             : 
     877           0 :     NotifyLoadRequested(enqueuedNode);
     878             : 
     879             :     // if there are no pages loading, kick off the request immediately
     880           0 :     if ((!mStopCount && mHaveProcessed) || mAggressive) {
     881           0 :         ProcessNextPrefetchURI();
     882             :     }
     883             : 
     884           0 :     return NS_OK;
     885             : }
     886             : 
     887             : NS_IMETHODIMP
     888           0 : nsPrefetchService::CancelPrefetchPreloadURI(nsIURI* aURI,
     889             :                                             nsIDOMNode* aSource)
     890             : {
     891           0 :     NS_ENSURE_ARG_POINTER(aURI);
     892             : 
     893           0 :     if (LOG_ENABLED()) {
     894           0 :         LOG(("CancelPrefetchURI [%s]\n", aURI->GetSpecOrDefault().get()));
     895             :     }
     896             : 
     897             :     //
     898             :     // look in current prefetches
     899             :     //
     900           0 :     for (uint32_t i = 0; i < mCurrentNodes.Length(); ++i) {
     901             :         bool equals;
     902           0 :         if (NS_SUCCEEDED(mCurrentNodes[i]->mURI->Equals(aURI, &equals)) &&
     903             :             equals) {
     904           0 :             nsCOMPtr<nsIWeakReference> source = do_GetWeakReference(aSource);
     905           0 :             if (mCurrentNodes[i]->mSources.IndexOf(source) !=
     906           0 :                 mCurrentNodes[i]->mSources.NoIndex) {
     907           0 :                 mCurrentNodes[i]->mSources.RemoveElement(source);
     908           0 :                 if (mCurrentNodes[i]->mSources.IsEmpty()) {
     909           0 :                     mCurrentNodes[i]->CancelChannel(NS_BINDING_ABORTED);
     910           0 :                     mCurrentNodes.RemoveElementAt(i);
     911             :                 }
     912           0 :                 return NS_OK;
     913             :             }
     914           0 :             return NS_ERROR_FAILURE;
     915             :         }
     916             :     }
     917             : 
     918             :     //
     919             :     // look into the prefetch queue
     920             :     //
     921           0 :     for (std::deque<RefPtr<nsPrefetchNode>>::iterator nodeIt = mPrefetchQueue.begin();
     922           0 :          nodeIt != mPrefetchQueue.end(); nodeIt++) {
     923             :         bool equals;
     924           0 :         RefPtr<nsPrefetchNode> node = nodeIt->get();
     925           0 :         if (NS_SUCCEEDED(node->mURI->Equals(aURI, &equals)) && equals) {
     926           0 :             nsCOMPtr<nsIWeakReference> source = do_GetWeakReference(aSource);
     927           0 :             if (node->mSources.IndexOf(source) !=
     928           0 :                 node->mSources.NoIndex) {
     929             : 
     930             : #ifdef DEBUG
     931           0 :                 int32_t inx = node->mSources.IndexOf(source);
     932             :                 nsCOMPtr<nsIDOMNode> domNode =
     933           0 :                     do_QueryReferent(node->mSources.ElementAt(inx));
     934           0 :                 MOZ_ASSERT(domNode);
     935             : #endif
     936             : 
     937           0 :                 node->mSources.RemoveElement(source);
     938           0 :                 if (node->mSources.IsEmpty()) {
     939           0 :                     mPrefetchQueue.erase(nodeIt);
     940             :                 }
     941           0 :                 return NS_OK;
     942             :             }
     943           0 :             return NS_ERROR_FAILURE;
     944             :         }
     945             :     }
     946             : 
     947             :     // not found!
     948           0 :     return NS_ERROR_FAILURE;
     949             : }
     950             : 
     951             : NS_IMETHODIMP
     952           0 : nsPrefetchService::PreloadURI(nsIURI *aURI,
     953             :                               nsIURI *aReferrerURI,
     954             :                               nsIDOMNode *aSource,
     955             :                               nsContentPolicyType aPolicyType)
     956             : {
     957           0 :     return Preload(aURI, aReferrerURI, aSource, aPolicyType);
     958             : }
     959             : 
     960             : NS_IMETHODIMP
     961           0 : nsPrefetchService::PrefetchURI(nsIURI *aURI,
     962             :                                nsIURI *aReferrerURI,
     963             :                                nsIDOMNode *aSource,
     964             :                                bool aExplicit)
     965             : {
     966           0 :     return Prefetch(aURI, aReferrerURI, aSource, aExplicit);
     967             : }
     968             : 
     969             : NS_IMETHODIMP
     970           0 : nsPrefetchService::HasMoreElements(bool *aHasMore)
     971             : {
     972           0 :     *aHasMore = (mCurrentNodes.Length() || !mPrefetchQueue.empty());
     973           0 :     return NS_OK;
     974             : }
     975             : 
     976             : //-----------------------------------------------------------------------------
     977             : // nsPrefetchService::nsIWebProgressListener
     978             : //-----------------------------------------------------------------------------
     979             : 
     980             : NS_IMETHODIMP
     981           0 : nsPrefetchService::OnProgressChange(nsIWebProgress *aProgress,
     982             :                                     nsIRequest *aRequest, 
     983             :                                     int32_t curSelfProgress, 
     984             :                                     int32_t maxSelfProgress, 
     985             :                                     int32_t curTotalProgress, 
     986             :                                     int32_t maxTotalProgress)
     987             : {
     988           0 :     NS_NOTREACHED("notification excluded in AddProgressListener(...)");
     989           0 :     return NS_OK;
     990             : }
     991             : 
     992             : NS_IMETHODIMP 
     993           0 : nsPrefetchService::OnStateChange(nsIWebProgress* aWebProgress, 
     994             :                                  nsIRequest *aRequest, 
     995             :                                  uint32_t progressStateFlags, 
     996             :                                  nsresult aStatus)
     997             : {
     998           0 :     if (progressStateFlags & STATE_IS_DOCUMENT) {
     999           0 :         if (progressStateFlags & STATE_STOP)
    1000           0 :             StartPrefetching();
    1001           0 :         else if (progressStateFlags & STATE_START)
    1002           0 :             StopPrefetching();
    1003             :     }
    1004             :             
    1005           0 :     return NS_OK;
    1006             : }
    1007             : 
    1008             : 
    1009             : NS_IMETHODIMP
    1010           0 : nsPrefetchService::OnLocationChange(nsIWebProgress* aWebProgress,
    1011             :                                     nsIRequest* aRequest,
    1012             :                                     nsIURI *location,
    1013             :                                     uint32_t aFlags)
    1014             : {
    1015           0 :     NS_NOTREACHED("notification excluded in AddProgressListener(...)");
    1016           0 :     return NS_OK;
    1017             : }
    1018             : 
    1019             : NS_IMETHODIMP 
    1020           0 : nsPrefetchService::OnStatusChange(nsIWebProgress* aWebProgress,
    1021             :                                   nsIRequest* aRequest,
    1022             :                                   nsresult aStatus,
    1023             :                                   const char16_t* aMessage)
    1024             : {
    1025           0 :     NS_NOTREACHED("notification excluded in AddProgressListener(...)");
    1026           0 :     return NS_OK;
    1027             : }
    1028             : 
    1029             : NS_IMETHODIMP 
    1030           0 : nsPrefetchService::OnSecurityChange(nsIWebProgress *aWebProgress, 
    1031             :                                     nsIRequest *aRequest, 
    1032             :                                     uint32_t state)
    1033             : {
    1034           0 :     NS_NOTREACHED("notification excluded in AddProgressListener(...)");
    1035           0 :     return NS_OK;
    1036             : }
    1037             : 
    1038             : //-----------------------------------------------------------------------------
    1039             : // nsPrefetchService::nsIObserver
    1040             : //-----------------------------------------------------------------------------
    1041             : 
    1042             : NS_IMETHODIMP
    1043           0 : nsPrefetchService::Observe(nsISupports     *aSubject,
    1044             :                            const char      *aTopic,
    1045             :                            const char16_t *aData)
    1046             : {
    1047           0 :     LOG(("nsPrefetchService::Observe [topic=%s]\n", aTopic));
    1048             : 
    1049           0 :     if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
    1050           0 :         StopAll();
    1051           0 :         mPrefetchDisabled = true;
    1052           0 :         mPreloadDisabled = true;
    1053             :     }
    1054           0 :     else if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
    1055           0 :         const nsCString converted = NS_ConvertUTF16toUTF8(aData);
    1056           0 :         const char* pref = converted.get();
    1057           0 :         if (!strcmp(pref, PREFETCH_PREF)) {
    1058           0 :             if (Preferences::GetBool(PREFETCH_PREF, false)) {
    1059           0 :                 if (mPrefetchDisabled) {
    1060           0 :                     LOG(("enabling prefetching\n"));
    1061           0 :                     mPrefetchDisabled = false;
    1062           0 :                     if (mPreloadDisabled) {
    1063           0 :                         AddProgressListener();
    1064             :                     }
    1065             :                 }
    1066             :             } else {
    1067           0 :                 if (!mPrefetchDisabled) {
    1068           0 :                     LOG(("disabling prefetching\n"));
    1069           0 :                     StopCurrentPrefetchsPreloads(false);
    1070           0 :                     mPrefetchDisabled = true;
    1071           0 :                     if (mPreloadDisabled) {
    1072           0 :                         RemoveProgressListener();
    1073             :                     }
    1074             :                 }
    1075             :             }
    1076           0 :         } else if (!strcmp(pref, PRELOAD_PREF)) {
    1077           0 :             if (Preferences::GetBool(PRELOAD_PREF, false)) {
    1078           0 :                 if (mPreloadDisabled) {
    1079           0 :                     LOG(("enabling preloading\n"));
    1080           0 :                     mPreloadDisabled = false;
    1081           0 :                     if (mPrefetchDisabled) {
    1082           0 :                         AddProgressListener();
    1083             :                     }
    1084             :                 }
    1085             :             } else {
    1086           0 :                 if (!mPreloadDisabled) {
    1087           0 :                   LOG(("disabling preloading\n"));
    1088           0 :                   StopCurrentPrefetchsPreloads(true);
    1089           0 :                   mPreloadDisabled = true;
    1090           0 :                   if (mPrefetchDisabled) {
    1091           0 :                       RemoveProgressListener();
    1092             :                   }
    1093             :                 }
    1094             :             }
    1095           0 :         } else if (!strcmp(pref, PARALLELISM_PREF)) {
    1096           0 :             mMaxParallelism = Preferences::GetInt(PARALLELISM_PREF, mMaxParallelism);
    1097           0 :             if (mMaxParallelism < 1) {
    1098           0 :                 mMaxParallelism = 1;
    1099             :             }
    1100             :             // If our parallelism has increased, go ahead and kick off enough
    1101             :             // prefetches to fill up our allowance. If we're now over our
    1102             :             // allowance, we'll just silently let some of them finish to get
    1103             :             // back below our limit.
    1104           0 :             while (((!mStopCount && mHaveProcessed) || mAggressive) &&
    1105           0 :                    !mPrefetchQueue.empty() &&
    1106           0 :                    mCurrentNodes.Length() < static_cast<uint32_t>(mMaxParallelism)) {
    1107           0 :                 ProcessNextPrefetchURI();
    1108             :             }
    1109           0 :         } else if (!strcmp(pref, AGGRESSIVE_PREF)) {
    1110           0 :             mAggressive = Preferences::GetBool(AGGRESSIVE_PREF, false);
    1111             :             // in aggressive mode, start prefetching immediately
    1112           0 :             if (mAggressive) {
    1113           0 :                 while (mStopCount && !mPrefetchQueue.empty() &&
    1114           0 :                        mCurrentNodes.Length() < static_cast<uint32_t>(mMaxParallelism)) {
    1115           0 :                     ProcessNextPrefetchURI();
    1116             :                 }
    1117             :             }
    1118             :         }
    1119             :     }
    1120             : 
    1121           0 :     return NS_OK;
    1122             : }
    1123             : 
    1124             : // vim: ts=4 sw=4 expandtab

Generated by: LCOV version 1.13