1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/uriloader/prefetch/nsPrefetchService.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,858 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +#include "nsPrefetchService.h" 1.9 +#include "nsICacheEntry.h" 1.10 +#include "nsIServiceManager.h" 1.11 +#include "nsICategoryManager.h" 1.12 +#include "nsIObserverService.h" 1.13 +#include "nsIWebProgress.h" 1.14 +#include "nsCURILoader.h" 1.15 +#include "nsICachingChannel.h" 1.16 +#include "nsICacheVisitor.h" 1.17 +#include "nsIHttpChannel.h" 1.18 +#include "nsIURL.h" 1.19 +#include "nsISimpleEnumerator.h" 1.20 +#include "nsNetUtil.h" 1.21 +#include "nsString.h" 1.22 +#include "nsXPIDLString.h" 1.23 +#include "nsReadableUtils.h" 1.24 +#include "nsStreamUtils.h" 1.25 +#include "nsAutoPtr.h" 1.26 +#include "prtime.h" 1.27 +#include "prlog.h" 1.28 +#include "plstr.h" 1.29 +#include "nsIAsyncVerifyRedirectCallback.h" 1.30 +#include "mozilla/Preferences.h" 1.31 +#include "mozilla/Attributes.h" 1.32 +#include "nsIDOMNode.h" 1.33 +#include "nsINode.h" 1.34 +#include "nsIDocument.h" 1.35 + 1.36 +using namespace mozilla; 1.37 + 1.38 +#if defined(PR_LOGGING) 1.39 +// 1.40 +// To enable logging (see prlog.h for full details): 1.41 +// 1.42 +// set NSPR_LOG_MODULES=nsPrefetch:5 1.43 +// set NSPR_LOG_FILE=prefetch.log 1.44 +// 1.45 +// this enables PR_LOG_ALWAYS level information and places all output in 1.46 +// the file http.log 1.47 +// 1.48 +static PRLogModuleInfo *gPrefetchLog; 1.49 +#endif 1.50 + 1.51 +#undef LOG 1.52 +#define LOG(args) PR_LOG(gPrefetchLog, 4, args) 1.53 + 1.54 +#undef LOG_ENABLED 1.55 +#define LOG_ENABLED() PR_LOG_TEST(gPrefetchLog, 4) 1.56 + 1.57 +#define PREFETCH_PREF "network.prefetch-next" 1.58 + 1.59 +//----------------------------------------------------------------------------- 1.60 +// helpers 1.61 +//----------------------------------------------------------------------------- 1.62 + 1.63 +static inline uint32_t 1.64 +PRTimeToSeconds(PRTime t_usec) 1.65 +{ 1.66 + PRTime usec_per_sec = PR_USEC_PER_SEC; 1.67 + return uint32_t(t_usec /= usec_per_sec); 1.68 +} 1.69 + 1.70 +#define NowInSeconds() PRTimeToSeconds(PR_Now()) 1.71 + 1.72 +//----------------------------------------------------------------------------- 1.73 +// nsPrefetchQueueEnumerator 1.74 +//----------------------------------------------------------------------------- 1.75 +class nsPrefetchQueueEnumerator MOZ_FINAL : public nsISimpleEnumerator 1.76 +{ 1.77 +public: 1.78 + NS_DECL_ISUPPORTS 1.79 + NS_DECL_NSISIMPLEENUMERATOR 1.80 + nsPrefetchQueueEnumerator(nsPrefetchService *aService); 1.81 + ~nsPrefetchQueueEnumerator(); 1.82 + 1.83 +private: 1.84 + void Increment(); 1.85 + 1.86 + nsRefPtr<nsPrefetchService> mService; 1.87 + nsRefPtr<nsPrefetchNode> mCurrent; 1.88 + bool mStarted; 1.89 +}; 1.90 + 1.91 +//----------------------------------------------------------------------------- 1.92 +// nsPrefetchQueueEnumerator <public> 1.93 +//----------------------------------------------------------------------------- 1.94 +nsPrefetchQueueEnumerator::nsPrefetchQueueEnumerator(nsPrefetchService *aService) 1.95 + : mService(aService) 1.96 + , mStarted(false) 1.97 +{ 1.98 + Increment(); 1.99 +} 1.100 + 1.101 +nsPrefetchQueueEnumerator::~nsPrefetchQueueEnumerator() 1.102 +{ 1.103 +} 1.104 + 1.105 +//----------------------------------------------------------------------------- 1.106 +// nsPrefetchQueueEnumerator::nsISimpleEnumerator 1.107 +//----------------------------------------------------------------------------- 1.108 +NS_IMETHODIMP 1.109 +nsPrefetchQueueEnumerator::HasMoreElements(bool *aHasMore) 1.110 +{ 1.111 + *aHasMore = (mCurrent != nullptr); 1.112 + return NS_OK; 1.113 +} 1.114 + 1.115 +NS_IMETHODIMP 1.116 +nsPrefetchQueueEnumerator::GetNext(nsISupports **aItem) 1.117 +{ 1.118 + if (!mCurrent) return NS_ERROR_FAILURE; 1.119 + 1.120 + NS_ADDREF(*aItem = static_cast<nsIStreamListener*>(mCurrent.get())); 1.121 + 1.122 + Increment(); 1.123 + 1.124 + return NS_OK; 1.125 +} 1.126 + 1.127 +//----------------------------------------------------------------------------- 1.128 +// nsPrefetchQueueEnumerator <private> 1.129 +//----------------------------------------------------------------------------- 1.130 + 1.131 +void 1.132 +nsPrefetchQueueEnumerator::Increment() 1.133 +{ 1.134 + if (!mStarted) { 1.135 + // If the service is currently serving a request, it won't be in 1.136 + // the pending queue, so we return it first. If it isn't, we'll 1.137 + // just start with the pending queue. 1.138 + mStarted = true; 1.139 + mCurrent = mService->GetCurrentNode(); 1.140 + if (!mCurrent) 1.141 + mCurrent = mService->GetQueueHead(); 1.142 + return; 1.143 + } 1.144 + 1.145 + if (mCurrent) { 1.146 + if (mCurrent == mService->GetCurrentNode()) { 1.147 + // If we just returned the node being processed by the service, 1.148 + // start with the pending queue 1.149 + mCurrent = mService->GetQueueHead(); 1.150 + } 1.151 + else { 1.152 + // Otherwise just advance to the next item in the queue 1.153 + mCurrent = mCurrent->mNext; 1.154 + } 1.155 + } 1.156 +} 1.157 + 1.158 +//----------------------------------------------------------------------------- 1.159 +// nsPrefetchQueueEnumerator::nsISupports 1.160 +//----------------------------------------------------------------------------- 1.161 + 1.162 +NS_IMPL_ISUPPORTS(nsPrefetchQueueEnumerator, nsISimpleEnumerator) 1.163 + 1.164 +//----------------------------------------------------------------------------- 1.165 +// nsPrefetchNode <public> 1.166 +//----------------------------------------------------------------------------- 1.167 + 1.168 +nsPrefetchNode::nsPrefetchNode(nsPrefetchService *aService, 1.169 + nsIURI *aURI, 1.170 + nsIURI *aReferrerURI, 1.171 + nsIDOMNode *aSource) 1.172 + : mNext(nullptr) 1.173 + , mURI(aURI) 1.174 + , mReferrerURI(aReferrerURI) 1.175 + , mService(aService) 1.176 + , mChannel(nullptr) 1.177 + , mBytesRead(0) 1.178 +{ 1.179 + mSource = do_GetWeakReference(aSource); 1.180 +} 1.181 + 1.182 +nsresult 1.183 +nsPrefetchNode::OpenChannel() 1.184 +{ 1.185 + nsCOMPtr<nsINode> source = do_QueryReferent(mSource); 1.186 + if (!source) { 1.187 + // Don't attempt to prefetch if we don't have a source node 1.188 + // (which should never happen). 1.189 + return NS_ERROR_FAILURE; 1.190 + } 1.191 + nsCOMPtr<nsILoadGroup> loadGroup = source->OwnerDoc()->GetDocumentLoadGroup(); 1.192 + nsresult rv = NS_NewChannel(getter_AddRefs(mChannel), 1.193 + mURI, 1.194 + nullptr, loadGroup, this, 1.195 + nsIRequest::LOAD_BACKGROUND | 1.196 + nsICachingChannel::LOAD_ONLY_IF_MODIFIED); 1.197 + NS_ENSURE_SUCCESS(rv, rv); 1.198 + 1.199 + // configure HTTP specific stuff 1.200 + nsCOMPtr<nsIHttpChannel> httpChannel = 1.201 + do_QueryInterface(mChannel); 1.202 + if (httpChannel) { 1.203 + httpChannel->SetReferrer(mReferrerURI); 1.204 + httpChannel->SetRequestHeader( 1.205 + NS_LITERAL_CSTRING("X-Moz"), 1.206 + NS_LITERAL_CSTRING("prefetch"), 1.207 + false); 1.208 + } 1.209 + 1.210 + rv = mChannel->AsyncOpen(this, nullptr); 1.211 + NS_ENSURE_SUCCESS(rv, rv); 1.212 + 1.213 + return NS_OK; 1.214 +} 1.215 + 1.216 +nsresult 1.217 +nsPrefetchNode::CancelChannel(nsresult error) 1.218 +{ 1.219 + mChannel->Cancel(error); 1.220 + mChannel = nullptr; 1.221 + 1.222 + return NS_OK; 1.223 +} 1.224 + 1.225 +//----------------------------------------------------------------------------- 1.226 +// nsPrefetchNode::nsISupports 1.227 +//----------------------------------------------------------------------------- 1.228 + 1.229 +NS_IMPL_ISUPPORTS(nsPrefetchNode, 1.230 + nsIRequestObserver, 1.231 + nsIStreamListener, 1.232 + nsIInterfaceRequestor, 1.233 + nsIChannelEventSink, 1.234 + nsIRedirectResultListener) 1.235 + 1.236 +//----------------------------------------------------------------------------- 1.237 +// nsPrefetchNode::nsIStreamListener 1.238 +//----------------------------------------------------------------------------- 1.239 + 1.240 +NS_IMETHODIMP 1.241 +nsPrefetchNode::OnStartRequest(nsIRequest *aRequest, 1.242 + nsISupports *aContext) 1.243 +{ 1.244 + nsresult rv; 1.245 + 1.246 + nsCOMPtr<nsICachingChannel> cachingChannel = 1.247 + do_QueryInterface(aRequest, &rv); 1.248 + if (NS_FAILED(rv)) return rv; 1.249 + 1.250 + // no need to prefetch a document that is already in the cache 1.251 + bool fromCache; 1.252 + if (NS_SUCCEEDED(cachingChannel->IsFromCache(&fromCache)) && 1.253 + fromCache) { 1.254 + LOG(("document is already in the cache; canceling prefetch\n")); 1.255 + return NS_BINDING_ABORTED; 1.256 + } 1.257 + 1.258 + // 1.259 + // no need to prefetch a document that must be requested fresh each 1.260 + // and every time. 1.261 + // 1.262 + nsCOMPtr<nsISupports> cacheToken; 1.263 + cachingChannel->GetCacheToken(getter_AddRefs(cacheToken)); 1.264 + if (!cacheToken) 1.265 + return NS_ERROR_ABORT; // bail, no cache entry 1.266 + 1.267 + nsCOMPtr<nsICacheEntry> entryInfo = 1.268 + do_QueryInterface(cacheToken, &rv); 1.269 + if (NS_FAILED(rv)) return rv; 1.270 + 1.271 + uint32_t expTime; 1.272 + if (NS_SUCCEEDED(entryInfo->GetExpirationTime(&expTime))) { 1.273 + if (NowInSeconds() >= expTime) { 1.274 + LOG(("document cannot be reused from cache; " 1.275 + "canceling prefetch\n")); 1.276 + return NS_BINDING_ABORTED; 1.277 + } 1.278 + } 1.279 + 1.280 + return NS_OK; 1.281 +} 1.282 + 1.283 +NS_IMETHODIMP 1.284 +nsPrefetchNode::OnDataAvailable(nsIRequest *aRequest, 1.285 + nsISupports *aContext, 1.286 + nsIInputStream *aStream, 1.287 + uint64_t aOffset, 1.288 + uint32_t aCount) 1.289 +{ 1.290 + uint32_t bytesRead = 0; 1.291 + aStream->ReadSegments(NS_DiscardSegment, nullptr, aCount, &bytesRead); 1.292 + mBytesRead += bytesRead; 1.293 + LOG(("prefetched %u bytes [offset=%llu]\n", bytesRead, aOffset)); 1.294 + return NS_OK; 1.295 +} 1.296 + 1.297 + 1.298 +NS_IMETHODIMP 1.299 +nsPrefetchNode::OnStopRequest(nsIRequest *aRequest, 1.300 + nsISupports *aContext, 1.301 + nsresult aStatus) 1.302 +{ 1.303 + LOG(("done prefetching [status=%x]\n", aStatus)); 1.304 + 1.305 + if (mBytesRead == 0 && aStatus == NS_OK) { 1.306 + // we didn't need to read (because LOAD_ONLY_IF_MODIFIED was 1.307 + // specified), but the object should report loadedSize as if it 1.308 + // did. 1.309 + mChannel->GetContentLength(&mBytesRead); 1.310 + } 1.311 + 1.312 + mService->NotifyLoadCompleted(this); 1.313 + mService->ProcessNextURI(); 1.314 + return NS_OK; 1.315 +} 1.316 + 1.317 +//----------------------------------------------------------------------------- 1.318 +// nsPrefetchNode::nsIInterfaceRequestor 1.319 +//----------------------------------------------------------------------------- 1.320 + 1.321 +NS_IMETHODIMP 1.322 +nsPrefetchNode::GetInterface(const nsIID &aIID, void **aResult) 1.323 +{ 1.324 + if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) { 1.325 + NS_ADDREF_THIS(); 1.326 + *aResult = static_cast<nsIChannelEventSink *>(this); 1.327 + return NS_OK; 1.328 + } 1.329 + 1.330 + if (aIID.Equals(NS_GET_IID(nsIRedirectResultListener))) { 1.331 + NS_ADDREF_THIS(); 1.332 + *aResult = static_cast<nsIRedirectResultListener *>(this); 1.333 + return NS_OK; 1.334 + } 1.335 + 1.336 + return NS_ERROR_NO_INTERFACE; 1.337 +} 1.338 + 1.339 +//----------------------------------------------------------------------------- 1.340 +// nsPrefetchNode::nsIChannelEventSink 1.341 +//----------------------------------------------------------------------------- 1.342 + 1.343 +NS_IMETHODIMP 1.344 +nsPrefetchNode::AsyncOnChannelRedirect(nsIChannel *aOldChannel, 1.345 + nsIChannel *aNewChannel, 1.346 + uint32_t aFlags, 1.347 + nsIAsyncVerifyRedirectCallback *callback) 1.348 +{ 1.349 + nsCOMPtr<nsIURI> newURI; 1.350 + nsresult rv = aNewChannel->GetURI(getter_AddRefs(newURI)); 1.351 + if (NS_FAILED(rv)) 1.352 + return rv; 1.353 + 1.354 + bool match; 1.355 + rv = newURI->SchemeIs("http", &match); 1.356 + if (NS_FAILED(rv) || !match) { 1.357 + rv = newURI->SchemeIs("https", &match); 1.358 + if (NS_FAILED(rv) || !match) { 1.359 + LOG(("rejected: URL is not of type http/https\n")); 1.360 + return NS_ERROR_ABORT; 1.361 + } 1.362 + } 1.363 + 1.364 + // HTTP request headers are not automatically forwarded to the new channel. 1.365 + nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aNewChannel); 1.366 + NS_ENSURE_STATE(httpChannel); 1.367 + 1.368 + httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("X-Moz"), 1.369 + NS_LITERAL_CSTRING("prefetch"), 1.370 + false); 1.371 + 1.372 + // Assign to mChannel after we get notification about success of the 1.373 + // redirect in OnRedirectResult. 1.374 + mRedirectChannel = aNewChannel; 1.375 + 1.376 + callback->OnRedirectVerifyCallback(NS_OK); 1.377 + return NS_OK; 1.378 +} 1.379 + 1.380 +//----------------------------------------------------------------------------- 1.381 +// nsPrefetchNode::nsIRedirectResultListener 1.382 +//----------------------------------------------------------------------------- 1.383 + 1.384 +NS_IMETHODIMP 1.385 +nsPrefetchNode::OnRedirectResult(bool proceeding) 1.386 +{ 1.387 + if (proceeding && mRedirectChannel) 1.388 + mChannel = mRedirectChannel; 1.389 + 1.390 + mRedirectChannel = nullptr; 1.391 + 1.392 + return NS_OK; 1.393 +} 1.394 + 1.395 +//----------------------------------------------------------------------------- 1.396 +// nsPrefetchService <public> 1.397 +//----------------------------------------------------------------------------- 1.398 + 1.399 +nsPrefetchService::nsPrefetchService() 1.400 + : mQueueHead(nullptr) 1.401 + , mQueueTail(nullptr) 1.402 + , mStopCount(0) 1.403 + , mHaveProcessed(false) 1.404 + , mDisabled(true) 1.405 +{ 1.406 +} 1.407 + 1.408 +nsPrefetchService::~nsPrefetchService() 1.409 +{ 1.410 + Preferences::RemoveObserver(this, PREFETCH_PREF); 1.411 + // cannot reach destructor if prefetch in progress (listener owns reference 1.412 + // to this service) 1.413 + EmptyQueue(); 1.414 +} 1.415 + 1.416 +nsresult 1.417 +nsPrefetchService::Init() 1.418 +{ 1.419 +#if defined(PR_LOGGING) 1.420 + if (!gPrefetchLog) 1.421 + gPrefetchLog = PR_NewLogModule("nsPrefetch"); 1.422 +#endif 1.423 + 1.424 + nsresult rv; 1.425 + 1.426 + // read prefs and hook up pref observer 1.427 + mDisabled = !Preferences::GetBool(PREFETCH_PREF, !mDisabled); 1.428 + Preferences::AddWeakObserver(this, PREFETCH_PREF); 1.429 + 1.430 + // Observe xpcom-shutdown event 1.431 + nsCOMPtr<nsIObserverService> observerService = 1.432 + mozilla::services::GetObserverService(); 1.433 + if (!observerService) 1.434 + return NS_ERROR_FAILURE; 1.435 + 1.436 + rv = observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, true); 1.437 + NS_ENSURE_SUCCESS(rv, rv); 1.438 + 1.439 + if (!mDisabled) 1.440 + AddProgressListener(); 1.441 + 1.442 + return NS_OK; 1.443 +} 1.444 + 1.445 +void 1.446 +nsPrefetchService::ProcessNextURI() 1.447 +{ 1.448 + nsresult rv; 1.449 + nsCOMPtr<nsIURI> uri, referrer; 1.450 + 1.451 + mCurrentNode = nullptr; 1.452 + 1.453 + do { 1.454 + rv = DequeueNode(getter_AddRefs(mCurrentNode)); 1.455 + 1.456 + if (NS_FAILED(rv)) break; 1.457 + 1.458 +#if defined(PR_LOGGING) 1.459 + if (LOG_ENABLED()) { 1.460 + nsAutoCString spec; 1.461 + mCurrentNode->mURI->GetSpec(spec); 1.462 + LOG(("ProcessNextURI [%s]\n", spec.get())); 1.463 + } 1.464 +#endif 1.465 + 1.466 + // 1.467 + // if opening the channel fails, then just skip to the next uri 1.468 + // 1.469 + nsRefPtr<nsPrefetchNode> node = mCurrentNode; 1.470 + rv = node->OpenChannel(); 1.471 + } 1.472 + while (NS_FAILED(rv)); 1.473 +} 1.474 + 1.475 +void 1.476 +nsPrefetchService::NotifyLoadRequested(nsPrefetchNode *node) 1.477 +{ 1.478 + nsCOMPtr<nsIObserverService> observerService = 1.479 + mozilla::services::GetObserverService(); 1.480 + if (!observerService) 1.481 + return; 1.482 + 1.483 + observerService->NotifyObservers(static_cast<nsIStreamListener*>(node), 1.484 + "prefetch-load-requested", nullptr); 1.485 +} 1.486 + 1.487 +void 1.488 +nsPrefetchService::NotifyLoadCompleted(nsPrefetchNode *node) 1.489 +{ 1.490 + nsCOMPtr<nsIObserverService> observerService = 1.491 + mozilla::services::GetObserverService(); 1.492 + if (!observerService) 1.493 + return; 1.494 + 1.495 + observerService->NotifyObservers(static_cast<nsIStreamListener*>(node), 1.496 + "prefetch-load-completed", nullptr); 1.497 +} 1.498 + 1.499 +//----------------------------------------------------------------------------- 1.500 +// nsPrefetchService <private> 1.501 +//----------------------------------------------------------------------------- 1.502 + 1.503 +void 1.504 +nsPrefetchService::AddProgressListener() 1.505 +{ 1.506 + // Register as an observer for the document loader 1.507 + nsCOMPtr<nsIWebProgress> progress = 1.508 + do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID); 1.509 + if (progress) 1.510 + progress->AddProgressListener(this, nsIWebProgress::NOTIFY_STATE_DOCUMENT); 1.511 +} 1.512 + 1.513 +void 1.514 +nsPrefetchService::RemoveProgressListener() 1.515 +{ 1.516 + // Register as an observer for the document loader 1.517 + nsCOMPtr<nsIWebProgress> progress = 1.518 + do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID); 1.519 + if (progress) 1.520 + progress->RemoveProgressListener(this); 1.521 +} 1.522 + 1.523 +nsresult 1.524 +nsPrefetchService::EnqueueNode(nsPrefetchNode *aNode) 1.525 +{ 1.526 + NS_ADDREF(aNode); 1.527 + 1.528 + if (!mQueueTail) { 1.529 + mQueueHead = aNode; 1.530 + mQueueTail = aNode; 1.531 + } 1.532 + else { 1.533 + mQueueTail->mNext = aNode; 1.534 + mQueueTail = aNode; 1.535 + } 1.536 + 1.537 + return NS_OK; 1.538 +} 1.539 + 1.540 +nsresult 1.541 +nsPrefetchService::EnqueueURI(nsIURI *aURI, 1.542 + nsIURI *aReferrerURI, 1.543 + nsIDOMNode *aSource, 1.544 + nsPrefetchNode **aNode) 1.545 +{ 1.546 + nsPrefetchNode *node = new nsPrefetchNode(this, aURI, aReferrerURI, 1.547 + aSource); 1.548 + if (!node) 1.549 + return NS_ERROR_OUT_OF_MEMORY; 1.550 + 1.551 + NS_ADDREF(*aNode = node); 1.552 + 1.553 + return EnqueueNode(node); 1.554 +} 1.555 + 1.556 +nsresult 1.557 +nsPrefetchService::DequeueNode(nsPrefetchNode **node) 1.558 +{ 1.559 + if (!mQueueHead) 1.560 + return NS_ERROR_NOT_AVAILABLE; 1.561 + 1.562 + // give the ref to the caller 1.563 + *node = mQueueHead; 1.564 + mQueueHead = mQueueHead->mNext; 1.565 + (*node)->mNext = nullptr; 1.566 + 1.567 + if (!mQueueHead) 1.568 + mQueueTail = nullptr; 1.569 + 1.570 + return NS_OK; 1.571 +} 1.572 + 1.573 +void 1.574 +nsPrefetchService::EmptyQueue() 1.575 +{ 1.576 + do { 1.577 + nsRefPtr<nsPrefetchNode> node; 1.578 + DequeueNode(getter_AddRefs(node)); 1.579 + } while (mQueueHead); 1.580 +} 1.581 + 1.582 +void 1.583 +nsPrefetchService::StartPrefetching() 1.584 +{ 1.585 + // 1.586 + // at initialization time we might miss the first DOCUMENT START 1.587 + // notification, so we have to be careful to avoid letting our 1.588 + // stop count go negative. 1.589 + // 1.590 + if (mStopCount > 0) 1.591 + mStopCount--; 1.592 + 1.593 + LOG(("StartPrefetching [stopcount=%d]\n", mStopCount)); 1.594 + 1.595 + // only start prefetching after we've received enough DOCUMENT 1.596 + // STOP notifications. we do this inorder to defer prefetching 1.597 + // until after all sub-frames have finished loading. 1.598 + if (mStopCount == 0 && !mCurrentNode) { 1.599 + mHaveProcessed = true; 1.600 + ProcessNextURI(); 1.601 + } 1.602 +} 1.603 + 1.604 +void 1.605 +nsPrefetchService::StopPrefetching() 1.606 +{ 1.607 + mStopCount++; 1.608 + 1.609 + LOG(("StopPrefetching [stopcount=%d]\n", mStopCount)); 1.610 + 1.611 + // only kill the prefetch queue if we've actually started prefetching. 1.612 + if (!mCurrentNode) 1.613 + return; 1.614 + 1.615 + mCurrentNode->CancelChannel(NS_BINDING_ABORTED); 1.616 + mCurrentNode = nullptr; 1.617 + EmptyQueue(); 1.618 +} 1.619 + 1.620 +//----------------------------------------------------------------------------- 1.621 +// nsPrefetchService::nsISupports 1.622 +//----------------------------------------------------------------------------- 1.623 + 1.624 +NS_IMPL_ISUPPORTS(nsPrefetchService, 1.625 + nsIPrefetchService, 1.626 + nsIWebProgressListener, 1.627 + nsIObserver, 1.628 + nsISupportsWeakReference) 1.629 + 1.630 +//----------------------------------------------------------------------------- 1.631 +// nsPrefetchService::nsIPrefetchService 1.632 +//----------------------------------------------------------------------------- 1.633 + 1.634 +nsresult 1.635 +nsPrefetchService::Prefetch(nsIURI *aURI, 1.636 + nsIURI *aReferrerURI, 1.637 + nsIDOMNode *aSource, 1.638 + bool aExplicit) 1.639 +{ 1.640 + nsresult rv; 1.641 + 1.642 + NS_ENSURE_ARG_POINTER(aURI); 1.643 + NS_ENSURE_ARG_POINTER(aReferrerURI); 1.644 + 1.645 +#if defined(PR_LOGGING) 1.646 + if (LOG_ENABLED()) { 1.647 + nsAutoCString spec; 1.648 + aURI->GetSpec(spec); 1.649 + LOG(("PrefetchURI [%s]\n", spec.get())); 1.650 + } 1.651 +#endif 1.652 + 1.653 + if (mDisabled) { 1.654 + LOG(("rejected: prefetch service is disabled\n")); 1.655 + return NS_ERROR_ABORT; 1.656 + } 1.657 + 1.658 + // 1.659 + // XXX we should really be asking the protocol handler if it supports 1.660 + // caching, so we can determine if there is any value to prefetching. 1.661 + // for now, we'll only prefetch http links since we know that's the 1.662 + // most common case. ignore https links since https content only goes 1.663 + // into the memory cache. 1.664 + // 1.665 + // XXX we might want to either leverage nsIProtocolHandler::protocolFlags 1.666 + // or possibly nsIRequest::loadFlags to determine if this URI should be 1.667 + // prefetched. 1.668 + // 1.669 + bool match; 1.670 + rv = aURI->SchemeIs("http", &match); 1.671 + if (NS_FAILED(rv) || !match) { 1.672 + rv = aURI->SchemeIs("https", &match); 1.673 + if (NS_FAILED(rv) || !match) { 1.674 + LOG(("rejected: URL is not of type http/https\n")); 1.675 + return NS_ERROR_ABORT; 1.676 + } 1.677 + } 1.678 + 1.679 + // 1.680 + // the referrer URI must be http: 1.681 + // 1.682 + rv = aReferrerURI->SchemeIs("http", &match); 1.683 + if (NS_FAILED(rv) || !match) { 1.684 + rv = aReferrerURI->SchemeIs("https", &match); 1.685 + if (NS_FAILED(rv) || !match) { 1.686 + LOG(("rejected: referrer URL is neither http nor https\n")); 1.687 + return NS_ERROR_ABORT; 1.688 + } 1.689 + } 1.690 + 1.691 + // skip URLs that contain query strings, except URLs for which prefetching 1.692 + // has been explicitly requested. 1.693 + if (!aExplicit) { 1.694 + nsCOMPtr<nsIURL> url(do_QueryInterface(aURI, &rv)); 1.695 + if (NS_FAILED(rv)) return rv; 1.696 + nsAutoCString query; 1.697 + rv = url->GetQuery(query); 1.698 + if (NS_FAILED(rv) || !query.IsEmpty()) { 1.699 + LOG(("rejected: URL has a query string\n")); 1.700 + return NS_ERROR_ABORT; 1.701 + } 1.702 + } 1.703 + 1.704 + // 1.705 + // cancel if being prefetched 1.706 + // 1.707 + if (mCurrentNode) { 1.708 + bool equals; 1.709 + if (NS_SUCCEEDED(mCurrentNode->mURI->Equals(aURI, &equals)) && equals) { 1.710 + LOG(("rejected: URL is already being prefetched\n")); 1.711 + return NS_ERROR_ABORT; 1.712 + } 1.713 + } 1.714 + 1.715 + // 1.716 + // cancel if already on the prefetch queue 1.717 + // 1.718 + nsPrefetchNode *node = mQueueHead; 1.719 + for (; node; node = node->mNext) { 1.720 + bool equals; 1.721 + if (NS_SUCCEEDED(node->mURI->Equals(aURI, &equals)) && equals) { 1.722 + LOG(("rejected: URL is already on prefetch queue\n")); 1.723 + return NS_ERROR_ABORT; 1.724 + } 1.725 + } 1.726 + 1.727 + nsRefPtr<nsPrefetchNode> enqueuedNode; 1.728 + rv = EnqueueURI(aURI, aReferrerURI, aSource, 1.729 + getter_AddRefs(enqueuedNode)); 1.730 + NS_ENSURE_SUCCESS(rv, rv); 1.731 + 1.732 + NotifyLoadRequested(enqueuedNode); 1.733 + 1.734 + // if there are no pages loading, kick off the request immediately 1.735 + if (mStopCount == 0 && mHaveProcessed) 1.736 + ProcessNextURI(); 1.737 + 1.738 + return NS_OK; 1.739 +} 1.740 + 1.741 +NS_IMETHODIMP 1.742 +nsPrefetchService::PrefetchURI(nsIURI *aURI, 1.743 + nsIURI *aReferrerURI, 1.744 + nsIDOMNode *aSource, 1.745 + bool aExplicit) 1.746 +{ 1.747 + return Prefetch(aURI, aReferrerURI, aSource, aExplicit); 1.748 +} 1.749 + 1.750 +NS_IMETHODIMP 1.751 +nsPrefetchService::EnumerateQueue(nsISimpleEnumerator **aEnumerator) 1.752 +{ 1.753 + *aEnumerator = new nsPrefetchQueueEnumerator(this); 1.754 + if (!*aEnumerator) return NS_ERROR_OUT_OF_MEMORY; 1.755 + 1.756 + NS_ADDREF(*aEnumerator); 1.757 + 1.758 + return NS_OK; 1.759 +} 1.760 + 1.761 +//----------------------------------------------------------------------------- 1.762 +// nsPrefetchService::nsIWebProgressListener 1.763 +//----------------------------------------------------------------------------- 1.764 + 1.765 +NS_IMETHODIMP 1.766 +nsPrefetchService::OnProgressChange(nsIWebProgress *aProgress, 1.767 + nsIRequest *aRequest, 1.768 + int32_t curSelfProgress, 1.769 + int32_t maxSelfProgress, 1.770 + int32_t curTotalProgress, 1.771 + int32_t maxTotalProgress) 1.772 +{ 1.773 + NS_NOTREACHED("notification excluded in AddProgressListener(...)"); 1.774 + return NS_OK; 1.775 +} 1.776 + 1.777 +NS_IMETHODIMP 1.778 +nsPrefetchService::OnStateChange(nsIWebProgress* aWebProgress, 1.779 + nsIRequest *aRequest, 1.780 + uint32_t progressStateFlags, 1.781 + nsresult aStatus) 1.782 +{ 1.783 + if (progressStateFlags & STATE_IS_DOCUMENT) { 1.784 + if (progressStateFlags & STATE_STOP) 1.785 + StartPrefetching(); 1.786 + else if (progressStateFlags & STATE_START) 1.787 + StopPrefetching(); 1.788 + } 1.789 + 1.790 + return NS_OK; 1.791 +} 1.792 + 1.793 + 1.794 +NS_IMETHODIMP 1.795 +nsPrefetchService::OnLocationChange(nsIWebProgress* aWebProgress, 1.796 + nsIRequest* aRequest, 1.797 + nsIURI *location, 1.798 + uint32_t aFlags) 1.799 +{ 1.800 + NS_NOTREACHED("notification excluded in AddProgressListener(...)"); 1.801 + return NS_OK; 1.802 +} 1.803 + 1.804 +NS_IMETHODIMP 1.805 +nsPrefetchService::OnStatusChange(nsIWebProgress* aWebProgress, 1.806 + nsIRequest* aRequest, 1.807 + nsresult aStatus, 1.808 + const char16_t* aMessage) 1.809 +{ 1.810 + NS_NOTREACHED("notification excluded in AddProgressListener(...)"); 1.811 + return NS_OK; 1.812 +} 1.813 + 1.814 +NS_IMETHODIMP 1.815 +nsPrefetchService::OnSecurityChange(nsIWebProgress *aWebProgress, 1.816 + nsIRequest *aRequest, 1.817 + uint32_t state) 1.818 +{ 1.819 + NS_NOTREACHED("notification excluded in AddProgressListener(...)"); 1.820 + return NS_OK; 1.821 +} 1.822 + 1.823 +//----------------------------------------------------------------------------- 1.824 +// nsPrefetchService::nsIObserver 1.825 +//----------------------------------------------------------------------------- 1.826 + 1.827 +NS_IMETHODIMP 1.828 +nsPrefetchService::Observe(nsISupports *aSubject, 1.829 + const char *aTopic, 1.830 + const char16_t *aData) 1.831 +{ 1.832 + LOG(("nsPrefetchService::Observe [topic=%s]\n", aTopic)); 1.833 + 1.834 + if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) { 1.835 + StopPrefetching(); 1.836 + EmptyQueue(); 1.837 + mDisabled = true; 1.838 + } 1.839 + else if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) { 1.840 + if (Preferences::GetBool(PREFETCH_PREF, false)) { 1.841 + if (mDisabled) { 1.842 + LOG(("enabling prefetching\n")); 1.843 + mDisabled = false; 1.844 + AddProgressListener(); 1.845 + } 1.846 + } 1.847 + else { 1.848 + if (!mDisabled) { 1.849 + LOG(("disabling prefetching\n")); 1.850 + StopPrefetching(); 1.851 + EmptyQueue(); 1.852 + mDisabled = true; 1.853 + RemoveProgressListener(); 1.854 + } 1.855 + } 1.856 + } 1.857 + 1.858 + return NS_OK; 1.859 +} 1.860 + 1.861 +// vim: ts=4 sw=4 expandtab