uriloader/prefetch/nsPrefetchService.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:46ca57e5419f
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/. */
4
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"
32
33 using namespace mozilla;
34
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
47
48 #undef LOG
49 #define LOG(args) PR_LOG(gPrefetchLog, 4, args)
50
51 #undef LOG_ENABLED
52 #define LOG_ENABLED() PR_LOG_TEST(gPrefetchLog, 4)
53
54 #define PREFETCH_PREF "network.prefetch-next"
55
56 //-----------------------------------------------------------------------------
57 // helpers
58 //-----------------------------------------------------------------------------
59
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 }
66
67 #define NowInSeconds() PRTimeToSeconds(PR_Now())
68
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();
79
80 private:
81 void Increment();
82
83 nsRefPtr<nsPrefetchService> mService;
84 nsRefPtr<nsPrefetchNode> mCurrent;
85 bool mStarted;
86 };
87
88 //-----------------------------------------------------------------------------
89 // nsPrefetchQueueEnumerator <public>
90 //-----------------------------------------------------------------------------
91 nsPrefetchQueueEnumerator::nsPrefetchQueueEnumerator(nsPrefetchService *aService)
92 : mService(aService)
93 , mStarted(false)
94 {
95 Increment();
96 }
97
98 nsPrefetchQueueEnumerator::~nsPrefetchQueueEnumerator()
99 {
100 }
101
102 //-----------------------------------------------------------------------------
103 // nsPrefetchQueueEnumerator::nsISimpleEnumerator
104 //-----------------------------------------------------------------------------
105 NS_IMETHODIMP
106 nsPrefetchQueueEnumerator::HasMoreElements(bool *aHasMore)
107 {
108 *aHasMore = (mCurrent != nullptr);
109 return NS_OK;
110 }
111
112 NS_IMETHODIMP
113 nsPrefetchQueueEnumerator::GetNext(nsISupports **aItem)
114 {
115 if (!mCurrent) return NS_ERROR_FAILURE;
116
117 NS_ADDREF(*aItem = static_cast<nsIStreamListener*>(mCurrent.get()));
118
119 Increment();
120
121 return NS_OK;
122 }
123
124 //-----------------------------------------------------------------------------
125 // nsPrefetchQueueEnumerator <private>
126 //-----------------------------------------------------------------------------
127
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 }
141
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 }
154
155 //-----------------------------------------------------------------------------
156 // nsPrefetchQueueEnumerator::nsISupports
157 //-----------------------------------------------------------------------------
158
159 NS_IMPL_ISUPPORTS(nsPrefetchQueueEnumerator, nsISimpleEnumerator)
160
161 //-----------------------------------------------------------------------------
162 // nsPrefetchNode <public>
163 //-----------------------------------------------------------------------------
164
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 }
178
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);
195
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 }
206
207 rv = mChannel->AsyncOpen(this, nullptr);
208 NS_ENSURE_SUCCESS(rv, rv);
209
210 return NS_OK;
211 }
212
213 nsresult
214 nsPrefetchNode::CancelChannel(nsresult error)
215 {
216 mChannel->Cancel(error);
217 mChannel = nullptr;
218
219 return NS_OK;
220 }
221
222 //-----------------------------------------------------------------------------
223 // nsPrefetchNode::nsISupports
224 //-----------------------------------------------------------------------------
225
226 NS_IMPL_ISUPPORTS(nsPrefetchNode,
227 nsIRequestObserver,
228 nsIStreamListener,
229 nsIInterfaceRequestor,
230 nsIChannelEventSink,
231 nsIRedirectResultListener)
232
233 //-----------------------------------------------------------------------------
234 // nsPrefetchNode::nsIStreamListener
235 //-----------------------------------------------------------------------------
236
237 NS_IMETHODIMP
238 nsPrefetchNode::OnStartRequest(nsIRequest *aRequest,
239 nsISupports *aContext)
240 {
241 nsresult rv;
242
243 nsCOMPtr<nsICachingChannel> cachingChannel =
244 do_QueryInterface(aRequest, &rv);
245 if (NS_FAILED(rv)) return rv;
246
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 }
254
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
263
264 nsCOMPtr<nsICacheEntry> entryInfo =
265 do_QueryInterface(cacheToken, &rv);
266 if (NS_FAILED(rv)) return rv;
267
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 }
276
277 return NS_OK;
278 }
279
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 }
293
294
295 NS_IMETHODIMP
296 nsPrefetchNode::OnStopRequest(nsIRequest *aRequest,
297 nsISupports *aContext,
298 nsresult aStatus)
299 {
300 LOG(("done prefetching [status=%x]\n", aStatus));
301
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 }
308
309 mService->NotifyLoadCompleted(this);
310 mService->ProcessNextURI();
311 return NS_OK;
312 }
313
314 //-----------------------------------------------------------------------------
315 // nsPrefetchNode::nsIInterfaceRequestor
316 //-----------------------------------------------------------------------------
317
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 }
326
327 if (aIID.Equals(NS_GET_IID(nsIRedirectResultListener))) {
328 NS_ADDREF_THIS();
329 *aResult = static_cast<nsIRedirectResultListener *>(this);
330 return NS_OK;
331 }
332
333 return NS_ERROR_NO_INTERFACE;
334 }
335
336 //-----------------------------------------------------------------------------
337 // nsPrefetchNode::nsIChannelEventSink
338 //-----------------------------------------------------------------------------
339
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;
350
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 }
360
361 // HTTP request headers are not automatically forwarded to the new channel.
362 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aNewChannel);
363 NS_ENSURE_STATE(httpChannel);
364
365 httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("X-Moz"),
366 NS_LITERAL_CSTRING("prefetch"),
367 false);
368
369 // Assign to mChannel after we get notification about success of the
370 // redirect in OnRedirectResult.
371 mRedirectChannel = aNewChannel;
372
373 callback->OnRedirectVerifyCallback(NS_OK);
374 return NS_OK;
375 }
376
377 //-----------------------------------------------------------------------------
378 // nsPrefetchNode::nsIRedirectResultListener
379 //-----------------------------------------------------------------------------
380
381 NS_IMETHODIMP
382 nsPrefetchNode::OnRedirectResult(bool proceeding)
383 {
384 if (proceeding && mRedirectChannel)
385 mChannel = mRedirectChannel;
386
387 mRedirectChannel = nullptr;
388
389 return NS_OK;
390 }
391
392 //-----------------------------------------------------------------------------
393 // nsPrefetchService <public>
394 //-----------------------------------------------------------------------------
395
396 nsPrefetchService::nsPrefetchService()
397 : mQueueHead(nullptr)
398 , mQueueTail(nullptr)
399 , mStopCount(0)
400 , mHaveProcessed(false)
401 , mDisabled(true)
402 {
403 }
404
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 }
412
413 nsresult
414 nsPrefetchService::Init()
415 {
416 #if defined(PR_LOGGING)
417 if (!gPrefetchLog)
418 gPrefetchLog = PR_NewLogModule("nsPrefetch");
419 #endif
420
421 nsresult rv;
422
423 // read prefs and hook up pref observer
424 mDisabled = !Preferences::GetBool(PREFETCH_PREF, !mDisabled);
425 Preferences::AddWeakObserver(this, PREFETCH_PREF);
426
427 // Observe xpcom-shutdown event
428 nsCOMPtr<nsIObserverService> observerService =
429 mozilla::services::GetObserverService();
430 if (!observerService)
431 return NS_ERROR_FAILURE;
432
433 rv = observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, true);
434 NS_ENSURE_SUCCESS(rv, rv);
435
436 if (!mDisabled)
437 AddProgressListener();
438
439 return NS_OK;
440 }
441
442 void
443 nsPrefetchService::ProcessNextURI()
444 {
445 nsresult rv;
446 nsCOMPtr<nsIURI> uri, referrer;
447
448 mCurrentNode = nullptr;
449
450 do {
451 rv = DequeueNode(getter_AddRefs(mCurrentNode));
452
453 if (NS_FAILED(rv)) break;
454
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
462
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 }
471
472 void
473 nsPrefetchService::NotifyLoadRequested(nsPrefetchNode *node)
474 {
475 nsCOMPtr<nsIObserverService> observerService =
476 mozilla::services::GetObserverService();
477 if (!observerService)
478 return;
479
480 observerService->NotifyObservers(static_cast<nsIStreamListener*>(node),
481 "prefetch-load-requested", nullptr);
482 }
483
484 void
485 nsPrefetchService::NotifyLoadCompleted(nsPrefetchNode *node)
486 {
487 nsCOMPtr<nsIObserverService> observerService =
488 mozilla::services::GetObserverService();
489 if (!observerService)
490 return;
491
492 observerService->NotifyObservers(static_cast<nsIStreamListener*>(node),
493 "prefetch-load-completed", nullptr);
494 }
495
496 //-----------------------------------------------------------------------------
497 // nsPrefetchService <private>
498 //-----------------------------------------------------------------------------
499
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 }
509
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 }
519
520 nsresult
521 nsPrefetchService::EnqueueNode(nsPrefetchNode *aNode)
522 {
523 NS_ADDREF(aNode);
524
525 if (!mQueueTail) {
526 mQueueHead = aNode;
527 mQueueTail = aNode;
528 }
529 else {
530 mQueueTail->mNext = aNode;
531 mQueueTail = aNode;
532 }
533
534 return NS_OK;
535 }
536
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;
547
548 NS_ADDREF(*aNode = node);
549
550 return EnqueueNode(node);
551 }
552
553 nsresult
554 nsPrefetchService::DequeueNode(nsPrefetchNode **node)
555 {
556 if (!mQueueHead)
557 return NS_ERROR_NOT_AVAILABLE;
558
559 // give the ref to the caller
560 *node = mQueueHead;
561 mQueueHead = mQueueHead->mNext;
562 (*node)->mNext = nullptr;
563
564 if (!mQueueHead)
565 mQueueTail = nullptr;
566
567 return NS_OK;
568 }
569
570 void
571 nsPrefetchService::EmptyQueue()
572 {
573 do {
574 nsRefPtr<nsPrefetchNode> node;
575 DequeueNode(getter_AddRefs(node));
576 } while (mQueueHead);
577 }
578
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--;
589
590 LOG(("StartPrefetching [stopcount=%d]\n", mStopCount));
591
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 }
600
601 void
602 nsPrefetchService::StopPrefetching()
603 {
604 mStopCount++;
605
606 LOG(("StopPrefetching [stopcount=%d]\n", mStopCount));
607
608 // only kill the prefetch queue if we've actually started prefetching.
609 if (!mCurrentNode)
610 return;
611
612 mCurrentNode->CancelChannel(NS_BINDING_ABORTED);
613 mCurrentNode = nullptr;
614 EmptyQueue();
615 }
616
617 //-----------------------------------------------------------------------------
618 // nsPrefetchService::nsISupports
619 //-----------------------------------------------------------------------------
620
621 NS_IMPL_ISUPPORTS(nsPrefetchService,
622 nsIPrefetchService,
623 nsIWebProgressListener,
624 nsIObserver,
625 nsISupportsWeakReference)
626
627 //-----------------------------------------------------------------------------
628 // nsPrefetchService::nsIPrefetchService
629 //-----------------------------------------------------------------------------
630
631 nsresult
632 nsPrefetchService::Prefetch(nsIURI *aURI,
633 nsIURI *aReferrerURI,
634 nsIDOMNode *aSource,
635 bool aExplicit)
636 {
637 nsresult rv;
638
639 NS_ENSURE_ARG_POINTER(aURI);
640 NS_ENSURE_ARG_POINTER(aReferrerURI);
641
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
649
650 if (mDisabled) {
651 LOG(("rejected: prefetch service is disabled\n"));
652 return NS_ERROR_ABORT;
653 }
654
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 }
675
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 }
687
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 }
700
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 }
711
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 }
723
724 nsRefPtr<nsPrefetchNode> enqueuedNode;
725 rv = EnqueueURI(aURI, aReferrerURI, aSource,
726 getter_AddRefs(enqueuedNode));
727 NS_ENSURE_SUCCESS(rv, rv);
728
729 NotifyLoadRequested(enqueuedNode);
730
731 // if there are no pages loading, kick off the request immediately
732 if (mStopCount == 0 && mHaveProcessed)
733 ProcessNextURI();
734
735 return NS_OK;
736 }
737
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 }
746
747 NS_IMETHODIMP
748 nsPrefetchService::EnumerateQueue(nsISimpleEnumerator **aEnumerator)
749 {
750 *aEnumerator = new nsPrefetchQueueEnumerator(this);
751 if (!*aEnumerator) return NS_ERROR_OUT_OF_MEMORY;
752
753 NS_ADDREF(*aEnumerator);
754
755 return NS_OK;
756 }
757
758 //-----------------------------------------------------------------------------
759 // nsPrefetchService::nsIWebProgressListener
760 //-----------------------------------------------------------------------------
761
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 }
773
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 }
786
787 return NS_OK;
788 }
789
790
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 }
800
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 }
810
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 }
819
820 //-----------------------------------------------------------------------------
821 // nsPrefetchService::nsIObserver
822 //-----------------------------------------------------------------------------
823
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));
830
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 }
854
855 return NS_OK;
856 }
857
858 // vim: ts=4 sw=4 expandtab

mercurial