uriloader/prefetch/nsPrefetchService.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 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 #include "nsPrefetchService.h"
     6 #include "nsICacheEntry.h"
     7 #include "nsIServiceManager.h"
     8 #include "nsICategoryManager.h"
     9 #include "nsIObserverService.h"
    10 #include "nsIWebProgress.h"
    11 #include "nsCURILoader.h"
    12 #include "nsICachingChannel.h"
    13 #include "nsICacheVisitor.h"
    14 #include "nsIHttpChannel.h"
    15 #include "nsIURL.h"
    16 #include "nsISimpleEnumerator.h"
    17 #include "nsNetUtil.h"
    18 #include "nsString.h"
    19 #include "nsXPIDLString.h"
    20 #include "nsReadableUtils.h"
    21 #include "nsStreamUtils.h"
    22 #include "nsAutoPtr.h"
    23 #include "prtime.h"
    24 #include "prlog.h"
    25 #include "plstr.h"
    26 #include "nsIAsyncVerifyRedirectCallback.h"
    27 #include "mozilla/Preferences.h"
    28 #include "mozilla/Attributes.h"
    29 #include "nsIDOMNode.h"
    30 #include "nsINode.h"
    31 #include "nsIDocument.h"
    33 using namespace mozilla;
    35 #if defined(PR_LOGGING)
    36 //
    37 // To enable logging (see prlog.h for full details):
    38 //
    39 //    set NSPR_LOG_MODULES=nsPrefetch:5
    40 //    set NSPR_LOG_FILE=prefetch.log
    41 //
    42 // this enables PR_LOG_ALWAYS level information and places all output in
    43 // the file http.log
    44 //
    45 static PRLogModuleInfo *gPrefetchLog;
    46 #endif
    48 #undef LOG
    49 #define LOG(args) PR_LOG(gPrefetchLog, 4, args)
    51 #undef LOG_ENABLED
    52 #define LOG_ENABLED() PR_LOG_TEST(gPrefetchLog, 4)
    54 #define PREFETCH_PREF "network.prefetch-next"
    56 //-----------------------------------------------------------------------------
    57 // helpers
    58 //-----------------------------------------------------------------------------
    60 static inline uint32_t
    61 PRTimeToSeconds(PRTime t_usec)
    62 {
    63     PRTime usec_per_sec = PR_USEC_PER_SEC;
    64     return uint32_t(t_usec /= usec_per_sec);
    65 }
    67 #define NowInSeconds() PRTimeToSeconds(PR_Now())
    69 //-----------------------------------------------------------------------------
    70 // nsPrefetchQueueEnumerator
    71 //-----------------------------------------------------------------------------
    72 class nsPrefetchQueueEnumerator MOZ_FINAL : public nsISimpleEnumerator
    73 {
    74 public:
    75     NS_DECL_ISUPPORTS
    76     NS_DECL_NSISIMPLEENUMERATOR
    77     nsPrefetchQueueEnumerator(nsPrefetchService *aService);
    78     ~nsPrefetchQueueEnumerator();
    80 private:
    81     void Increment();
    83     nsRefPtr<nsPrefetchService> mService;
    84     nsRefPtr<nsPrefetchNode> mCurrent;
    85     bool mStarted;
    86 };
    88 //-----------------------------------------------------------------------------
    89 // nsPrefetchQueueEnumerator <public>
    90 //-----------------------------------------------------------------------------
    91 nsPrefetchQueueEnumerator::nsPrefetchQueueEnumerator(nsPrefetchService *aService)
    92     : mService(aService)
    93     , mStarted(false)
    94 {
    95     Increment();
    96 }
    98 nsPrefetchQueueEnumerator::~nsPrefetchQueueEnumerator()
    99 {
   100 }
   102 //-----------------------------------------------------------------------------
   103 // nsPrefetchQueueEnumerator::nsISimpleEnumerator
   104 //-----------------------------------------------------------------------------
   105 NS_IMETHODIMP
   106 nsPrefetchQueueEnumerator::HasMoreElements(bool *aHasMore)
   107 {
   108     *aHasMore = (mCurrent != nullptr);
   109     return NS_OK;
   110 }
   112 NS_IMETHODIMP
   113 nsPrefetchQueueEnumerator::GetNext(nsISupports **aItem)
   114 {
   115     if (!mCurrent) return NS_ERROR_FAILURE;
   117     NS_ADDREF(*aItem = static_cast<nsIStreamListener*>(mCurrent.get()));
   119     Increment();
   121     return NS_OK;
   122 }
   124 //-----------------------------------------------------------------------------
   125 // nsPrefetchQueueEnumerator <private>
   126 //-----------------------------------------------------------------------------
   128 void
   129 nsPrefetchQueueEnumerator::Increment()
   130 {
   131   if (!mStarted) {
   132     // If the service is currently serving a request, it won't be in
   133     // the pending queue, so we return it first.  If it isn't, we'll
   134     // just start with the pending queue.
   135     mStarted = true;
   136     mCurrent = mService->GetCurrentNode();
   137     if (!mCurrent)
   138       mCurrent = mService->GetQueueHead();
   139     return;
   140   }
   142   if (mCurrent) {
   143     if (mCurrent == mService->GetCurrentNode()) {
   144       // If we just returned the node being processed by the service,
   145       // start with the pending queue
   146       mCurrent = mService->GetQueueHead();
   147     }
   148     else {
   149       // Otherwise just advance to the next item in the queue
   150       mCurrent = mCurrent->mNext;
   151     }
   152   }
   153 }
   155 //-----------------------------------------------------------------------------
   156 // nsPrefetchQueueEnumerator::nsISupports
   157 //-----------------------------------------------------------------------------
   159 NS_IMPL_ISUPPORTS(nsPrefetchQueueEnumerator, nsISimpleEnumerator)
   161 //-----------------------------------------------------------------------------
   162 // nsPrefetchNode <public>
   163 //-----------------------------------------------------------------------------
   165 nsPrefetchNode::nsPrefetchNode(nsPrefetchService *aService,
   166                                nsIURI *aURI,
   167                                nsIURI *aReferrerURI,
   168                                nsIDOMNode *aSource)
   169     : mNext(nullptr)
   170     , mURI(aURI)
   171     , mReferrerURI(aReferrerURI)
   172     , mService(aService)
   173     , mChannel(nullptr)
   174     , mBytesRead(0)
   175 {
   176     mSource = do_GetWeakReference(aSource);
   177 }
   179 nsresult
   180 nsPrefetchNode::OpenChannel()
   181 {
   182     nsCOMPtr<nsINode> source = do_QueryReferent(mSource);
   183     if (!source) {
   184         // Don't attempt to prefetch if we don't have a source node
   185         // (which should never happen).
   186         return NS_ERROR_FAILURE;
   187     }
   188     nsCOMPtr<nsILoadGroup> loadGroup = source->OwnerDoc()->GetDocumentLoadGroup();
   189     nsresult rv = NS_NewChannel(getter_AddRefs(mChannel),
   190                                 mURI,
   191                                 nullptr, loadGroup, this,
   192                                 nsIRequest::LOAD_BACKGROUND |
   193                                 nsICachingChannel::LOAD_ONLY_IF_MODIFIED);
   194     NS_ENSURE_SUCCESS(rv, rv);
   196     // configure HTTP specific stuff
   197     nsCOMPtr<nsIHttpChannel> httpChannel =
   198         do_QueryInterface(mChannel);
   199     if (httpChannel) {
   200         httpChannel->SetReferrer(mReferrerURI);
   201         httpChannel->SetRequestHeader(
   202             NS_LITERAL_CSTRING("X-Moz"),
   203             NS_LITERAL_CSTRING("prefetch"),
   204             false);
   205     }
   207     rv = mChannel->AsyncOpen(this, nullptr);
   208     NS_ENSURE_SUCCESS(rv, rv);
   210     return NS_OK;
   211 }
   213 nsresult
   214 nsPrefetchNode::CancelChannel(nsresult error)
   215 {
   216     mChannel->Cancel(error);
   217     mChannel = nullptr;
   219     return NS_OK;
   220 }
   222 //-----------------------------------------------------------------------------
   223 // nsPrefetchNode::nsISupports
   224 //-----------------------------------------------------------------------------
   226 NS_IMPL_ISUPPORTS(nsPrefetchNode,
   227                   nsIRequestObserver,
   228                   nsIStreamListener,
   229                   nsIInterfaceRequestor,
   230                   nsIChannelEventSink,
   231                   nsIRedirectResultListener)
   233 //-----------------------------------------------------------------------------
   234 // nsPrefetchNode::nsIStreamListener
   235 //-----------------------------------------------------------------------------
   237 NS_IMETHODIMP
   238 nsPrefetchNode::OnStartRequest(nsIRequest *aRequest,
   239                                nsISupports *aContext)
   240 {
   241     nsresult rv;
   243     nsCOMPtr<nsICachingChannel> cachingChannel =
   244         do_QueryInterface(aRequest, &rv);
   245     if (NS_FAILED(rv)) return rv;
   247     // no need to prefetch a document that is already in the cache
   248     bool fromCache;
   249     if (NS_SUCCEEDED(cachingChannel->IsFromCache(&fromCache)) &&
   250         fromCache) {
   251         LOG(("document is already in the cache; canceling prefetch\n"));
   252         return NS_BINDING_ABORTED;
   253     }
   255     //
   256     // no need to prefetch a document that must be requested fresh each
   257     // and every time.
   258     //
   259     nsCOMPtr<nsISupports> cacheToken;
   260     cachingChannel->GetCacheToken(getter_AddRefs(cacheToken));
   261     if (!cacheToken)
   262         return NS_ERROR_ABORT; // bail, no cache entry
   264     nsCOMPtr<nsICacheEntry> entryInfo =
   265         do_QueryInterface(cacheToken, &rv);
   266     if (NS_FAILED(rv)) return rv;
   268     uint32_t expTime;
   269     if (NS_SUCCEEDED(entryInfo->GetExpirationTime(&expTime))) {
   270         if (NowInSeconds() >= expTime) {
   271             LOG(("document cannot be reused from cache; "
   272                  "canceling prefetch\n"));
   273             return NS_BINDING_ABORTED;
   274         }
   275     }
   277     return NS_OK;
   278 }
   280 NS_IMETHODIMP
   281 nsPrefetchNode::OnDataAvailable(nsIRequest *aRequest,
   282                                 nsISupports *aContext,
   283                                 nsIInputStream *aStream,
   284                                 uint64_t aOffset,
   285                                 uint32_t aCount)
   286 {
   287     uint32_t bytesRead = 0;
   288     aStream->ReadSegments(NS_DiscardSegment, nullptr, aCount, &bytesRead);
   289     mBytesRead += bytesRead;
   290     LOG(("prefetched %u bytes [offset=%llu]\n", bytesRead, aOffset));
   291     return NS_OK;
   292 }
   295 NS_IMETHODIMP
   296 nsPrefetchNode::OnStopRequest(nsIRequest *aRequest,
   297                               nsISupports *aContext,
   298                               nsresult aStatus)
   299 {
   300     LOG(("done prefetching [status=%x]\n", aStatus));
   302     if (mBytesRead == 0 && aStatus == NS_OK) {
   303         // we didn't need to read (because LOAD_ONLY_IF_MODIFIED was
   304         // specified), but the object should report loadedSize as if it
   305         // did.
   306         mChannel->GetContentLength(&mBytesRead);
   307     }
   309     mService->NotifyLoadCompleted(this);
   310     mService->ProcessNextURI();
   311     return NS_OK;
   312 }
   314 //-----------------------------------------------------------------------------
   315 // nsPrefetchNode::nsIInterfaceRequestor
   316 //-----------------------------------------------------------------------------
   318 NS_IMETHODIMP
   319 nsPrefetchNode::GetInterface(const nsIID &aIID, void **aResult)
   320 {
   321     if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
   322         NS_ADDREF_THIS();
   323         *aResult = static_cast<nsIChannelEventSink *>(this);
   324         return NS_OK;
   325     }
   327     if (aIID.Equals(NS_GET_IID(nsIRedirectResultListener))) {
   328         NS_ADDREF_THIS();
   329         *aResult = static_cast<nsIRedirectResultListener *>(this);
   330         return NS_OK;
   331     }
   333     return NS_ERROR_NO_INTERFACE;
   334 }
   336 //-----------------------------------------------------------------------------
   337 // nsPrefetchNode::nsIChannelEventSink
   338 //-----------------------------------------------------------------------------
   340 NS_IMETHODIMP
   341 nsPrefetchNode::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
   342                                        nsIChannel *aNewChannel,
   343                                        uint32_t aFlags,
   344                                        nsIAsyncVerifyRedirectCallback *callback)
   345 {
   346     nsCOMPtr<nsIURI> newURI;
   347     nsresult rv = aNewChannel->GetURI(getter_AddRefs(newURI));
   348     if (NS_FAILED(rv))
   349         return rv;
   351     bool match;
   352     rv = newURI->SchemeIs("http", &match); 
   353     if (NS_FAILED(rv) || !match) {
   354         rv = newURI->SchemeIs("https", &match); 
   355         if (NS_FAILED(rv) || !match) {
   356             LOG(("rejected: URL is not of type http/https\n"));
   357             return NS_ERROR_ABORT;
   358         }
   359     }
   361     // HTTP request headers are not automatically forwarded to the new channel.
   362     nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aNewChannel);
   363     NS_ENSURE_STATE(httpChannel);
   365     httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("X-Moz"),
   366                                   NS_LITERAL_CSTRING("prefetch"),
   367                                   false);
   369     // Assign to mChannel after we get notification about success of the
   370     // redirect in OnRedirectResult.
   371     mRedirectChannel = aNewChannel;
   373     callback->OnRedirectVerifyCallback(NS_OK);
   374     return NS_OK;
   375 }
   377 //-----------------------------------------------------------------------------
   378 // nsPrefetchNode::nsIRedirectResultListener
   379 //-----------------------------------------------------------------------------
   381 NS_IMETHODIMP
   382 nsPrefetchNode::OnRedirectResult(bool proceeding)
   383 {
   384     if (proceeding && mRedirectChannel)
   385         mChannel = mRedirectChannel;
   387     mRedirectChannel = nullptr;
   389     return NS_OK;
   390 }
   392 //-----------------------------------------------------------------------------
   393 // nsPrefetchService <public>
   394 //-----------------------------------------------------------------------------
   396 nsPrefetchService::nsPrefetchService()
   397     : mQueueHead(nullptr)
   398     , mQueueTail(nullptr)
   399     , mStopCount(0)
   400     , mHaveProcessed(false)
   401     , mDisabled(true)
   402 {
   403 }
   405 nsPrefetchService::~nsPrefetchService()
   406 {
   407     Preferences::RemoveObserver(this, PREFETCH_PREF);
   408     // cannot reach destructor if prefetch in progress (listener owns reference
   409     // to this service)
   410     EmptyQueue();
   411 }
   413 nsresult
   414 nsPrefetchService::Init()
   415 {
   416 #if defined(PR_LOGGING)
   417     if (!gPrefetchLog)
   418         gPrefetchLog = PR_NewLogModule("nsPrefetch");
   419 #endif
   421     nsresult rv;
   423     // read prefs and hook up pref observer
   424     mDisabled = !Preferences::GetBool(PREFETCH_PREF, !mDisabled);
   425     Preferences::AddWeakObserver(this, PREFETCH_PREF);
   427     // Observe xpcom-shutdown event
   428     nsCOMPtr<nsIObserverService> observerService =
   429       mozilla::services::GetObserverService();
   430     if (!observerService)
   431       return NS_ERROR_FAILURE;
   433     rv = observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, true);
   434     NS_ENSURE_SUCCESS(rv, rv);
   436     if (!mDisabled)
   437         AddProgressListener();
   439     return NS_OK;
   440 }
   442 void
   443 nsPrefetchService::ProcessNextURI()
   444 {
   445     nsresult rv;
   446     nsCOMPtr<nsIURI> uri, referrer;
   448     mCurrentNode = nullptr;
   450     do {
   451         rv = DequeueNode(getter_AddRefs(mCurrentNode));
   453         if (NS_FAILED(rv)) break;
   455 #if defined(PR_LOGGING)
   456         if (LOG_ENABLED()) {
   457             nsAutoCString spec;
   458             mCurrentNode->mURI->GetSpec(spec);
   459             LOG(("ProcessNextURI [%s]\n", spec.get()));
   460         }
   461 #endif
   463         //
   464         // if opening the channel fails, then just skip to the next uri
   465         //
   466         nsRefPtr<nsPrefetchNode> node = mCurrentNode;
   467         rv = node->OpenChannel();
   468     }
   469     while (NS_FAILED(rv));
   470 }
   472 void
   473 nsPrefetchService::NotifyLoadRequested(nsPrefetchNode *node)
   474 {
   475     nsCOMPtr<nsIObserverService> observerService =
   476       mozilla::services::GetObserverService();
   477     if (!observerService)
   478       return;
   480     observerService->NotifyObservers(static_cast<nsIStreamListener*>(node),
   481                                      "prefetch-load-requested", nullptr);
   482 }
   484 void
   485 nsPrefetchService::NotifyLoadCompleted(nsPrefetchNode *node)
   486 {
   487     nsCOMPtr<nsIObserverService> observerService =
   488       mozilla::services::GetObserverService();
   489     if (!observerService)
   490       return;
   492     observerService->NotifyObservers(static_cast<nsIStreamListener*>(node),
   493                                      "prefetch-load-completed", nullptr);
   494 }
   496 //-----------------------------------------------------------------------------
   497 // nsPrefetchService <private>
   498 //-----------------------------------------------------------------------------
   500 void
   501 nsPrefetchService::AddProgressListener()
   502 {
   503     // Register as an observer for the document loader  
   504     nsCOMPtr<nsIWebProgress> progress = 
   505         do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID);
   506     if (progress)
   507         progress->AddProgressListener(this, nsIWebProgress::NOTIFY_STATE_DOCUMENT);
   508 }
   510 void
   511 nsPrefetchService::RemoveProgressListener()
   512 {
   513     // Register as an observer for the document loader  
   514     nsCOMPtr<nsIWebProgress> progress =
   515         do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID);
   516     if (progress)
   517         progress->RemoveProgressListener(this);
   518 }
   520 nsresult
   521 nsPrefetchService::EnqueueNode(nsPrefetchNode *aNode)
   522 {
   523     NS_ADDREF(aNode);
   525     if (!mQueueTail) {
   526         mQueueHead = aNode;
   527         mQueueTail = aNode;
   528     }
   529     else {
   530         mQueueTail->mNext = aNode;
   531         mQueueTail = aNode;
   532     }
   534     return NS_OK;
   535 }
   537 nsresult
   538 nsPrefetchService::EnqueueURI(nsIURI *aURI,
   539                               nsIURI *aReferrerURI,
   540                               nsIDOMNode *aSource,
   541                               nsPrefetchNode **aNode)
   542 {
   543     nsPrefetchNode *node = new nsPrefetchNode(this, aURI, aReferrerURI,
   544                                               aSource);
   545     if (!node)
   546         return NS_ERROR_OUT_OF_MEMORY;
   548     NS_ADDREF(*aNode = node);
   550     return EnqueueNode(node);
   551 }
   553 nsresult
   554 nsPrefetchService::DequeueNode(nsPrefetchNode **node)
   555 {
   556     if (!mQueueHead)
   557         return NS_ERROR_NOT_AVAILABLE;
   559     // give the ref to the caller
   560     *node = mQueueHead;
   561     mQueueHead = mQueueHead->mNext;
   562     (*node)->mNext = nullptr;
   564     if (!mQueueHead)
   565         mQueueTail = nullptr;
   567     return NS_OK;
   568 }
   570 void
   571 nsPrefetchService::EmptyQueue()
   572 {
   573     do {
   574         nsRefPtr<nsPrefetchNode> node;
   575         DequeueNode(getter_AddRefs(node));
   576     } while (mQueueHead);
   577 }
   579 void
   580 nsPrefetchService::StartPrefetching()
   581 {
   582     //
   583     // at initialization time we might miss the first DOCUMENT START
   584     // notification, so we have to be careful to avoid letting our
   585     // stop count go negative.
   586     //
   587     if (mStopCount > 0)
   588         mStopCount--;
   590     LOG(("StartPrefetching [stopcount=%d]\n", mStopCount));
   592     // only start prefetching after we've received enough DOCUMENT
   593     // STOP notifications.  we do this inorder to defer prefetching
   594     // until after all sub-frames have finished loading.
   595     if (mStopCount == 0 && !mCurrentNode) {
   596         mHaveProcessed = true;
   597         ProcessNextURI();
   598     }
   599 }
   601 void
   602 nsPrefetchService::StopPrefetching()
   603 {
   604     mStopCount++;
   606     LOG(("StopPrefetching [stopcount=%d]\n", mStopCount));
   608     // only kill the prefetch queue if we've actually started prefetching.
   609     if (!mCurrentNode)
   610         return;
   612     mCurrentNode->CancelChannel(NS_BINDING_ABORTED);
   613     mCurrentNode = nullptr;
   614     EmptyQueue();
   615 }
   617 //-----------------------------------------------------------------------------
   618 // nsPrefetchService::nsISupports
   619 //-----------------------------------------------------------------------------
   621 NS_IMPL_ISUPPORTS(nsPrefetchService,
   622                   nsIPrefetchService,
   623                   nsIWebProgressListener,
   624                   nsIObserver,
   625                   nsISupportsWeakReference)
   627 //-----------------------------------------------------------------------------
   628 // nsPrefetchService::nsIPrefetchService
   629 //-----------------------------------------------------------------------------
   631 nsresult
   632 nsPrefetchService::Prefetch(nsIURI *aURI,
   633                             nsIURI *aReferrerURI,
   634                             nsIDOMNode *aSource,
   635                             bool aExplicit)
   636 {
   637     nsresult rv;
   639     NS_ENSURE_ARG_POINTER(aURI);
   640     NS_ENSURE_ARG_POINTER(aReferrerURI);
   642 #if defined(PR_LOGGING)
   643     if (LOG_ENABLED()) {
   644         nsAutoCString spec;
   645         aURI->GetSpec(spec);
   646         LOG(("PrefetchURI [%s]\n", spec.get()));
   647     }
   648 #endif
   650     if (mDisabled) {
   651         LOG(("rejected: prefetch service is disabled\n"));
   652         return NS_ERROR_ABORT;
   653     }
   655     //
   656     // XXX we should really be asking the protocol handler if it supports
   657     // caching, so we can determine if there is any value to prefetching.
   658     // for now, we'll only prefetch http links since we know that's the 
   659     // most common case.  ignore https links since https content only goes
   660     // into the memory cache.
   661     //
   662     // XXX we might want to either leverage nsIProtocolHandler::protocolFlags
   663     // or possibly nsIRequest::loadFlags to determine if this URI should be
   664     // prefetched.
   665     //
   666     bool match;
   667     rv = aURI->SchemeIs("http", &match); 
   668     if (NS_FAILED(rv) || !match) {
   669         rv = aURI->SchemeIs("https", &match); 
   670         if (NS_FAILED(rv) || !match) {
   671             LOG(("rejected: URL is not of type http/https\n"));
   672             return NS_ERROR_ABORT;
   673         }
   674     }
   676     // 
   677     // the referrer URI must be http:
   678     //
   679     rv = aReferrerURI->SchemeIs("http", &match);
   680     if (NS_FAILED(rv) || !match) {
   681         rv = aReferrerURI->SchemeIs("https", &match);
   682         if (NS_FAILED(rv) || !match) {
   683             LOG(("rejected: referrer URL is neither http nor https\n"));
   684             return NS_ERROR_ABORT;
   685         }
   686     }
   688     // skip URLs that contain query strings, except URLs for which prefetching
   689     // has been explicitly requested.
   690     if (!aExplicit) {
   691         nsCOMPtr<nsIURL> url(do_QueryInterface(aURI, &rv));
   692         if (NS_FAILED(rv)) return rv;
   693         nsAutoCString query;
   694         rv = url->GetQuery(query);
   695         if (NS_FAILED(rv) || !query.IsEmpty()) {
   696             LOG(("rejected: URL has a query string\n"));
   697             return NS_ERROR_ABORT;
   698         }
   699     }
   701     //
   702     // cancel if being prefetched
   703     //
   704     if (mCurrentNode) {
   705         bool equals;
   706         if (NS_SUCCEEDED(mCurrentNode->mURI->Equals(aURI, &equals)) && equals) {
   707             LOG(("rejected: URL is already being prefetched\n"));
   708             return NS_ERROR_ABORT;
   709         }
   710     }
   712     //
   713     // cancel if already on the prefetch queue
   714     //
   715     nsPrefetchNode *node = mQueueHead;
   716     for (; node; node = node->mNext) {
   717         bool equals;
   718         if (NS_SUCCEEDED(node->mURI->Equals(aURI, &equals)) && equals) {
   719             LOG(("rejected: URL is already on prefetch queue\n"));
   720             return NS_ERROR_ABORT;
   721         }
   722     }
   724     nsRefPtr<nsPrefetchNode> enqueuedNode;
   725     rv = EnqueueURI(aURI, aReferrerURI, aSource,
   726                     getter_AddRefs(enqueuedNode));
   727     NS_ENSURE_SUCCESS(rv, rv);
   729     NotifyLoadRequested(enqueuedNode);
   731     // if there are no pages loading, kick off the request immediately
   732     if (mStopCount == 0 && mHaveProcessed)
   733         ProcessNextURI();
   735     return NS_OK;
   736 }
   738 NS_IMETHODIMP
   739 nsPrefetchService::PrefetchURI(nsIURI *aURI,
   740                                nsIURI *aReferrerURI,
   741                                nsIDOMNode *aSource,
   742                                bool aExplicit)
   743 {
   744     return Prefetch(aURI, aReferrerURI, aSource, aExplicit);
   745 }
   747 NS_IMETHODIMP
   748 nsPrefetchService::EnumerateQueue(nsISimpleEnumerator **aEnumerator)
   749 {
   750     *aEnumerator = new nsPrefetchQueueEnumerator(this);
   751     if (!*aEnumerator) return NS_ERROR_OUT_OF_MEMORY;
   753     NS_ADDREF(*aEnumerator);
   755     return NS_OK;
   756 }
   758 //-----------------------------------------------------------------------------
   759 // nsPrefetchService::nsIWebProgressListener
   760 //-----------------------------------------------------------------------------
   762 NS_IMETHODIMP
   763 nsPrefetchService::OnProgressChange(nsIWebProgress *aProgress,
   764                                   nsIRequest *aRequest, 
   765                                   int32_t curSelfProgress, 
   766                                   int32_t maxSelfProgress, 
   767                                   int32_t curTotalProgress, 
   768                                   int32_t maxTotalProgress)
   769 {
   770     NS_NOTREACHED("notification excluded in AddProgressListener(...)");
   771     return NS_OK;
   772 }
   774 NS_IMETHODIMP 
   775 nsPrefetchService::OnStateChange(nsIWebProgress* aWebProgress, 
   776                                  nsIRequest *aRequest, 
   777                                  uint32_t progressStateFlags, 
   778                                  nsresult aStatus)
   779 {
   780     if (progressStateFlags & STATE_IS_DOCUMENT) {
   781         if (progressStateFlags & STATE_STOP)
   782             StartPrefetching();
   783         else if (progressStateFlags & STATE_START)
   784             StopPrefetching();
   785     }
   787     return NS_OK;
   788 }
   791 NS_IMETHODIMP
   792 nsPrefetchService::OnLocationChange(nsIWebProgress* aWebProgress,
   793                                     nsIRequest* aRequest,
   794                                     nsIURI *location,
   795                                     uint32_t aFlags)
   796 {
   797     NS_NOTREACHED("notification excluded in AddProgressListener(...)");
   798     return NS_OK;
   799 }
   801 NS_IMETHODIMP 
   802 nsPrefetchService::OnStatusChange(nsIWebProgress* aWebProgress,
   803                                   nsIRequest* aRequest,
   804                                   nsresult aStatus,
   805                                   const char16_t* aMessage)
   806 {
   807     NS_NOTREACHED("notification excluded in AddProgressListener(...)");
   808     return NS_OK;
   809 }
   811 NS_IMETHODIMP 
   812 nsPrefetchService::OnSecurityChange(nsIWebProgress *aWebProgress, 
   813                                     nsIRequest *aRequest, 
   814                                     uint32_t state)
   815 {
   816     NS_NOTREACHED("notification excluded in AddProgressListener(...)");
   817     return NS_OK;
   818 }
   820 //-----------------------------------------------------------------------------
   821 // nsPrefetchService::nsIObserver
   822 //-----------------------------------------------------------------------------
   824 NS_IMETHODIMP
   825 nsPrefetchService::Observe(nsISupports     *aSubject,
   826                            const char      *aTopic,
   827                            const char16_t *aData)
   828 {
   829     LOG(("nsPrefetchService::Observe [topic=%s]\n", aTopic));
   831     if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
   832         StopPrefetching();
   833         EmptyQueue();
   834         mDisabled = true;
   835     }
   836     else if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
   837         if (Preferences::GetBool(PREFETCH_PREF, false)) {
   838             if (mDisabled) {
   839                 LOG(("enabling prefetching\n"));
   840                 mDisabled = false;
   841                 AddProgressListener();
   842             }
   843         } 
   844         else {
   845             if (!mDisabled) {
   846                 LOG(("disabling prefetching\n"));
   847                 StopPrefetching();
   848                 EmptyQueue();
   849                 mDisabled = true;
   850                 RemoveProgressListener();
   851             }
   852         }
   853     }
   855     return NS_OK;
   856 }
   858 // vim: ts=4 sw=4 expandtab

mercurial