Add copy of .ttf font with .eot extension for testing
[wine-gecko.git] / uriloader / prefetch / nsPrefetchService.cpp
blob764bde814eb11de42315bf6c77e62325706f7a45
1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
12 * License.
14 * The Original Code is Mozilla.
16 * The Initial Developer of the Original Code is
17 * Netscape Communications Corporation.
18 * Portions created by the Initial Developer are Copyright (C) 2002
19 * the Initial Developer. All Rights Reserved.
21 * Contributor(s):
22 * Darin Fisher <darin@netscape.com> (original author)
24 * Alternatively, the contents of this file may be used under the terms of
25 * either the GNU General Public License Version 2 or later (the "GPL"), or
26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
38 #include "nsPrefetchService.h"
39 #include "nsICacheSession.h"
40 #include "nsICacheService.h"
41 #include "nsIServiceManager.h"
42 #include "nsICategoryManager.h"
43 #include "nsIObserverService.h"
44 #include "nsIPrefService.h"
45 #include "nsIPrefBranch2.h"
46 #include "nsIDocCharset.h"
47 #include "nsIWebProgress.h"
48 #include "nsCURILoader.h"
49 #include "nsICachingChannel.h"
50 #include "nsICacheVisitor.h"
51 #include "nsIHttpChannel.h"
52 #include "nsIURL.h"
53 #include "nsISimpleEnumerator.h"
54 #include "nsNetUtil.h"
55 #include "nsString.h"
56 #include "nsXPIDLString.h"
57 #include "nsReadableUtils.h"
58 #include "nsStreamUtils.h"
59 #include "nsAutoPtr.h"
60 #include "prtime.h"
61 #include "prlog.h"
62 #include "plstr.h"
64 #if defined(PR_LOGGING)
66 // To enable logging (see prlog.h for full details):
68 // set NSPR_LOG_MODULES=nsPrefetch:5
69 // set NSPR_LOG_FILE=prefetch.log
71 // this enables PR_LOG_ALWAYS level information and places all output in
72 // the file http.log
74 static PRLogModuleInfo *gPrefetchLog;
75 #endif
76 #define LOG(args) PR_LOG(gPrefetchLog, 4, args)
77 #define LOG_ENABLED() PR_LOG_TEST(gPrefetchLog, 4)
79 #define PREFETCH_PREF "network.prefetch-next"
81 //-----------------------------------------------------------------------------
82 // helpers
83 //-----------------------------------------------------------------------------
85 static inline PRUint32
86 PRTimeToSeconds(PRTime t_usec)
88 PRTime usec_per_sec;
89 PRUint32 t_sec;
90 LL_I2L(usec_per_sec, PR_USEC_PER_SEC);
91 LL_DIV(t_usec, t_usec, usec_per_sec);
92 LL_L2I(t_sec, t_usec);
93 return t_sec;
96 #define NowInSeconds() PRTimeToSeconds(PR_Now())
98 //-----------------------------------------------------------------------------
99 // nsPrefetchQueueEnumerator
100 //-----------------------------------------------------------------------------
101 class nsPrefetchQueueEnumerator : public nsISimpleEnumerator
103 public:
104 NS_DECL_ISUPPORTS
105 NS_DECL_NSISIMPLEENUMERATOR
106 nsPrefetchQueueEnumerator(nsPrefetchService *aService);
107 ~nsPrefetchQueueEnumerator();
109 private:
110 void Increment();
112 nsRefPtr<nsPrefetchService> mService;
113 nsRefPtr<nsPrefetchNode> mCurrent;
114 PRBool mStarted;
117 //-----------------------------------------------------------------------------
118 // nsPrefetchQueueEnumerator <public>
119 //-----------------------------------------------------------------------------
120 nsPrefetchQueueEnumerator::nsPrefetchQueueEnumerator(nsPrefetchService *aService)
121 : mService(aService)
122 , mStarted(PR_FALSE)
124 Increment();
127 nsPrefetchQueueEnumerator::~nsPrefetchQueueEnumerator()
131 //-----------------------------------------------------------------------------
132 // nsPrefetchQueueEnumerator::nsISimpleEnumerator
133 //-----------------------------------------------------------------------------
134 NS_IMETHODIMP
135 nsPrefetchQueueEnumerator::HasMoreElements(PRBool *aHasMore)
137 *aHasMore = (mCurrent != nsnull);
138 return NS_OK;
141 NS_IMETHODIMP
142 nsPrefetchQueueEnumerator::GetNext(nsISupports **aItem)
144 if (!mCurrent) return NS_ERROR_FAILURE;
146 NS_ADDREF(*aItem = static_cast<nsIDOMLoadStatus*>(mCurrent.get()));
148 Increment();
150 return NS_OK;
153 //-----------------------------------------------------------------------------
154 // nsPrefetchQueueEnumerator <private>
155 //-----------------------------------------------------------------------------
157 void
158 nsPrefetchQueueEnumerator::Increment()
160 do {
161 if (!mStarted) {
162 // If the service is currently serving a request, it won't be
163 // in the pending queue, so we return it first. If it isn't,
164 // we'll just start with the pending queue.
165 mStarted = PR_TRUE;
166 mCurrent = mService->GetCurrentNode();
167 if (!mCurrent)
168 mCurrent = mService->GetQueueHead();
170 else if (mCurrent) {
171 if (mCurrent == mService->GetCurrentNode()) {
172 // If we just returned the node being processed by the service,
173 // start with the pending queue
174 mCurrent = mService->GetQueueHead();
176 else {
177 // Otherwise just advance to the next item in the queue
178 mCurrent = mCurrent->mNext;
181 } while (mCurrent);
184 //-----------------------------------------------------------------------------
185 // nsPrefetchQueueEnumerator::nsISupports
186 //-----------------------------------------------------------------------------
188 NS_IMPL_ISUPPORTS1(nsPrefetchQueueEnumerator, nsISimpleEnumerator)
190 //-----------------------------------------------------------------------------
191 // nsPrefetchNode <public>
192 //-----------------------------------------------------------------------------
194 nsPrefetchNode::nsPrefetchNode(nsPrefetchService *aService,
195 nsIURI *aURI,
196 nsIURI *aReferrerURI,
197 nsIDOMNode *aSource)
198 : mNext(nsnull)
199 , mURI(aURI)
200 , mReferrerURI(aReferrerURI)
201 , mService(aService)
202 , mChannel(nsnull)
203 , mState(nsIDOMLoadStatus::UNINITIALIZED)
204 , mBytesRead(0)
206 mSource = do_GetWeakReference(aSource);
209 nsresult
210 nsPrefetchNode::OpenChannel()
212 nsresult rv = NS_NewChannel(getter_AddRefs(mChannel),
213 mURI,
214 nsnull, nsnull, this,
215 nsIRequest::LOAD_BACKGROUND |
216 nsICachingChannel::LOAD_ONLY_IF_MODIFIED);
217 NS_ENSURE_SUCCESS(rv, rv);
219 // configure HTTP specific stuff
220 nsCOMPtr<nsIHttpChannel> httpChannel =
221 do_QueryInterface(mChannel);
222 if (httpChannel) {
223 httpChannel->SetReferrer(mReferrerURI);
224 httpChannel->SetRequestHeader(
225 NS_LITERAL_CSTRING("X-Moz"),
226 NS_LITERAL_CSTRING("prefetch"),
227 PR_FALSE);
230 rv = mChannel->AsyncOpen(this, nsnull);
231 NS_ENSURE_SUCCESS(rv, rv);
233 mState = nsIDOMLoadStatus::REQUESTED;
235 return NS_OK;
238 nsresult
239 nsPrefetchNode::CancelChannel(nsresult error)
241 mChannel->Cancel(error);
242 mChannel = nsnull;
244 mState = nsIDOMLoadStatus::UNINITIALIZED;
246 return NS_OK;
249 //-----------------------------------------------------------------------------
250 // nsPrefetchNode::nsISupports
251 //-----------------------------------------------------------------------------
253 NS_IMPL_ISUPPORTS5(nsPrefetchNode,
254 nsIDOMLoadStatus,
255 nsIRequestObserver,
256 nsIStreamListener,
257 nsIInterfaceRequestor,
258 nsIChannelEventSink)
260 //-----------------------------------------------------------------------------
261 // nsPrefetchNode::nsIStreamListener
262 //-----------------------------------------------------------------------------
264 NS_IMETHODIMP
265 nsPrefetchNode::OnStartRequest(nsIRequest *aRequest,
266 nsISupports *aContext)
268 nsresult rv;
270 nsCOMPtr<nsICachingChannel> cachingChannel =
271 do_QueryInterface(aRequest, &rv);
272 if (NS_FAILED(rv)) return rv;
274 // no need to prefetch a document that is already in the cache
275 PRBool fromCache;
276 if (NS_SUCCEEDED(cachingChannel->IsFromCache(&fromCache)) &&
277 fromCache) {
278 LOG(("document is already in the cache; canceling prefetch\n"));
279 return NS_BINDING_ABORTED;
283 // no need to prefetch a document that must be requested fresh each
284 // and every time.
286 nsCOMPtr<nsISupports> cacheToken;
287 cachingChannel->GetCacheToken(getter_AddRefs(cacheToken));
288 if (!cacheToken)
289 return NS_ERROR_ABORT; // bail, no cache entry
291 nsCOMPtr<nsICacheEntryInfo> entryInfo =
292 do_QueryInterface(cacheToken, &rv);
293 if (NS_FAILED(rv)) return rv;
295 PRUint32 expTime;
296 if (NS_SUCCEEDED(entryInfo->GetExpirationTime(&expTime))) {
297 if (NowInSeconds() >= expTime) {
298 LOG(("document cannot be reused from cache; "
299 "canceling prefetch\n"));
300 return NS_BINDING_ABORTED;
304 mState = nsIDOMLoadStatus::RECEIVING;
306 return NS_OK;
309 NS_IMETHODIMP
310 nsPrefetchNode::OnDataAvailable(nsIRequest *aRequest,
311 nsISupports *aContext,
312 nsIInputStream *aStream,
313 PRUint32 aOffset,
314 PRUint32 aCount)
316 PRUint32 bytesRead = 0;
317 aStream->ReadSegments(NS_DiscardSegment, nsnull, aCount, &bytesRead);
318 mBytesRead += bytesRead;
319 LOG(("prefetched %u bytes [offset=%u]\n", bytesRead, aOffset));
320 return NS_OK;
324 NS_IMETHODIMP
325 nsPrefetchNode::OnStopRequest(nsIRequest *aRequest,
326 nsISupports *aContext,
327 nsresult aStatus)
329 LOG(("done prefetching [status=%x]\n", aStatus));
331 mState = nsIDOMLoadStatus::LOADED;
333 if (mBytesRead == 0 && aStatus == NS_OK) {
334 // we didn't need to read (because LOAD_ONLY_IF_MODIFIED was
335 // specified), but the object should report loadedSize as if it
336 // did.
337 mChannel->GetContentLength(&mBytesRead);
340 mService->NotifyLoadCompleted(this);
341 mService->ProcessNextURI();
342 return NS_OK;
345 //-----------------------------------------------------------------------------
346 // nsPrefetchNode::nsIInterfaceRequestor
347 //-----------------------------------------------------------------------------
349 NS_IMETHODIMP
350 nsPrefetchNode::GetInterface(const nsIID &aIID, void **aResult)
352 if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
353 NS_ADDREF_THIS();
354 *aResult = static_cast<nsIChannelEventSink *>(this);
355 return NS_OK;
358 return NS_ERROR_NO_INTERFACE;
361 //-----------------------------------------------------------------------------
362 // nsPrefetchNode::nsIChannelEventSink
363 //-----------------------------------------------------------------------------
365 NS_IMETHODIMP
366 nsPrefetchNode::OnChannelRedirect(nsIChannel *aOldChannel,
367 nsIChannel *aNewChannel,
368 PRUint32 aFlags)
370 nsCOMPtr<nsIURI> newURI;
371 nsresult rv = aNewChannel->GetURI(getter_AddRefs(newURI));
372 if (NS_FAILED(rv))
373 return rv;
375 nsCOMPtr<nsICachingChannel> oldCachingChannel =
376 do_QueryInterface(aOldChannel);
378 PRBool match;
379 rv = newURI->SchemeIs("http", &match);
380 if (NS_FAILED(rv) || !match) {
381 rv = newURI->SchemeIs("https", &match);
382 if (NS_FAILED(rv) || !match) {
383 LOG(("rejected: URL is not of type http/https\n"));
384 return NS_ERROR_ABORT;
388 // HTTP request headers are not automatically forwarded to the new channel.
389 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aNewChannel);
390 NS_ENSURE_STATE(httpChannel);
392 httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("X-Moz"),
393 NS_LITERAL_CSTRING("prefetch"),
394 PR_FALSE);
396 mChannel = aNewChannel;
398 return NS_OK;
402 //-----------------------------------------------------------------------------
403 // nsPrefetchService <public>
404 //-----------------------------------------------------------------------------
406 nsPrefetchService::nsPrefetchService()
407 : mQueueHead(nsnull)
408 , mQueueTail(nsnull)
409 , mStopCount(0)
410 , mHaveProcessed(PR_FALSE)
411 , mDisabled(PR_TRUE)
415 nsPrefetchService::~nsPrefetchService()
417 // cannot reach destructor if prefetch in progress (listener owns reference
418 // to this service)
419 EmptyQueue();
422 nsresult
423 nsPrefetchService::Init()
425 #if defined(PR_LOGGING)
426 if (!gPrefetchLog)
427 gPrefetchLog = PR_NewLogModule("nsPrefetch");
428 #endif
430 nsresult rv;
432 // read prefs and hook up pref observer
433 nsCOMPtr<nsIPrefBranch2> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
434 if (NS_SUCCEEDED(rv)) {
435 PRBool enabled;
436 rv = prefs->GetBoolPref(PREFETCH_PREF, &enabled);
437 if (NS_SUCCEEDED(rv) && enabled)
438 mDisabled = PR_FALSE;
440 prefs->AddObserver(PREFETCH_PREF, this, PR_TRUE);
443 // Observe xpcom-shutdown event
444 nsCOMPtr<nsIObserverService> observerServ(
445 do_GetService("@mozilla.org/observer-service;1", &rv));
446 if (NS_FAILED(rv)) return rv;
448 rv = observerServ->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, PR_TRUE);
449 if (NS_FAILED(rv)) return rv;
451 if (!mDisabled)
452 AddProgressListener();
454 return NS_OK;
457 void
458 nsPrefetchService::ProcessNextURI()
460 nsresult rv;
461 nsCOMPtr<nsIURI> uri, referrer;
463 mCurrentNode = nsnull;
465 do {
466 rv = DequeueNode(getter_AddRefs(mCurrentNode));
468 if (NS_FAILED(rv)) break;
470 #if defined(PR_LOGGING)
471 if (LOG_ENABLED()) {
472 nsCAutoString spec;
473 mCurrentNode->mURI->GetSpec(spec);
474 LOG(("ProcessNextURI [%s]\n", spec.get()));
476 #endif
479 // if opening the channel fails, then just skip to the next uri
481 rv = mCurrentNode->OpenChannel();
483 while (NS_FAILED(rv));
486 void
487 nsPrefetchService::NotifyLoadRequested(nsPrefetchNode *node)
489 nsresult rv;
491 nsCOMPtr<nsIObserverService> observerService =
492 do_GetService("@mozilla.org/observer-service;1", &rv);
493 if (NS_FAILED(rv)) return;
495 observerService->NotifyObservers(static_cast<nsIDOMLoadStatus*>(node),
496 "prefetch-load-requested", nsnull);
499 void
500 nsPrefetchService::NotifyLoadCompleted(nsPrefetchNode *node)
502 nsresult rv;
504 nsCOMPtr<nsIObserverService> observerService =
505 do_GetService("@mozilla.org/observer-service;1", &rv);
506 if (NS_FAILED(rv)) return;
508 observerService->NotifyObservers(static_cast<nsIDOMLoadStatus*>(node),
509 "prefetch-load-completed", nsnull);
512 //-----------------------------------------------------------------------------
513 // nsPrefetchService <private>
514 //-----------------------------------------------------------------------------
516 void
517 nsPrefetchService::AddProgressListener()
519 // Register as an observer for the document loader
520 nsCOMPtr<nsIWebProgress> progress =
521 do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID);
522 if (progress)
523 progress->AddProgressListener(this, nsIWebProgress::NOTIFY_STATE_DOCUMENT);
526 void
527 nsPrefetchService::RemoveProgressListener()
529 // Register as an observer for the document loader
530 nsCOMPtr<nsIWebProgress> progress =
531 do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID);
532 if (progress)
533 progress->RemoveProgressListener(this);
536 nsresult
537 nsPrefetchService::EnqueueNode(nsPrefetchNode *aNode)
539 NS_ADDREF(aNode);
541 if (!mQueueTail) {
542 mQueueHead = aNode;
543 mQueueTail = aNode;
545 else {
546 mQueueTail->mNext = aNode;
547 mQueueTail = aNode;
550 return NS_OK;
553 nsresult
554 nsPrefetchService::EnqueueURI(nsIURI *aURI,
555 nsIURI *aReferrerURI,
556 nsIDOMNode *aSource,
557 nsPrefetchNode **aNode)
559 nsPrefetchNode *node = new nsPrefetchNode(this, aURI, aReferrerURI,
560 aSource);
561 if (!node)
562 return NS_ERROR_OUT_OF_MEMORY;
564 NS_ADDREF(*aNode = node);
566 return EnqueueNode(node);
569 nsresult
570 nsPrefetchService::DequeueNode(nsPrefetchNode **node)
572 if (!mQueueHead)
573 return NS_ERROR_NOT_AVAILABLE;
575 // give the ref to the caller
576 *node = mQueueHead;
577 mQueueHead = mQueueHead->mNext;
578 (*node)->mNext = nsnull;
580 if (!mQueueHead)
581 mQueueTail = nsnull;
583 return NS_OK;
586 void
587 nsPrefetchService::EmptyQueue()
589 nsPrefetchNode *prev = 0;
590 nsPrefetchNode *node = mQueueHead;
592 while (node) {
593 nsPrefetchNode *next = node->mNext;
594 if (prev)
595 prev->mNext = next;
596 else
597 mQueueHead = next;
598 NS_RELEASE(node);
600 node = next;
604 void
605 nsPrefetchService::StartPrefetching()
608 // at initialization time we might miss the first DOCUMENT START
609 // notification, so we have to be careful to avoid letting our
610 // stop count go negative.
612 if (mStopCount > 0)
613 mStopCount--;
615 LOG(("StartPrefetching [stopcount=%d]\n", mStopCount));
617 // only start prefetching after we've received enough DOCUMENT
618 // STOP notifications. we do this inorder to defer prefetching
619 // until after all sub-frames have finished loading.
620 if (mStopCount == 0 && !mCurrentNode) {
621 mHaveProcessed = PR_TRUE;
622 ProcessNextURI();
626 void
627 nsPrefetchService::StopPrefetching()
629 mStopCount++;
631 LOG(("StopPrefetching [stopcount=%d]\n", mStopCount));
633 // only kill the prefetch queue if we've actually started prefetching.
634 if (!mCurrentNode)
635 return;
637 mCurrentNode->CancelChannel(NS_BINDING_ABORTED);
638 mCurrentNode = nsnull;
639 EmptyQueue();
642 //-----------------------------------------------------------------------------
643 // nsPrefetchService::nsISupports
644 //-----------------------------------------------------------------------------
646 NS_IMPL_ISUPPORTS4(nsPrefetchService,
647 nsIPrefetchService,
648 nsIWebProgressListener,
649 nsIObserver,
650 nsISupportsWeakReference)
652 //-----------------------------------------------------------------------------
653 // nsPrefetchService::nsIPrefetchService
654 //-----------------------------------------------------------------------------
656 nsresult
657 nsPrefetchService::Prefetch(nsIURI *aURI,
658 nsIURI *aReferrerURI,
659 nsIDOMNode *aSource,
660 PRBool aExplicit)
662 nsresult rv;
664 NS_ENSURE_ARG_POINTER(aURI);
665 NS_ENSURE_ARG_POINTER(aReferrerURI);
667 #if defined(PR_LOGGING)
668 if (LOG_ENABLED()) {
669 nsCAutoString spec;
670 aURI->GetSpec(spec);
671 LOG(("PrefetchURI [%s]\n", spec.get()));
673 #endif
675 if (mDisabled) {
676 LOG(("rejected: prefetch service is disabled\n"));
677 return NS_ERROR_ABORT;
681 // XXX we should really be asking the protocol handler if it supports
682 // caching, so we can determine if there is any value to prefetching.
683 // for now, we'll only prefetch http links since we know that's the
684 // most common case. ignore https links since https content only goes
685 // into the memory cache.
687 // XXX we might want to either leverage nsIProtocolHandler::protocolFlags
688 // or possibly nsIRequest::loadFlags to determine if this URI should be
689 // prefetched.
691 PRBool match;
692 rv = aURI->SchemeIs("http", &match);
693 if (NS_FAILED(rv) || !match) {
694 rv = aURI->SchemeIs("https", &match);
695 if (NS_FAILED(rv) || !match) {
696 LOG(("rejected: URL is not of type http/https\n"));
697 return NS_ERROR_ABORT;
702 // the referrer URI must be http:
704 rv = aReferrerURI->SchemeIs("http", &match);
705 if (NS_FAILED(rv) || !match) {
706 rv = aReferrerURI->SchemeIs("https", &match);
707 if (NS_FAILED(rv) || !match) {
708 LOG(("rejected: referrer URL is neither http nor https\n"));
709 return NS_ERROR_ABORT;
713 // skip URLs that contain query strings, except URLs for which prefetching
714 // has been explicitly requested.
715 if (!aExplicit) {
716 nsCOMPtr<nsIURL> url(do_QueryInterface(aURI, &rv));
717 if (NS_FAILED(rv)) return rv;
718 nsCAutoString query;
719 rv = url->GetQuery(query);
720 if (NS_FAILED(rv) || !query.IsEmpty()) {
721 LOG(("rejected: URL has a query string\n"));
722 return NS_ERROR_ABORT;
727 // cancel if being prefetched
729 if (mCurrentNode) {
730 PRBool equals;
731 if (NS_SUCCEEDED(mCurrentNode->mURI->Equals(aURI, &equals)) && equals) {
732 LOG(("rejected: URL is already being prefetched\n"));
733 return NS_ERROR_ABORT;
738 // cancel if already on the prefetch queue
740 nsPrefetchNode *node = mQueueHead;
741 for (; node; node = node->mNext) {
742 PRBool equals;
743 if (NS_SUCCEEDED(node->mURI->Equals(aURI, &equals)) && equals) {
744 LOG(("rejected: URL is already on prefetch queue\n"));
745 return NS_ERROR_ABORT;
749 nsRefPtr<nsPrefetchNode> enqueuedNode;
750 rv = EnqueueURI(aURI, aReferrerURI, aSource,
751 getter_AddRefs(enqueuedNode));
752 NS_ENSURE_SUCCESS(rv, rv);
754 NotifyLoadRequested(enqueuedNode);
756 // if there are no pages loading, kick off the request immediately
757 if (mStopCount == 0 && mHaveProcessed)
758 ProcessNextURI();
760 return NS_OK;
763 NS_IMETHODIMP
764 nsPrefetchService::PrefetchURI(nsIURI *aURI,
765 nsIURI *aReferrerURI,
766 nsIDOMNode *aSource,
767 PRBool aExplicit)
769 return Prefetch(aURI, aReferrerURI, aSource, aExplicit);
772 NS_IMETHODIMP
773 nsPrefetchService::PrefetchURIForOfflineUse(nsIURI *aURI,
774 nsIURI *aReferrerURI,
775 nsIDOMNode *aSource,
776 PRBool aExplicit)
778 return NS_ERROR_NOT_IMPLEMENTED;
781 NS_IMETHODIMP
782 nsPrefetchService::EnumerateQueue(PRBool aIncludeNormalItems,
783 PRBool aIncludeOfflineItems,
784 nsISimpleEnumerator **aEnumerator)
786 NS_ENSURE_TRUE(aIncludeNormalItems && !aIncludeOfflineItems,
787 NS_ERROR_NOT_IMPLEMENTED);
789 *aEnumerator = new nsPrefetchQueueEnumerator(this);
790 if (!*aEnumerator) return NS_ERROR_OUT_OF_MEMORY;
792 NS_ADDREF(*aEnumerator);
794 return NS_OK;
797 //-----------------------------------------------------------------------------
798 // nsPrefetchNode::nsIDOMLoadStatus
799 //-----------------------------------------------------------------------------
800 NS_IMETHODIMP
801 nsPrefetchNode::GetSource(nsIDOMNode **aSource)
803 *aSource = nsnull;
804 nsCOMPtr<nsIDOMNode> source = do_QueryReferent(mSource);
805 if (source)
806 source.swap(*aSource);
808 return NS_OK;
811 NS_IMETHODIMP
812 nsPrefetchNode::GetUri(nsAString &aURI)
814 nsCAutoString spec;
815 nsresult rv = mURI->GetSpec(spec);
816 NS_ENSURE_SUCCESS(rv, rv);
818 CopyUTF8toUTF16(spec, aURI);
819 return NS_OK;
822 NS_IMETHODIMP
823 nsPrefetchNode::GetTotalSize(PRInt32 *aTotalSize)
825 if (mChannel) {
826 return mChannel->GetContentLength(aTotalSize);
829 *aTotalSize = -1;
830 return NS_OK;
833 NS_IMETHODIMP
834 nsPrefetchNode::GetLoadedSize(PRInt32 *aLoadedSize)
836 *aLoadedSize = mBytesRead;
837 return NS_OK;
840 NS_IMETHODIMP
841 nsPrefetchNode::GetReadyState(PRUint16 *aReadyState)
843 *aReadyState = mState;
844 return NS_OK;
847 NS_IMETHODIMP
848 nsPrefetchNode::GetStatus(PRUint16 *aStatus)
850 if (!mChannel) {
851 *aStatus = 0;
852 return NS_OK;
855 nsresult rv;
856 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel, &rv);
857 NS_ENSURE_SUCCESS(rv, rv);
859 PRUint32 httpStatus;
860 rv = httpChannel->GetResponseStatus(&httpStatus);
861 if (rv == NS_ERROR_NOT_AVAILABLE) {
862 // Someone's calling this before we got a response... Check our
863 // ReadyState. If we're at RECEIVING or LOADED, then this means the
864 // connection errored before we got any data; return a somewhat
865 // sensible error code in that case.
866 if (mState >= nsIDOMLoadStatus::RECEIVING) {
867 *aStatus = NS_ERROR_NOT_AVAILABLE;
868 return NS_OK;
871 *aStatus = 0;
872 return NS_OK;
875 NS_ENSURE_SUCCESS(rv, rv);
876 *aStatus = PRUint16(httpStatus);
877 return NS_OK;
880 //-----------------------------------------------------------------------------
881 // nsPrefetchService::nsIWebProgressListener
882 //-----------------------------------------------------------------------------
884 NS_IMETHODIMP
885 nsPrefetchService::OnProgressChange(nsIWebProgress *aProgress,
886 nsIRequest *aRequest,
887 PRInt32 curSelfProgress,
888 PRInt32 maxSelfProgress,
889 PRInt32 curTotalProgress,
890 PRInt32 maxTotalProgress)
892 NS_NOTREACHED("notification excluded in AddProgressListener(...)");
893 return NS_OK;
896 NS_IMETHODIMP
897 nsPrefetchService::OnStateChange(nsIWebProgress* aWebProgress,
898 nsIRequest *aRequest,
899 PRUint32 progressStateFlags,
900 nsresult aStatus)
902 if (progressStateFlags & STATE_IS_DOCUMENT) {
903 if (progressStateFlags & STATE_STOP)
904 StartPrefetching();
905 else if (progressStateFlags & STATE_START)
906 StopPrefetching();
909 return NS_OK;
913 NS_IMETHODIMP
914 nsPrefetchService::OnLocationChange(nsIWebProgress* aWebProgress,
915 nsIRequest* aRequest,
916 nsIURI *location)
918 NS_NOTREACHED("notification excluded in AddProgressListener(...)");
919 return NS_OK;
922 NS_IMETHODIMP
923 nsPrefetchService::OnStatusChange(nsIWebProgress* aWebProgress,
924 nsIRequest* aRequest,
925 nsresult aStatus,
926 const PRUnichar* aMessage)
928 NS_NOTREACHED("notification excluded in AddProgressListener(...)");
929 return NS_OK;
932 NS_IMETHODIMP
933 nsPrefetchService::OnSecurityChange(nsIWebProgress *aWebProgress,
934 nsIRequest *aRequest,
935 PRUint32 state)
937 NS_NOTREACHED("notification excluded in AddProgressListener(...)");
938 return NS_OK;
941 //-----------------------------------------------------------------------------
942 // nsPrefetchService::nsIObserver
943 //-----------------------------------------------------------------------------
945 NS_IMETHODIMP
946 nsPrefetchService::Observe(nsISupports *aSubject,
947 const char *aTopic,
948 const PRUnichar *aData)
950 LOG(("nsPrefetchService::Observe [topic=%s]\n", aTopic));
952 if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
953 StopPrefetching();
954 mDisabled = PR_TRUE;
956 else if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
957 nsCOMPtr<nsIPrefBranch> prefs(do_QueryInterface(aSubject));
958 PRBool enabled;
959 nsresult rv = prefs->GetBoolPref(PREFETCH_PREF, &enabled);
960 if (NS_SUCCEEDED(rv) && enabled) {
961 if (mDisabled) {
962 LOG(("enabling prefetching\n"));
963 mDisabled = PR_FALSE;
964 AddProgressListener();
967 else {
968 if (!mDisabled) {
969 LOG(("disabling prefetching\n"));
970 StopPrefetching();
971 mDisabled = PR_TRUE;
972 RemoveProgressListener();
977 return NS_OK;
980 // vim: ts=4 sw=4 expandtab