diff --git a/content/html/document/src/nsHTMLContentSink.cpp b/content/html/document/src/nsHTMLContentSink.cpp index d47a841e0f7b..212e62372a04 100644 --- a/content/html/document/src/nsHTMLContentSink.cpp +++ b/content/html/document/src/nsHTMLContentSink.cpp @@ -4929,6 +4929,36 @@ HTMLContentSink::ProcessStyleLink(nsIHTMLContent* aElement, void HTMLContentSink::PrefetchHref(const nsAString &aHref) { + // + // SECURITY CHECK: disable prefetching from mailnews! + // + // walk up the docshell tree to see if any containing + // docshell are of type MAIL. + // + nsCOMPtr docshell(do_QueryInterface(mWebShell)); + if (!docshell) + return; + nsCOMPtr treeItem, parentItem; + do { + PRUint32 appType; + nsresult rv = docshell->GetAppType(&appType); + if (NS_FAILED(rv) || appType == nsIDocShell::APP_TYPE_MAIL) + return; // do not prefetch from mailnews + if (treeItem = do_QueryInterface(docshell)) { + treeItem->GetParent(getter_AddRefs(parentItem)); + if (parentItem) { + treeItem = parentItem; + docshell = do_QueryInterface(treeItem); + if (!docshell) { + NS_ERROR("cannot get a docshell from a treeItem!"); + return; + } + } + } + } while (parentItem); + + // OK, we passed the security check... + nsCOMPtr prefetchService( do_GetService(NS_PREFETCHSERVICE_CONTRACTID)); if (prefetchService) { @@ -4941,7 +4971,7 @@ HTMLContentSink::PrefetchHref(const nsAString &aHref) : NS_LossyConvertUCS2toASCII(charset).get(), mDocumentBaseURL); if (uri) - prefetchService->PrefetchURI(uri); + prefetchService->PrefetchURI(uri, mDocumentURI); } } diff --git a/uriloader/prefetch/nsIPrefetchService.idl b/uriloader/prefetch/nsIPrefetchService.idl index b2e027bee9b5..36e9c56321ea 100644 --- a/uriloader/prefetch/nsIPrefetchService.idl +++ b/uriloader/prefetch/nsIPrefetchService.idl @@ -44,8 +44,11 @@ interface nsIPrefetchService : nsISupports { /** * Enqueue a request to prefetch the specified URI. + * + * @param aURI the URI of the document to prefetch + * @param aReferrerURI the URI of the referring page */ - void prefetchURI(in nsIURI aURI); + void prefetchURI(in nsIURI aURI, in nsIURI aReferrerURI); // XXX do we need a way to cancel prefetch requests? }; diff --git a/uriloader/prefetch/nsPrefetchService.cpp b/uriloader/prefetch/nsPrefetchService.cpp index 0316559bf6d5..7679b51e7184 100644 --- a/uriloader/prefetch/nsPrefetchService.cpp +++ b/uriloader/prefetch/nsPrefetchService.cpp @@ -139,30 +139,36 @@ NS_IMETHODIMP nsPrefetchListener::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext) { - nsCOMPtr cachingChannel(do_QueryInterface(aRequest)); - if (cachingChannel) { - // no need to prefetch a document that is already in the cache - PRBool fromCache; - if (NS_SUCCEEDED(cachingChannel->IsFromCache(&fromCache)) && fromCache) { - LOG(("document is already in the cache; canceling prefetch\n")); + nsresult rv; + + nsCOMPtr cachingChannel(do_QueryInterface(aRequest, &rv)); + if (NS_FAILED(rv)) return rv; + + // no need to prefetch a document that is already in the cache + PRBool fromCache; + if (NS_SUCCEEDED(cachingChannel->IsFromCache(&fromCache)) && fromCache) { + LOG(("document is already in the cache; canceling prefetch\n")); + return NS_BINDING_ABORTED; + } + + // + // no need to prefetch a document that must be requested fresh each + // and every time. + // + nsCOMPtr cacheToken; + cachingChannel->GetCacheToken(getter_AddRefs(cacheToken)); + if (!cacheToken) + return NS_ERROR_ABORT; // bail, no cache entry + + nsCOMPtr entryInfo(do_QueryInterface(cacheToken, &rv)); + if (NS_FAILED(rv)) return rv; + + PRUint32 expTime; + if (NS_SUCCEEDED(entryInfo->GetExpirationTime(&expTime))) { + if (NowInSeconds() >= expTime) { + LOG(("document cannot be reused from cache; canceling prefetch\n")); return NS_BINDING_ABORTED; } - // no need to prefetch a document that must be requested fresh each - // and every time. - nsCOMPtr cacheToken; - cachingChannel->GetCacheToken(getter_AddRefs(cacheToken)); - if (cacheToken) { - nsCOMPtr entryInfo(do_QueryInterface(cacheToken)); - if (entryInfo) { - PRUint32 expTime; - if (NS_SUCCEEDED(entryInfo->GetExpirationTime(&expTime))) { - if (NowInSeconds() >= expTime) { - LOG(("document cannot be reused from cache; canceling prefetch\n")); - return NS_BINDING_ABORTED; - } - } - } - } } return NS_OK; } @@ -256,7 +262,7 @@ void nsPrefetchService::ProcessNextURI() { nsresult rv; - nsCOMPtr uri; + nsCOMPtr uri, referrer; mCurrentChannel = nsnull; @@ -264,7 +270,7 @@ nsPrefetchService::ProcessNextURI() if (!listener) return; do { - rv = DequeueURI(getter_AddRefs(uri)); + rv = DequeueURI(getter_AddRefs(uri), getter_AddRefs(referrer)); if (NS_FAILED(rv)) break; #if defined(PR_LOGGING) @@ -282,6 +288,14 @@ nsPrefetchService::ProcessNextURI() nsnull, nsIRequest::LOAD_BACKGROUND); if (NS_FAILED(rv)) continue; + // configure HTTP specific stuff + nsCOMPtr httpChannel(do_QueryInterface(mCurrentChannel)); + if (httpChannel) { + httpChannel->SetReferrer(referrer); + httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("X-Moz"), + NS_LITERAL_CSTRING("prefetch")); + } + rv = mCurrentChannel->AsyncOpen(listener, nsnull); } while (NS_FAILED(rv)); @@ -292,9 +306,9 @@ nsPrefetchService::ProcessNextURI() //----------------------------------------------------------------------------- nsresult -nsPrefetchService::EnqueueURI(nsIURI *aURI) +nsPrefetchService::EnqueueURI(nsIURI *aURI, nsIURI *aReferrerURI) { - nsPrefetchNode *node = new nsPrefetchNode(aURI); + nsPrefetchNode *node = new nsPrefetchNode(aURI, aReferrerURI); if (!node) return NS_ERROR_OUT_OF_MEMORY; @@ -311,13 +325,14 @@ nsPrefetchService::EnqueueURI(nsIURI *aURI) } nsresult -nsPrefetchService::DequeueURI(nsIURI **aURI) +nsPrefetchService::DequeueURI(nsIURI **aURI, nsIURI **aReferrerURI) { if (!mQueueHead) return NS_ERROR_NOT_AVAILABLE; // remove from the head NS_ADDREF(*aURI = mQueueHead->mURI); + NS_ADDREF(*aReferrerURI = mQueueHead->mReferrerURI); nsPrefetchNode *node = mQueueHead; mQueueHead = mQueueHead->mNext; @@ -333,10 +348,11 @@ void nsPrefetchService::EmptyQueue() { nsresult rv; - nsCOMPtr uri; + nsCOMPtr uri, referrer; do { - rv = DequeueURI(getter_AddRefs(uri)); + rv = DequeueURI(getter_AddRefs(uri), + getter_AddRefs(referrer)); } while (NS_SUCCEEDED(rv)); } @@ -392,10 +408,13 @@ NS_IMPL_ISUPPORTS4(nsPrefetchService, //----------------------------------------------------------------------------- NS_IMETHODIMP -nsPrefetchService::PrefetchURI(nsIURI *aURI) +nsPrefetchService::PrefetchURI(nsIURI *aURI, nsIURI *aReferrerURI) { nsresult rv; + NS_ENSURE_ARG_POINTER(aURI); + NS_ENSURE_ARG_POINTER(aReferrerURI); + #if defined(PR_LOGGING) if (LOG_ENABLED()) { nsCAutoString spec; @@ -420,13 +439,22 @@ nsPrefetchService::PrefetchURI(nsIURI *aURI) // or possibly nsIRequest::loadFlags to determine if this URI should be // prefetched. // - PRBool isHttp; - rv = aURI->SchemeIs("http", &isHttp); - if (NS_FAILED(rv) || !isHttp) { + PRBool match; + rv = aURI->SchemeIs("http", &match); + if (NS_FAILED(rv) || !match) { LOG(("rejected: URL is not of type http\n")); return NS_ERROR_ABORT; } + // + // the referrer URI must be http: + // + rv = aReferrerURI->SchemeIs("http", &match); + if (NS_FAILED(rv) || !match) { + LOG(("rejected: referrer URL is not of type http\n")); + return NS_ERROR_ABORT; + } + // // skip URLs that contain query strings. these URLs are likely to result // in documents that have zero freshness lifetimes, which we'd stop @@ -469,7 +497,7 @@ nsPrefetchService::PrefetchURI(nsIURI *aURI) } } - return EnqueueURI(aURI); + return EnqueueURI(aURI, aReferrerURI); } //----------------------------------------------------------------------------- diff --git a/uriloader/prefetch/nsPrefetchService.h b/uriloader/prefetch/nsPrefetchService.h index 0802aec29c8c..acdba2b7d21f 100644 --- a/uriloader/prefetch/nsPrefetchService.h +++ b/uriloader/prefetch/nsPrefetchService.h @@ -75,8 +75,8 @@ public: private: - nsresult EnqueueURI(nsIURI *aURI); - nsresult DequeueURI(nsIURI **aURI); + nsresult EnqueueURI(nsIURI *aURI, nsIURI *aReferrerURI); + nsresult DequeueURI(nsIURI **aURI, nsIURI **aReferrerURI); void EmptyQueue(); void StartPrefetching(); void StopPrefetching(); @@ -116,10 +116,16 @@ private: class nsPrefetchNode { public: - nsPrefetchNode(nsIURI *aURI) : mNext(nsnull), mURI(aURI) { } + nsPrefetchNode(nsIURI *aURI, + nsIURI *aReferrerURI) + : mNext(nsnull) + , mURI(aURI) + , mReferrerURI(aReferrerURI) + { } nsPrefetchNode *mNext; nsCOMPtr mURI; + nsCOMPtr mReferrerURI; }; #endif // !nsPrefetchService_h__