fixes bug 166647 "allow link prefetching from a META tag + other fixes"

r=dougt,hixie sr=rpotts
This commit is contained in:
darin%netscape.com 2002-09-11 03:12:13 +00:00
Родитель 425dbd86db
Коммит f83410ff28
5 изменённых файлов: 166 добавлений и 388 удалений

Просмотреть файл

@ -47,6 +47,7 @@ REQUIRES = xpcom \
uconv \
pref \
uriloader \
prefetch \
rdf \
chardet \
nkcache \

Просмотреть файл

@ -101,6 +101,7 @@
#include "nsICharsetAlias.h"
#include "nsIChannel.h"
#include "nsIHttpChannel.h"
#include "nsCPrefetchService.h"
#include "nsIWebShell.h"
#include "nsIDocShell.h"
@ -446,11 +447,16 @@ public:
void AddBaseTagInfo(nsIHTMLContent* aContent);
nsresult ProcessLink(nsIHTMLContent* aElement, const nsAString& aLinkData);
nsresult ProcessStyleLink(nsIHTMLContent* aElement,
nsresult ProcessLinkHeader(nsIHTMLContent* aElement, const nsAString& aLinkData);
nsresult ProcessLink(nsIHTMLContent* aElement,
const nsString& aHref, const nsString& aRel,
const nsString& aTitle, const nsString& aType,
const nsString& aMedia);
nsresult ProcessStyleLink(nsIHTMLContent* aElement,
const nsString& aHref, const nsStringArray& aLinkTypes,
const nsString& aTitle, const nsString& aType,
const nsString& aMedia);
void ProcessNextLink(const nsAString &aRel);
void ProcessBaseHref(const nsAString& aBaseHref);
void ProcessBaseTarget(const nsAString& aBaseTarget);
@ -4167,7 +4173,7 @@ const PRUnichar kLessThanCh = PRUnichar('<');
const PRUnichar kGreaterThanCh = PRUnichar('>');
nsresult
HTMLContentSink::ProcessLink(nsIHTMLContent* aElement, const nsAString& aLinkData)
HTMLContentSink::ProcessLinkHeader(nsIHTMLContent* aElement, const nsAString& aLinkData)
{
nsresult result = NS_OK;
@ -4282,8 +4288,8 @@ HTMLContentSink::ProcessLink(nsIHTMLContent* aElement, const nsAString& aLinkDat
}
}
if (kCommaCh == endCh) { // hit a comma, process what we've got so far
if (!href.IsEmpty()) {
result = ProcessStyleLink(aElement, href, rel, title, type, media);
if (!href.IsEmpty() && !rel.IsEmpty()) {
result = ProcessLink(aElement, href, rel, title, type, media);
if (NS_ERROR_HTMLPARSER_BLOCK == result) {
didBlock = PR_TRUE;
}
@ -4298,8 +4304,8 @@ HTMLContentSink::ProcessLink(nsIHTMLContent* aElement, const nsAString& aLinkDat
start = ++end;
}
if (!href.IsEmpty()) {
result = ProcessStyleLink(aElement, href, rel, title, type, media);
if (!href.IsEmpty() && !rel.IsEmpty()) {
result = ProcessLink(aElement, href, rel, title, type, media);
if (NS_SUCCEEDED(result) && didBlock) {
result = NS_ERROR_HTMLPARSER_BLOCK;
}
@ -4308,25 +4314,38 @@ HTMLContentSink::ProcessLink(nsIHTMLContent* aElement, const nsAString& aLinkDat
}
nsresult
HTMLContentSink::ProcessStyleLink(nsIHTMLContent* aElement,
HTMLContentSink::ProcessLink(nsIHTMLContent* aElement,
const nsString& aHref, const nsString& aRel,
const nsString& aTitle, const nsString& aType,
const nsString& aMedia)
{
nsresult result = NS_OK;
if (aHref.IsEmpty()) {
// if href is empty then just bail
// XXX seems overkill to generate this string array
nsStringArray linkTypes;
nsStyleLinkElement::ParseLinkTypes(aRel, linkTypes);
if (-1 != linkTypes.IndexOf(NS_LITERAL_STRING("next"))) { // is it a next link?
ProcessNextLink(aHref);
}
if (-1 != linkTypes.IndexOf(NS_LITERAL_STRING("stylesheet"))) { // is it a stylesheet link?
result = ProcessStyleLink(aElement, aHref, linkTypes, aTitle, aType, aMedia);
}
return result;
}
nsStringArray linkTypes;
nsStyleLinkElement::ParseLinkTypes(aRel, linkTypes);
nsresult
HTMLContentSink::ProcessStyleLink(nsIHTMLContent* aElement,
const nsString& aHref, const nsStringArray& aLinkTypes,
const nsString& aTitle, const nsString& aType,
const nsString& aMedia)
{
nsresult result = NS_OK;
PRBool isAlternate = PR_FALSE;
if (-1 != linkTypes.IndexOf(NS_LITERAL_STRING("stylesheet"))) { // is it a stylesheet link?
if (-1 != linkTypes.IndexOf(NS_LITERAL_STRING("alternate"))) { // if alternate, does it have title?
if (-1 != aLinkTypes.IndexOf(NS_LITERAL_STRING("alternate"))) { // if alternate, does it have title?
if (0 == aTitle.Length()) { // alternates must have title
return NS_OK; //return without error, for now
} else {
@ -4371,7 +4390,7 @@ HTMLContentSink::ProcessStyleLink(nsIHTMLContent* aElement,
return NS_OK; // The URL is bad, move along, don't propagate the error (for now)
}
if (-1 == linkTypes.IndexOf(NS_LITERAL_STRING("alternate"))) {
if (!isAlternate) {
if (!aTitle.IsEmpty()) { // possibly preferred sheet
nsAutoString preferredStyle;
mDocument->GetHeaderData(nsHTMLAtoms::headerDefaultStyle, preferredStyle);
@ -4406,10 +4425,28 @@ HTMLContentSink::ProcessStyleLink(nsIHTMLContent* aElement,
result = NS_ERROR_HTMLPARSER_BLOCK;
}
}
}
return result;
}
void
HTMLContentSink::ProcessNextLink(const nsAString &aHref)
{
nsCOMPtr<nsIPrefetchService> prefetchService(
do_GetService(NS_PREFETCHSERVICE_CONTRACTID));
if (prefetchService) {
// construct URI using document charset
nsAutoString charset;
mDocument->GetDocumentCharacterSet(charset);
nsCOMPtr<nsIURI> uri;
NS_NewURI(getter_AddRefs(uri), aHref,
charset.IsEmpty() ? nsnull
: NS_LossyConvertUCS2toASCII(charset).get(),
mDocumentBaseURL);
if (uri)
prefetchService->PrefetchURI(uri);
}
}
nsresult
HTMLContentSink::ProcessLINKTag(const nsIParserNode& aNode)
{
@ -4462,6 +4499,22 @@ HTMLContentSink::ProcessLINKTag(const nsIParserNode& aNode)
mStyleSheetCount++;
}
}
// look for <link rel="next" href="url">
nsAutoString relVal;
element->GetAttr(kNameSpaceID_None, nsHTMLAtoms::rel, relVal);
if (!relVal.IsEmpty()) {
// XXX seems overkill to generate this string array
nsStringArray linkTypes;
nsStyleLinkElement::ParseLinkTypes(relVal, linkTypes);
if (-1 != linkTypes.IndexOf(NS_LITERAL_STRING("next"))) {
nsAutoString hrefVal;
element->GetAttr(kNameSpaceID_None, nsHTMLAtoms::href, hrefVal);
if (!hrefVal.IsEmpty()) {
ProcessNextLink(hrefVal);
}
}
}
}
}
return result;
@ -4668,7 +4721,7 @@ HTMLContentSink::ProcessHeaderData(nsIAtom* aHeader,const nsAString& aValue,nsIH
if (NS_FAILED(rv)) return rv;
} // END set-cookie
else if (aHeader == nsHTMLAtoms::link) {
rv = ProcessLink(aContent, aValue);
rv = ProcessLinkHeader(aContent, aValue);
}
else if (mParser) {
// we also need to report back HTTP-EQUIV headers to the channel

Просмотреть файл

@ -72,7 +72,7 @@ static const nsModuleComponentInfo components[] = {
{ "Netscape Default Protocol Handler", NS_EXTERNALPROTOCOLHANDLER_CID, NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX"default",
nsExternalProtocolHandlerConstructor, },
{ NS_PREFETCHSERVICE_CLASSNAME, NS_PREFETCHSERVICE_CID, NS_PREFETCHSERVICE_CONTRACTID,
nsPrefetchServiceConstructor, nsPrefetchService::RegisterProc, nsPrefetchService::UnregisterProc },
nsPrefetchServiceConstructor, },
};

Просмотреть файл

@ -44,14 +44,11 @@
#include "nsIDocCharset.h"
#include "nsIWebProgress.h"
#include "nsCURILoader.h"
#include "nsINetModuleMgr.h"
#include "nsICachingChannel.h"
#include "nsICacheVisitor.h"
#include "nsIHttpChannel.h"
#include "nsIURL.h"
#include "nsNetUtil.h"
#include "nsIParserService.h"
#include "nsParserCIID.h"
#include "nsString.h"
#include "nsXPIDLString.h"
#include "nsReadableUtils.h"
@ -75,8 +72,6 @@ static PRLogModuleInfo *gPrefetchLog;
#define LOG_ENABLED() PR_LOG_TEST(gPrefetchLog, 4)
static NS_DEFINE_IID(kDocLoaderServiceCID, NS_DOCUMENTLOADER_SERVICE_CID);
static NS_DEFINE_IID(kParserServiceCID, NS_PARSERSERVICE_CID);
static NS_DEFINE_IID(kNetModuleMgrCID, NS_NETMODULEMGR_CID);
static NS_DEFINE_IID(kPrefServiceCID, NS_PREFSERVICE_CID);
//-----------------------------------------------------------------------------
@ -96,50 +91,6 @@ PRTimeToSeconds(PRTime t_usec)
#define NowInSeconds() PRTimeToSeconds(PR_Now())
//
// parses an attribute value pair (e.g., attr=value). allows
// double-quotation marks surrounding value. input must be null
// terminated, and will be updated to point past the attribute
// value pair on success or the next whitespace character or comma
// on failure.
//
static PRBool
ParseAttrValue(const char *&input,
const char *&ab,
const char *&ae,
const char *&vb,
const char *&ve)
{
const char *p = PL_strpbrk(input, " \t=,");
if (!p) {
input += strlen(input);
return PR_FALSE;
}
if (*p == ',' || *p == ' ' || *p == '\t') {
input = p;
return PR_FALSE;
}
// else, we located the equals sign...
ab = input;
ae = p;
vb = p+1;
if (*vb == '"')
++vb;
ve = PL_strpbrk(p+1, " \t,");
if (!ve)
ve = input + strlen(input);
input = ve;
// only ignore trailing quote if there was a leading one
if (ve > vb && *(vb-1) == '"' && *(ve-1) == '"')
--ve;
return PR_TRUE;
}
//-----------------------------------------------------------------------------
// nsPrefetchListener <public>
//-----------------------------------------------------------------------------
@ -247,6 +198,7 @@ nsPrefetchListener::OnStopRequest(nsIRequest *aRequest,
nsPrefetchService::nsPrefetchService()
: mQueueHead(nsnull)
, mQueueTail(nsnull)
, mStopCount(0)
, mDisabled(PR_FALSE)
{
NS_INIT_ISUPPORTS();
@ -267,12 +219,6 @@ nsPrefetchService::Init()
gPrefetchLog = PR_NewLogModule("nsPrefetch");
#endif
static const eHTMLTags watchTags[] =
{
eHTMLTag_link,
eHTMLTag_unknown
};
nsresult rv;
// Verify that "network.prefetch-next" preference is set to true. Skip
@ -299,21 +245,6 @@ nsPrefetchService::Init()
rv = observerServ->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, PR_TRUE);
if (NS_FAILED(rv)) return rv;
// Register as an observer for HTTP headers
nsCOMPtr<nsINetModuleMgr> netModuleMgr(do_GetService(kNetModuleMgrCID, &rv));
if (NS_FAILED(rv)) return rv;
rv = netModuleMgr->RegisterModule(
NS_NETWORK_MODULE_MANAGER_HTTP_RESPONSE_CONTRACTID, this);
if (NS_FAILED(rv)) return rv;
// Register as an observer for HTML "link" tags
nsCOMPtr<nsIParserService> parserServ(do_GetService(kParserServiceCID, &rv));
if (NS_FAILED(rv)) return rv;
rv = parserServ->RegisterObserver(this, NS_LITERAL_STRING("text/html"), watchTags);
if (NS_FAILED(rv)) return rv;
// Register as an observer for the document loader
nsCOMPtr<nsIWebProgress> progress(do_GetService(kDocLoaderServiceCID, &rv));
if (NS_FAILED(rv)) return rv;
@ -321,46 +252,6 @@ nsPrefetchService::Init()
return progress->AddProgressListener(this, nsIWebProgress::NOTIFY_STATE_DOCUMENT);
}
//
// we register ourselves with the parser service category to ensure that
// we'll be initialized before the first page is read.
//
NS_METHOD
nsPrefetchService::RegisterProc(nsIComponentManager *aCompMgr,
nsIFile *aPath,
const char *registryLocation,
const char *componentType,
const nsModuleComponentInfo *info)
{
nsCOMPtr<nsICategoryManager> catman(
do_GetService(NS_CATEGORYMANAGER_CONTRACTID));
if (catman) {
nsXPIDLCString prevEntry;
catman->AddCategoryEntry("parser-service-category",
NS_PREFETCHSERVICE_CLASSNAME,
NS_PREFETCHSERVICE_CONTRACTID,
PR_TRUE, PR_TRUE,
getter_Copies(prevEntry));
}
return NS_OK;
}
NS_METHOD
nsPrefetchService::UnregisterProc(nsIComponentManager *aCompMgr,
nsIFile *aPath,
const char *registryLocation,
const nsModuleComponentInfo *info)
{
nsCOMPtr<nsICategoryManager> catman(
do_GetService(NS_CATEGORYMANAGER_CONTRACTID));
if (catman)
catman->DeleteCategoryEntry("parser-service-category",
NS_PREFETCHSERVICE_CONTRACTID,
PR_TRUE);
return NS_OK;
}
void
nsPrefetchService::ProcessNextURI()
{
@ -453,49 +344,45 @@ nsPrefetchService::EmptyQueue()
void
nsPrefetchService::StartPrefetching()
{
LOG(("StartPrefetching\n"));
//
// at initialization time we might miss the first DOCUMENT START
// notification, so we have to be careful to avoid letting our
// stop count go negative.
//
if (mStopCount > 0)
mStopCount--;
if (!mCurrentChannel)
LOG(("StartPrefetching [stopcount=%d]\n", mStopCount));
// only start prefetching after we've received enough DOCUMENT
// STOP notifications. we do this inorder to defer prefetching
// until after all sub-frames have finished loading.
if (mStopCount == 0 && !mCurrentChannel)
ProcessNextURI();
}
void
nsPrefetchService::StopPrefetching()
{
LOG(("StopPrefetching\n"));
mStopCount++;
LOG(("StopPrefetching [stopcount=%d]\n", mStopCount));
// only kill the prefetch queue if we've actually started prefetching.
if (!mCurrentChannel)
return;
if (mCurrentChannel) {
mCurrentChannel->Cancel(NS_BINDING_ABORTED);
mCurrentChannel = nsnull;
}
EmptyQueue();
}
nsresult
nsPrefetchService::GetDocumentCharset(nsISupports *aWebshell, nsACString &aCharset)
{
nsresult rv;
nsCOMPtr<nsIDocCharset> docCharset(do_QueryInterface(aWebshell, &rv));
if (NS_FAILED(rv)) return rv;
nsXPIDLString uCharset;
rv = docCharset->GetCharset(getter_Copies(uCharset));
if (NS_FAILED(rv)) return rv;
CopyUCS2toASCII(uCharset, aCharset);
return NS_OK;
}
//-----------------------------------------------------------------------------
// nsPrefetchService::nsISupports
//-----------------------------------------------------------------------------
NS_IMPL_ISUPPORTS7(nsPrefetchService,
NS_IMPL_ISUPPORTS4(nsPrefetchService,
nsIPrefetchService,
nsIHttpNotify,
nsINetNotify,
nsIElementObserver,
nsIWebProgressListener,
nsIObserver,
nsISupportsWeakReference)
@ -585,139 +472,6 @@ nsPrefetchService::PrefetchURI(nsIURI *aURI)
return EnqueueURI(aURI);
}
//-----------------------------------------------------------------------------
// nsPrefetchService::nsIHttpNotify
//-----------------------------------------------------------------------------
NS_IMETHODIMP
nsPrefetchService::OnModifyRequest(nsIHttpChannel *aHttpChannel)
{
// ignored
return NS_OK;
}
NS_IMETHODIMP
nsPrefetchService::OnExamineResponse(nsIHttpChannel *aHttpChannel)
{
// look for Link: rel=next href=http://foo.com/blah
nsCAutoString linkVal;
aHttpChannel->GetResponseHeader(NS_LITERAL_CSTRING("link"), linkVal);
if (!linkVal.IsEmpty()) {
LOG(("nsPrefetchService::OnExamineResponse [Link: %s]\n", linkVal.get()));
// verify rel=next or rel="next" and extract href value
const char *it = linkVal.get();
const char *hrefBeg = nsnull;
const char *hrefEnd = nsnull;
PRBool haveRelNext = PR_FALSE;
for (; *it; ++it) {
if (*it == ',') {
haveRelNext = PR_FALSE;
hrefBeg = nsnull;
hrefEnd = nsnull;
}
// skip over whitespace
if (*it != ' ' && *it != '\t' && !(haveRelNext && hrefBeg)) {
// looking for attribute=value
const char *attrBeg, *attrEnd, *valBeg, *valEnd;
if (ParseAttrValue(it, attrBeg, attrEnd, valBeg, valEnd)) {
if (!haveRelNext && !PL_strncasecmp(attrBeg, "rel", attrEnd - attrBeg))
haveRelNext = PR_TRUE;
else if (!hrefBeg && !PL_strncasecmp(attrBeg, "href", attrEnd - attrBeg)) {
hrefBeg = valBeg;
hrefEnd = valEnd;
}
}
if (haveRelNext && hrefBeg) {
// ok, we got something to prefetch...
nsCOMPtr<nsIURI> uri, baseURI;
aHttpChannel->GetURI(getter_AddRefs(baseURI));
NS_NewURI(getter_AddRefs(uri),
Substring(hrefBeg, hrefEnd),
nsnull, baseURI);
if (uri)
PrefetchURI(uri);
}
continue; // do not increment
}
}
}
return NS_OK;
}
//-----------------------------------------------------------------------------
// nsPrefetchService::nsIElementObserver
//-----------------------------------------------------------------------------
NS_IMETHODIMP
nsPrefetchService::Notify(PRUint32 aDocumentID, eHTMLTags aTag,
PRUint32 numOfAttributes, const PRUnichar* nameArray[],
const PRUnichar* valueArray[])
{
// ignored
return NS_OK;
}
NS_IMETHODIMP
nsPrefetchService::Notify(PRUint32 aDocumentID, const PRUnichar* aTag,
PRUint32 numOfAttributes, const PRUnichar* nameArray[],
const PRUnichar *valueArray[])
{
// ignored
return NS_OK;
}
NS_IMETHODIMP
nsPrefetchService::Notify(nsISupports *aWebShell,
nsISupports *aChannel,
const PRUnichar *aTag,
const nsStringArray *aKeys,
const nsStringArray *aValues,
const PRUint32 aFlags)
{
LOG(("nsPrefetchService::Notify\n"));
PRInt32 count = aKeys->Count();
nsCOMPtr<nsIURI> uri;
PRBool relNext = PR_FALSE;
// check for <link rel=next href="http://blah">
for (PRInt32 i=0; i<count; ++i) {
nsString *key = aKeys->StringAt(i);
if (!relNext && key->EqualsIgnoreCase("rel")) {
if (aValues->StringAt(i)->EqualsIgnoreCase("next")) {
relNext = PR_TRUE;
if (uri)
break;
}
}
else if (!uri && key->EqualsIgnoreCase("href")) {
nsCOMPtr<nsIURI> baseURI;
nsCOMPtr<nsIChannel> channel(do_QueryInterface(aChannel));
if (channel) {
channel->GetURI(getter_AddRefs(baseURI));
// need to pass document charset to necko...
nsCAutoString docCharset;
GetDocumentCharset(aWebShell, docCharset);
NS_NewURI(getter_AddRefs(uri), *aValues->StringAt(i),
docCharset.IsEmpty() ? nsnull : docCharset.get(),
baseURI);
if (uri && relNext)
break;
}
}
}
if (relNext && uri)
PrefetchURI(uri);
return NS_OK;
}
//-----------------------------------------------------------------------------
// nsPrefetchService::nsIWebProgressListener
//-----------------------------------------------------------------------------

Просмотреть файл

@ -42,8 +42,6 @@
#include "nsIGenericFactory.h"
#include "nsIObserver.h"
#include "nsIWebProgressListener.h"
#include "nsIElementObserver.h"
#include "nsIHttpNotify.h"
#include "nsIStreamListener.h"
#include "nsIChannel.h"
#include "nsIURI.h"
@ -59,8 +57,6 @@ class nsPrefetchNode;
//-----------------------------------------------------------------------------
class nsPrefetchService : public nsIPrefetchService
, public nsIHttpNotify
, public nsIElementObserver
, public nsIWebProgressListener
, public nsIObserver
, public nsSupportsWeakReference
@ -68,7 +64,6 @@ class nsPrefetchService : public nsIPrefetchService
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIPREFETCHSERVICE
NS_DECL_NSIHTTPNOTIFY
NS_DECL_NSIWEBPROGRESSLISTENER
NS_DECL_NSIOBSERVER
@ -78,31 +73,6 @@ public:
nsresult Init();
void ProcessNextURI();
// nsIElementObserver methods
NS_IMETHOD Notify(PRUint32 aDocumentID, eHTMLTags aTag,
PRUint32 numOfAttributes, const PRUnichar* nameArray[],
const PRUnichar* valueArray[]);
NS_IMETHOD Notify(PRUint32 aDocumentID, const PRUnichar* aTag,
PRUint32 numOfAttributes, const PRUnichar* nameArray[],
const PRUnichar* valueArray[]);
NS_IMETHOD Notify(nsISupports* aWebShell,
nsISupports* aChannel,
const PRUnichar* aTag,
const nsStringArray* aKeys,
const nsStringArray* aValues,
const PRUint32 aFlags);
// XPCOM component registration methods
static NS_METHOD RegisterProc(nsIComponentManager *aCompMgr,
nsIFile *aPath,
const char *registryLocation,
const char *componentType,
const nsModuleComponentInfo *info);
static NS_METHOD UnregisterProc(nsIComponentManager *aCompMgr,
nsIFile *aPath,
const char *registryLocation,
const nsModuleComponentInfo *info);
private:
nsresult EnqueueURI(nsIURI *aURI);
@ -110,11 +80,11 @@ private:
void EmptyQueue();
void StartPrefetching();
void StopPrefetching();
nsresult GetDocumentCharset(nsISupports *aWebshell, nsACString &);
nsPrefetchNode *mQueueHead;
nsPrefetchNode *mQueueTail;
nsCOMPtr<nsIChannel> mCurrentChannel;
PRInt32 mStopCount;
PRBool mDisabled;
};