зеркало из https://github.com/mozilla/gecko-dev.git
Bug 364315 - Speculatively look for URLs in the document while the parser waits for a script to download and execute. This should show a decent speedup, especially on mobile. Currently, this only finds other <script>s to preload, but hopefully we'll extend it to images and stylesheets as well. r+sr=jst
This commit is contained in:
Родитель
4aadfb492e
Коммит
428671d01a
|
@ -96,6 +96,11 @@ public:
|
||||||
mElement->ScriptEvaluated(aResult, mElement, mIsInline);
|
mElement->ScriptEvaluated(aResult, mElement, mIsInline);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PRBool IsPreload()
|
||||||
|
{
|
||||||
|
return mElement == nsnull;
|
||||||
|
}
|
||||||
|
|
||||||
nsCOMPtr<nsIScriptElement> mElement;
|
nsCOMPtr<nsIScriptElement> mElement;
|
||||||
PRPackedBool mLoading; // Are we still waiting for a load to complete?
|
PRPackedBool mLoading; // Are we still waiting for a load to complete?
|
||||||
PRPackedBool mDefer; // Is execution defered?
|
PRPackedBool mDefer; // Is execution defered?
|
||||||
|
@ -189,6 +194,72 @@ IsScriptEventHandler(nsIScriptElement *aScriptElement)
|
||||||
return PR_FALSE;
|
return PR_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
nsScriptLoader::StartLoad(nsScriptLoadRequest *aRequest, const nsAString &aType)
|
||||||
|
{
|
||||||
|
// Check that the containing page is allowed to load this URI.
|
||||||
|
nsresult rv = nsContentUtils::GetSecurityManager()->
|
||||||
|
CheckLoadURIWithPrincipal(mDocument->NodePrincipal(), aRequest->mURI,
|
||||||
|
nsIScriptSecurityManager::ALLOW_CHROME);
|
||||||
|
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
// After the security manager, the content-policy stuff gets a veto
|
||||||
|
PRInt16 shouldLoad = nsIContentPolicy::ACCEPT;
|
||||||
|
rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_SCRIPT,
|
||||||
|
aRequest->mURI,
|
||||||
|
mDocument->NodePrincipal(),
|
||||||
|
aRequest->mElement,
|
||||||
|
NS_LossyConvertUTF16toASCII(aType),
|
||||||
|
nsnull, //extra
|
||||||
|
&shouldLoad,
|
||||||
|
nsContentUtils::GetContentPolicy(),
|
||||||
|
nsContentUtils::GetSecurityManager());
|
||||||
|
if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
|
||||||
|
if (NS_FAILED(rv) || shouldLoad != nsIContentPolicy::REJECT_TYPE) {
|
||||||
|
return NS_ERROR_CONTENT_BLOCKED;
|
||||||
|
}
|
||||||
|
return NS_ERROR_CONTENT_BLOCKED_SHOW_ALT;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCOMPtr<nsILoadGroup> loadGroup = mDocument->GetDocumentLoadGroup();
|
||||||
|
nsCOMPtr<nsIStreamLoader> loader;
|
||||||
|
|
||||||
|
nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(mDocument->GetScriptGlobalObject()));
|
||||||
|
nsIDocShell *docshell = window->GetDocShell();
|
||||||
|
|
||||||
|
nsCOMPtr<nsIInterfaceRequestor> prompter(do_QueryInterface(docshell));
|
||||||
|
|
||||||
|
nsCOMPtr<nsIChannel> channel;
|
||||||
|
rv = NS_NewChannel(getter_AddRefs(channel),
|
||||||
|
aRequest->mURI, nsnull, loadGroup,
|
||||||
|
prompter, nsIRequest::LOAD_NORMAL);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
|
||||||
|
if (httpChannel) {
|
||||||
|
// HTTP content negotation has little value in this context.
|
||||||
|
httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
|
||||||
|
NS_LITERAL_CSTRING("*/*"),
|
||||||
|
PR_FALSE);
|
||||||
|
httpChannel->SetReferrer(mDocument->GetDocumentURI());
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = NS_NewStreamLoader(getter_AddRefs(loader), this);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
return channel->AsyncOpen(loader, aRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
PRBool
|
||||||
|
nsScriptLoader::PreloadURIComparator::Equals(const PreloadInfo &aPi,
|
||||||
|
nsIURI * const &aURI) const
|
||||||
|
{
|
||||||
|
PRBool same;
|
||||||
|
return NS_SUCCEEDED(aPi.mRequest->mURI->Equals(aURI, &same)) &&
|
||||||
|
same;
|
||||||
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
nsScriptLoader::ProcessScriptElement(nsIScriptElement *aElement)
|
nsScriptLoader::ProcessScriptElement(nsIScriptElement *aElement)
|
||||||
{
|
{
|
||||||
|
@ -376,74 +447,55 @@ nsScriptLoader::ProcessScriptElement(nsIScriptElement *aElement)
|
||||||
nsCOMPtr<nsIContent> eltContent(do_QueryInterface(aElement));
|
nsCOMPtr<nsIContent> eltContent(do_QueryInterface(aElement));
|
||||||
eltContent->SetScriptTypeID(typeID);
|
eltContent->SetScriptTypeID(typeID);
|
||||||
|
|
||||||
|
PRBool hadPendingRequests = !!GetFirstPendingRequest();
|
||||||
|
|
||||||
|
// Did we preload this request?
|
||||||
|
nsCOMPtr<nsIURI> scriptURI = aElement->GetScriptURI();
|
||||||
|
nsRefPtr<nsScriptLoadRequest> request;
|
||||||
|
if (scriptURI) {
|
||||||
|
nsTArray<PreloadInfo>::index_type i =
|
||||||
|
mPreloads.IndexOf(scriptURI.get(), 0, PreloadURIComparator());
|
||||||
|
if (i != nsTArray<PreloadInfo>::NoIndex) {
|
||||||
|
request = mPreloads[i].mRequest;
|
||||||
|
request->mElement = aElement;
|
||||||
|
request->mJSVersion = version;
|
||||||
|
request->mDefer = mDeferEnabled && aElement->GetScriptDeferred();
|
||||||
|
mPreloads.RemoveElementAt(i);
|
||||||
|
|
||||||
|
if (!request->mLoading && !request->mDefer && !hadPendingRequests &&
|
||||||
|
ReadyToExecuteScripts() && nsContentUtils::IsSafeToRunScript()) {
|
||||||
|
return ProcessRequest(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not done loading yet. Move into the real requests queue and wait.
|
||||||
|
mRequests.AppendObject(request);
|
||||||
|
|
||||||
|
if (!request->mLoading && !hadPendingRequests && ReadyToExecuteScripts() &&
|
||||||
|
!request->mDefer) {
|
||||||
|
nsContentUtils::AddScriptRunner(new nsRunnableMethod<nsScriptLoader>(this,
|
||||||
|
&nsScriptLoader::ProcessPendingRequests));
|
||||||
|
}
|
||||||
|
|
||||||
|
return request->mDefer ? NS_OK : NS_ERROR_HTMLPARSER_BLOCK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Create a request object for this script
|
// Create a request object for this script
|
||||||
nsRefPtr<nsScriptLoadRequest> request = new nsScriptLoadRequest(aElement, version);
|
request = new nsScriptLoadRequest(aElement, version);
|
||||||
NS_ENSURE_TRUE(request, NS_ERROR_OUT_OF_MEMORY);
|
NS_ENSURE_TRUE(request, NS_ERROR_OUT_OF_MEMORY);
|
||||||
|
|
||||||
request->mDefer = mDeferEnabled && aElement->GetScriptDeferred();
|
request->mDefer = mDeferEnabled && aElement->GetScriptDeferred();
|
||||||
|
|
||||||
PRBool hadPendingRequests = !!GetFirstPendingRequest();
|
|
||||||
|
|
||||||
// First check to see if this is an external script
|
// First check to see if this is an external script
|
||||||
nsCOMPtr<nsIURI> scriptURI = aElement->GetScriptURI();
|
|
||||||
if (scriptURI) {
|
if (scriptURI) {
|
||||||
// Check that the containing page is allowed to load this URI.
|
|
||||||
rv = nsContentUtils::GetSecurityManager()->
|
|
||||||
CheckLoadURIWithPrincipal(mDocument->NodePrincipal(), scriptURI,
|
|
||||||
nsIScriptSecurityManager::ALLOW_CHROME);
|
|
||||||
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
|
|
||||||
// After the security manager, the content-policy stuff gets a veto
|
|
||||||
PRInt16 shouldLoad = nsIContentPolicy::ACCEPT;
|
|
||||||
rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_SCRIPT,
|
|
||||||
scriptURI,
|
|
||||||
mDocument->NodePrincipal(),
|
|
||||||
aElement,
|
|
||||||
NS_LossyConvertUTF16toASCII(type),
|
|
||||||
nsnull, //extra
|
|
||||||
&shouldLoad,
|
|
||||||
nsContentUtils::GetContentPolicy(),
|
|
||||||
nsContentUtils::GetSecurityManager());
|
|
||||||
if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
|
|
||||||
if (NS_FAILED(rv) || shouldLoad != nsIContentPolicy::REJECT_TYPE) {
|
|
||||||
return NS_ERROR_CONTENT_BLOCKED;
|
|
||||||
}
|
|
||||||
return NS_ERROR_CONTENT_BLOCKED_SHOW_ALT;
|
|
||||||
}
|
|
||||||
|
|
||||||
request->mURI = scriptURI;
|
request->mURI = scriptURI;
|
||||||
request->mIsInline = PR_FALSE;
|
request->mIsInline = PR_FALSE;
|
||||||
request->mLoading = PR_TRUE;
|
request->mLoading = PR_TRUE;
|
||||||
|
|
||||||
nsCOMPtr<nsILoadGroup> loadGroup = mDocument->GetDocumentLoadGroup();
|
rv = StartLoad(request, type);
|
||||||
nsCOMPtr<nsIStreamLoader> loader;
|
if (NS_FAILED(rv)) {
|
||||||
|
return rv;
|
||||||
nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(globalObject));
|
|
||||||
nsIDocShell *docshell = window->GetDocShell();
|
|
||||||
|
|
||||||
nsCOMPtr<nsIInterfaceRequestor> prompter(do_QueryInterface(docshell));
|
|
||||||
|
|
||||||
nsCOMPtr<nsIChannel> channel;
|
|
||||||
rv = NS_NewChannel(getter_AddRefs(channel),
|
|
||||||
scriptURI, nsnull, loadGroup,
|
|
||||||
prompter, nsIRequest::LOAD_NORMAL);
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
|
|
||||||
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
|
|
||||||
if (httpChannel) {
|
|
||||||
// HTTP content negotation has little value in this context.
|
|
||||||
httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
|
|
||||||
NS_LITERAL_CSTRING("*/*"),
|
|
||||||
PR_FALSE);
|
|
||||||
httpChannel->SetReferrer(mDocument->GetDocumentURI());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rv = NS_NewStreamLoader(getter_AddRefs(loader), this);
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
|
|
||||||
rv = channel->AsyncOpen(loader, request);
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
} else {
|
} else {
|
||||||
request->mLoading = PR_FALSE;
|
request->mLoading = PR_FALSE;
|
||||||
request->mIsInline = PR_TRUE;
|
request->mIsInline = PR_TRUE;
|
||||||
|
@ -814,8 +866,11 @@ nsScriptLoader::OnStreamComplete(nsIStreamLoader* aLoader,
|
||||||
nsresult rv = PrepareLoadedRequest(request, aLoader, aStatus, aStringLen,
|
nsresult rv = PrepareLoadedRequest(request, aLoader, aStatus, aStringLen,
|
||||||
aString);
|
aString);
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
mRequests.RemoveObject(request);
|
if (!mRequests.RemoveObject(request)) {
|
||||||
FireScriptAvailable(rv, request);
|
mPreloads.RemoveElement(request, PreloadRequestComparator());
|
||||||
|
} else {
|
||||||
|
FireScriptAvailable(rv, request);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process our request and/or any pending ones
|
// Process our request and/or any pending ones
|
||||||
|
@ -860,7 +915,14 @@ nsScriptLoader::PrepareLoadedRequest(nsScriptLoadRequest* aRequest,
|
||||||
if (aStringLen) {
|
if (aStringLen) {
|
||||||
// Check the charset attribute to determine script charset.
|
// Check the charset attribute to determine script charset.
|
||||||
nsAutoString hintCharset;
|
nsAutoString hintCharset;
|
||||||
aRequest->mElement->GetScriptCharset(hintCharset);
|
if (!aRequest->IsPreload()) {
|
||||||
|
aRequest->mElement->GetScriptCharset(hintCharset);
|
||||||
|
} else {
|
||||||
|
nsTArray<PreloadInfo>::index_type i =
|
||||||
|
mPreloads.IndexOf(aRequest, 0, PreloadRequestComparator());
|
||||||
|
NS_ASSERTION(i != mPreloads.NoIndex, "Incorrect preload bookkeeping");
|
||||||
|
hintCharset = mPreloads[i].mCharset;
|
||||||
|
}
|
||||||
rv = ConvertToUTF16(channel, aString, aStringLen, hintCharset, mDocument,
|
rv = ConvertToUTF16(channel, aString, aStringLen, hintCharset, mDocument,
|
||||||
aRequest->mScriptText);
|
aRequest->mScriptText);
|
||||||
|
|
||||||
|
@ -875,7 +937,8 @@ nsScriptLoader::PrepareLoadedRequest(nsScriptLoadRequest* aRequest,
|
||||||
// inserting the request in the array. However it's an unlikely case
|
// inserting the request in the array. However it's an unlikely case
|
||||||
// so if you see this assertion it is likely something else that is
|
// so if you see this assertion it is likely something else that is
|
||||||
// wrong, especially if you see it more than once.
|
// wrong, especially if you see it more than once.
|
||||||
NS_ASSERTION(mRequests.IndexOf(aRequest) >= 0,
|
NS_ASSERTION(mRequests.IndexOf(aRequest) >= 0 ||
|
||||||
|
mPreloads.Contains(aRequest, PreloadRequestComparator()),
|
||||||
"aRequest should be pending!");
|
"aRequest should be pending!");
|
||||||
|
|
||||||
// Mark this as loaded
|
// Mark this as loaded
|
||||||
|
@ -918,9 +981,33 @@ void
|
||||||
nsScriptLoader::EndDeferringScripts()
|
nsScriptLoader::EndDeferringScripts()
|
||||||
{
|
{
|
||||||
mDeferEnabled = PR_FALSE;
|
mDeferEnabled = PR_FALSE;
|
||||||
for (PRUint32 i = 0; i < mRequests.Count(); ++i) {
|
for (PRUint32 i = 0; i < (PRUint32)mRequests.Count(); ++i) {
|
||||||
mRequests[i]->mDefer = PR_FALSE;
|
mRequests[i]->mDefer = PR_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
ProcessPendingRequests();
|
ProcessPendingRequests();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nsScriptLoader::PreloadURI(nsIURI *aURI, const nsAString &aCharset,
|
||||||
|
const nsAString &aType)
|
||||||
|
{
|
||||||
|
nsRefPtr<nsScriptLoadRequest> request = new nsScriptLoadRequest(nsnull, 0);
|
||||||
|
if (!request) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
request->mURI = aURI;
|
||||||
|
request->mIsInline = PR_FALSE;
|
||||||
|
request->mLoading = PR_TRUE;
|
||||||
|
request->mDefer = PR_FALSE; // This is computed later when we go to execute the
|
||||||
|
// script.
|
||||||
|
nsresult rv = StartLoad(request, aType);
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PreloadInfo *pi = mPreloads.AppendElement();
|
||||||
|
pi->mRequest = request;
|
||||||
|
pi->mCharset = aCharset;
|
||||||
|
}
|
||||||
|
|
|
@ -205,7 +205,22 @@ public:
|
||||||
*/
|
*/
|
||||||
void EndDeferringScripts();
|
void EndDeferringScripts();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds aURI to the preload list and starts loading it.
|
||||||
|
*
|
||||||
|
* @param aURI The URI of the external script.
|
||||||
|
* @param aCharset The charset parameter for the script.
|
||||||
|
* @param aType The type parameter for the script.
|
||||||
|
*/
|
||||||
|
virtual void PreloadURI(nsIURI *aURI, const nsAString &aCharset,
|
||||||
|
const nsAString &aType);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
/**
|
||||||
|
* Start a load for aRequest's URI.
|
||||||
|
*/
|
||||||
|
nsresult StartLoad(nsScriptLoadRequest *aRequest, const nsAString &aType);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process any pending requests asyncronously (i.e. off an event) if there
|
* Process any pending requests asyncronously (i.e. off an event) if there
|
||||||
* are any. Note that this is a no-op if there aren't any currently pending
|
* are any. Note that this is a no-op if there aren't any currently pending
|
||||||
|
@ -253,6 +268,25 @@ protected:
|
||||||
nsIDocument* mDocument; // [WEAK]
|
nsIDocument* mDocument; // [WEAK]
|
||||||
nsCOMArray<nsIScriptLoaderObserver> mObservers;
|
nsCOMArray<nsIScriptLoaderObserver> mObservers;
|
||||||
nsCOMArray<nsScriptLoadRequest> mRequests;
|
nsCOMArray<nsScriptLoadRequest> mRequests;
|
||||||
|
|
||||||
|
// In mRequests, the additional information here is stored by the element.
|
||||||
|
struct PreloadInfo {
|
||||||
|
nsRefPtr<nsScriptLoadRequest> mRequest;
|
||||||
|
nsString mCharset;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PreloadRequestComparator {
|
||||||
|
PRBool Equals(const PreloadInfo &aPi, nsScriptLoadRequest * const &aRequest)
|
||||||
|
const
|
||||||
|
{
|
||||||
|
return aRequest == aPi.mRequest;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
struct PreloadURIComparator {
|
||||||
|
PRBool Equals(const PreloadInfo &aPi, nsIURI * const &aURI) const;
|
||||||
|
};
|
||||||
|
nsTArray<PreloadInfo> mPreloads;
|
||||||
|
|
||||||
nsCOMPtr<nsIScriptElement> mCurrentScript;
|
nsCOMPtr<nsIScriptElement> mCurrentScript;
|
||||||
// XXXbz do we want to cycle-collect these or something? Not sure.
|
// XXXbz do we want to cycle-collect these or something? Not sure.
|
||||||
nsTArray< nsRefPtr<nsScriptLoader> > mPendingChildLoaders;
|
nsTArray< nsRefPtr<nsScriptLoader> > mPendingChildLoaders;
|
||||||
|
|
|
@ -63,7 +63,7 @@ CParserContext::CParserContext(nsScanner* aScanner,
|
||||||
mParserCommand(aCommand),
|
mParserCommand(aCommand),
|
||||||
mMultipart(PR_TRUE),
|
mMultipart(PR_TRUE),
|
||||||
mCopyUnused(aCopyUnused),
|
mCopyUnused(aCopyUnused),
|
||||||
mTransferBufferSize(eTransferBufferSize)
|
mNumConsumed(0)
|
||||||
{
|
{
|
||||||
MOZ_COUNT_CTOR(CParserContext);
|
MOZ_COUNT_CTOR(CParserContext);
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,20 +60,17 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class CParserContext {
|
class CParserContext {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
enum eContextType {eCTNone,eCTURL,eCTString,eCTStream};
|
||||||
|
|
||||||
enum {eTransferBufferSize=4096};
|
CParserContext(nsScanner* aScanner,
|
||||||
enum eContextType {eCTNone,eCTURL,eCTString,eCTStream};
|
void* aKey = 0,
|
||||||
|
eParserCommands aCommand = eViewNormal,
|
||||||
|
nsIRequestObserver* aListener = 0,
|
||||||
|
nsIDTD* aDTD = 0,
|
||||||
|
eAutoDetectResult aStatus = eUnknownDetect,
|
||||||
|
PRBool aCopyUnused = PR_FALSE);
|
||||||
|
|
||||||
CParserContext( nsScanner* aScanner,
|
|
||||||
void* aKey=0,
|
|
||||||
eParserCommands aCommand=eViewNormal,
|
|
||||||
nsIRequestObserver* aListener=0,
|
|
||||||
nsIDTD *aDTD=0,
|
|
||||||
eAutoDetectResult aStatus=eUnknownDetect,
|
|
||||||
PRBool aCopyUnused=PR_FALSE);
|
|
||||||
|
|
||||||
~CParserContext();
|
~CParserContext();
|
||||||
|
|
||||||
nsresult GetTokenizer(PRInt32 aType,
|
nsresult GetTokenizer(PRInt32 aType,
|
||||||
|
@ -83,26 +80,26 @@ public:
|
||||||
|
|
||||||
nsCOMPtr<nsIRequest> mRequest; // provided by necko to differnciate different input streams
|
nsCOMPtr<nsIRequest> mRequest; // provided by necko to differnciate different input streams
|
||||||
// why is mRequest strongly referenced? see bug 102376.
|
// why is mRequest strongly referenced? see bug 102376.
|
||||||
nsCOMPtr<nsIDTD> mDTD;
|
nsCOMPtr<nsIDTD> mDTD;
|
||||||
nsCOMPtr<nsIRequestObserver> mListener;
|
nsCOMPtr<nsIRequestObserver> mListener;
|
||||||
nsAutoArrayPtr<char> mTransferBuffer;
|
|
||||||
void* mKey;
|
void* mKey;
|
||||||
nsCOMPtr<nsITokenizer> mTokenizer;
|
nsCOMPtr<nsITokenizer> mTokenizer;
|
||||||
CParserContext* mPrevContext;
|
CParserContext* mPrevContext;
|
||||||
nsAutoPtr<nsScanner> mScanner;
|
nsAutoPtr<nsScanner> mScanner;
|
||||||
|
|
||||||
nsCString mMimeType;
|
nsCString mMimeType;
|
||||||
nsDTDMode mDTDMode;
|
nsDTDMode mDTDMode;
|
||||||
|
|
||||||
eParserDocType mDocType;
|
eParserDocType mDocType;
|
||||||
eStreamState mStreamListenerState; //this is really only here for debug purposes.
|
eStreamState mStreamListenerState;
|
||||||
eContextType mContextType;
|
eContextType mContextType;
|
||||||
eAutoDetectResult mAutoDetectStatus;
|
eAutoDetectResult mAutoDetectStatus;
|
||||||
eParserCommands mParserCommand; //tells us to viewcontent/viewsource/viewerrors...
|
eParserCommands mParserCommand;
|
||||||
|
|
||||||
PRPackedBool mMultipart;
|
PRPackedBool mMultipart;
|
||||||
PRPackedBool mCopyUnused;
|
PRPackedBool mCopyUnused;
|
||||||
PRUint32 mTransferBufferSize;
|
|
||||||
|
PRUint32 mNumConsumed;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
/* vim: set sw=2 ts=2 et tw=78: */
|
/* vim: set sw=2 ts=2 et tw=79: */
|
||||||
/* ***** BEGIN LICENSE BLOCK *****
|
/* ***** BEGIN LICENSE BLOCK *****
|
||||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||||
*
|
*
|
||||||
|
@ -52,6 +52,9 @@
|
||||||
#include "nsIInputStream.h"
|
#include "nsIInputStream.h"
|
||||||
#include "CNavDTD.h"
|
#include "CNavDTD.h"
|
||||||
#include "prenv.h"
|
#include "prenv.h"
|
||||||
|
#include "prlock.h"
|
||||||
|
#include "prcvar.h"
|
||||||
|
#include "nsAutoLock.h"
|
||||||
#include "nsParserCIID.h"
|
#include "nsParserCIID.h"
|
||||||
#include "nsReadableUtils.h"
|
#include "nsReadableUtils.h"
|
||||||
#include "nsCOMPtr.h"
|
#include "nsCOMPtr.h"
|
||||||
|
@ -61,6 +64,11 @@
|
||||||
#include "nsISupportsPrimitives.h"
|
#include "nsISupportsPrimitives.h"
|
||||||
#include "nsIFragmentContentSink.h"
|
#include "nsIFragmentContentSink.h"
|
||||||
#include "nsStreamUtils.h"
|
#include "nsStreamUtils.h"
|
||||||
|
#include "nsHTMLTokenizer.h"
|
||||||
|
#include "nsIDocument.h"
|
||||||
|
#include "nsNetUtil.h"
|
||||||
|
#include "nsScriptLoader.h"
|
||||||
|
#include "nsDataHashtable.h"
|
||||||
|
|
||||||
#ifdef MOZ_VIEW_SOURCE
|
#ifdef MOZ_VIEW_SOURCE
|
||||||
#include "nsViewSourceHTML.h"
|
#include "nsViewSourceHTML.h"
|
||||||
|
@ -155,6 +163,478 @@ public:
|
||||||
|
|
||||||
//-------------- End ParseContinue Event Definition ------------------------
|
//-------------- End ParseContinue Event Definition ------------------------
|
||||||
|
|
||||||
|
template <class Type>
|
||||||
|
class Holder {
|
||||||
|
public:
|
||||||
|
typedef void (*Reaper)(Type *);
|
||||||
|
|
||||||
|
Holder(Reaper aReaper)
|
||||||
|
: mHoldee(nsnull), mReaper(aReaper)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~Holder() {
|
||||||
|
if (mHoldee) {
|
||||||
|
mReaper(mHoldee);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Type *get() {
|
||||||
|
return mHoldee;
|
||||||
|
}
|
||||||
|
const Holder &operator =(Type *aHoldee) {
|
||||||
|
if (mHoldee && aHoldee != mHoldee) {
|
||||||
|
mReaper(mHoldee);
|
||||||
|
}
|
||||||
|
mHoldee = aHoldee;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Type *mHoldee;
|
||||||
|
Reaper mReaper;
|
||||||
|
};
|
||||||
|
|
||||||
|
class nsSpeculativeScriptThread : public nsIRunnable {
|
||||||
|
public:
|
||||||
|
nsSpeculativeScriptThread(nsTokenAllocator *aTokenAllocator)
|
||||||
|
: mLock(PR_DestroyLock),
|
||||||
|
mCVar(PR_DestroyCondVar),
|
||||||
|
mKeepParsing(0),
|
||||||
|
mCurrentlyParsing(0),
|
||||||
|
mNumURIs(0),
|
||||||
|
mNumConsumed(0),
|
||||||
|
mTokenAllocator(aTokenAllocator) {
|
||||||
|
}
|
||||||
|
|
||||||
|
~nsSpeculativeScriptThread() {
|
||||||
|
if (mThread) {
|
||||||
|
mThread->Shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_DECL_ISUPPORTS
|
||||||
|
NS_DECL_NSIRUNNABLE
|
||||||
|
|
||||||
|
nsresult StartParsing(nsParser *aParser);
|
||||||
|
void StopParsing(PRBool aFromDocWrite);
|
||||||
|
|
||||||
|
enum PrefetchType { SCRIPT, STYLESHEET, IMAGE };
|
||||||
|
struct PrefetchEntry {
|
||||||
|
PrefetchType type;
|
||||||
|
nsString uri;
|
||||||
|
nsString charset;
|
||||||
|
nsString elementType;
|
||||||
|
};
|
||||||
|
|
||||||
|
nsIDocument *GetDocument() {
|
||||||
|
NS_ASSERTION(NS_IsMainThread(), "Potential threadsafety hazard");
|
||||||
|
return mDocument;
|
||||||
|
}
|
||||||
|
|
||||||
|
PRBool Parsing() {
|
||||||
|
return mCurrentlyParsing;
|
||||||
|
}
|
||||||
|
|
||||||
|
CParserContext *Context() {
|
||||||
|
return mContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef nsDataHashtable<nsCStringHashKey, PRBool> PreloadedType;
|
||||||
|
PreloadedType& GetPreloadedURIs() {
|
||||||
|
return mPreloadedURIs;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
nsresult ProcessToken(CToken *aToken);
|
||||||
|
|
||||||
|
void AddToPrefetchList(const nsAString &src,
|
||||||
|
const nsAString &charset,
|
||||||
|
const nsAString &elementType,
|
||||||
|
PrefetchType type);
|
||||||
|
|
||||||
|
// The following members are shared across the main thread and the
|
||||||
|
// speculatively parsing thread.
|
||||||
|
Holder<PRLock> mLock;
|
||||||
|
Holder<PRCondVar> mCVar;
|
||||||
|
|
||||||
|
PRUint32 mKeepParsing;
|
||||||
|
PRUint32 mCurrentlyParsing;
|
||||||
|
nsRefPtr<nsHTMLTokenizer> mTokenizer;
|
||||||
|
nsAutoPtr<nsScanner> mScanner;
|
||||||
|
|
||||||
|
enum { kBatchPrefetchURIs = 5 };
|
||||||
|
nsAutoTArray<PrefetchEntry, kBatchPrefetchURIs> mURIs;
|
||||||
|
PRUint16 mNumURIs;
|
||||||
|
|
||||||
|
// Number of characters consumed by the last speculative parse.
|
||||||
|
PRUint32 mNumConsumed;
|
||||||
|
|
||||||
|
// These members are only accessed on the main thread.
|
||||||
|
nsCOMPtr<nsIThread> mThread;
|
||||||
|
nsCOMPtr<nsIDocument> mDocument;
|
||||||
|
CParserContext *mContext;
|
||||||
|
PreloadedType mPreloadedURIs;
|
||||||
|
|
||||||
|
// These members are only accessed on the speculatively parsing thread.
|
||||||
|
nsTokenAllocator *mTokenAllocator;
|
||||||
|
};
|
||||||
|
|
||||||
|
class nsPreloadURIs : public nsIRunnable {
|
||||||
|
public:
|
||||||
|
nsPreloadURIs(nsAutoTArray<nsSpeculativeScriptThread::PrefetchEntry, 5> &aURIs,
|
||||||
|
nsSpeculativeScriptThread *aScriptThread)
|
||||||
|
: mURIs(aURIs),
|
||||||
|
mScriptThread(aScriptThread) {
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_DECL_ISUPPORTS
|
||||||
|
NS_DECL_NSIRUNNABLE
|
||||||
|
|
||||||
|
static void PreloadURIs(const nsAutoTArray<nsSpeculativeScriptThread::PrefetchEntry, 5> &aURIs,
|
||||||
|
nsSpeculativeScriptThread *aScriptThread);
|
||||||
|
|
||||||
|
private:
|
||||||
|
nsAutoTArray<nsSpeculativeScriptThread::PrefetchEntry, 5> mURIs;
|
||||||
|
nsRefPtr<nsSpeculativeScriptThread> mScriptThread;
|
||||||
|
};
|
||||||
|
|
||||||
|
NS_IMPL_THREADSAFE_ISUPPORTS1(nsPreloadURIs, nsIRunnable)
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
nsPreloadURIs::Run()
|
||||||
|
{
|
||||||
|
PreloadURIs(mURIs, mScriptThread);
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nsPreloadURIs::PreloadURIs(const nsAutoTArray<nsSpeculativeScriptThread::PrefetchEntry, 5> &aURIs,
|
||||||
|
nsSpeculativeScriptThread *aScriptThread)
|
||||||
|
{
|
||||||
|
NS_ASSERTION(NS_IsMainThread(), "Touching non-threadsafe objects off thread");
|
||||||
|
|
||||||
|
nsIDocument *doc = aScriptThread->GetDocument();
|
||||||
|
NS_ASSERTION(doc, "We shouldn't have started preloading without a document");
|
||||||
|
|
||||||
|
// Note: Per the code in the HTML content sink, we should be keeping track
|
||||||
|
// of each <base href> as it comes. However, because we do our speculative
|
||||||
|
// parsing off the main thread, this is hard to emulate. For now, just load
|
||||||
|
// the URIs using the document's base URI at the potential cost of being
|
||||||
|
// wrong and having to re-load a given relative URI later.
|
||||||
|
nsIURI *base = doc->GetBaseURI();
|
||||||
|
const nsCString &charset = doc->GetDocumentCharacterSet();
|
||||||
|
nsSpeculativeScriptThread::PreloadedType &alreadyPreloaded =
|
||||||
|
aScriptThread->GetPreloadedURIs();
|
||||||
|
for (PRUint32 i = 0, e = aURIs.Length(); i < e; ++i) {
|
||||||
|
const nsSpeculativeScriptThread::PrefetchEntry &pe = aURIs[i];
|
||||||
|
if (pe.type != nsSpeculativeScriptThread::SCRIPT) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCOMPtr<nsIURI> uri;
|
||||||
|
nsresult rv = NS_NewURI(getter_AddRefs(uri), pe.uri, charset.get(), base);
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
NS_WARNING("Failed to create a URI");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCAutoString spec;
|
||||||
|
uri->GetSpec(spec);
|
||||||
|
PRBool answer;
|
||||||
|
if (alreadyPreloaded.Get(spec, &answer)) {
|
||||||
|
// Already preloaded. Don't preload again.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
alreadyPreloaded.Put(spec, PR_TRUE);
|
||||||
|
|
||||||
|
doc->ScriptLoader()->PreloadURI(uri, pe.charset, pe.elementType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMPL_THREADSAFE_ISUPPORTS1(nsSpeculativeScriptThread, nsIRunnable)
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
nsSpeculativeScriptThread::Run()
|
||||||
|
{
|
||||||
|
nsScannerIterator start;
|
||||||
|
mScanner->CurrentPosition(start);
|
||||||
|
mTokenizer->WillTokenize(PR_FALSE, mTokenAllocator);
|
||||||
|
while (mKeepParsing) {
|
||||||
|
PRBool flushTokens = PR_FALSE;
|
||||||
|
nsresult rv = mTokenizer->ConsumeToken(*mScanner, flushTokens);
|
||||||
|
if (rv == kEOF) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Don't pop the tokens.
|
||||||
|
CToken *token;
|
||||||
|
while (mKeepParsing && NS_SUCCEEDED(rv) && (token = mTokenizer->PopToken())) {
|
||||||
|
rv = ProcessToken(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mTokenizer->DidTokenize(PR_FALSE);
|
||||||
|
|
||||||
|
nsAutoLock al(mLock.get());
|
||||||
|
|
||||||
|
nsScannerIterator end;
|
||||||
|
mScanner->CurrentPosition(end);
|
||||||
|
|
||||||
|
mNumConsumed = Distance(start, end);
|
||||||
|
|
||||||
|
mCurrentlyParsing = 0;
|
||||||
|
PR_NotifyCondVar(mCVar.get());
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
nsSpeculativeScriptThread::StartParsing(nsParser *aParser)
|
||||||
|
{
|
||||||
|
NS_ASSERTION(NS_IsMainThread(), "Called on the wrong thread");
|
||||||
|
NS_ASSERTION(!mCurrentlyParsing, "Bad race happening");
|
||||||
|
|
||||||
|
nsIContentSink *sink = aParser->GetContentSink();
|
||||||
|
if (!sink) {
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCOMPtr<nsIDocument> doc = do_QueryInterface(sink->GetTarget());
|
||||||
|
if (!doc) {
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsAutoString toScan;
|
||||||
|
CParserContext *context = aParser->PeekContext();
|
||||||
|
if (!mThread) {
|
||||||
|
nsresult rv = NS_NewThread(getter_AddRefs(mThread), nsnull);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
mLock = PR_NewLock();
|
||||||
|
if (!mLock.get()) {
|
||||||
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
mCVar = PR_NewCondVar(mLock.get());
|
||||||
|
if (!mCVar.get()) {
|
||||||
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mPreloadedURIs.Init(15)) {
|
||||||
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
mTokenizer = new nsHTMLTokenizer(context->mDTDMode, context->mDocType,
|
||||||
|
context->mParserCommand, 0);
|
||||||
|
if (!mTokenizer) {
|
||||||
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
mTokenizer->CopyState(context->mTokenizer);
|
||||||
|
context->mScanner->CopyUnusedData(toScan);
|
||||||
|
} else if (context == mContext) {
|
||||||
|
// Don't parse the same part of the document twice.
|
||||||
|
nsScannerIterator end;
|
||||||
|
context->mScanner->EndReading(end);
|
||||||
|
|
||||||
|
nsScannerIterator start;
|
||||||
|
context->mScanner->CurrentPosition(start);
|
||||||
|
|
||||||
|
if (mNumConsumed > context->mNumConsumed) {
|
||||||
|
// We consumed more the last time we tried speculatively parsing than we
|
||||||
|
// did the last time we actually parsed.
|
||||||
|
PRUint32 distance = Distance(start, end);
|
||||||
|
start.advance(PR_MIN(mNumConsumed - context->mNumConsumed, distance));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start == end) {
|
||||||
|
// We're at the end of this context's buffer, nothing else to do.
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
CopyUnicodeTo(start, end, toScan);
|
||||||
|
} else {
|
||||||
|
// Grab all of the context.
|
||||||
|
context->mScanner->CopyUnusedData(toScan);
|
||||||
|
if (toScan.IsEmpty()) {
|
||||||
|
// Nothing to parse, don't do anything.
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCAutoString charset;
|
||||||
|
PRInt32 source;
|
||||||
|
aParser->GetDocumentCharset(charset, source);
|
||||||
|
|
||||||
|
mScanner = new nsScanner(toScan, charset, source);
|
||||||
|
if (!mScanner) {
|
||||||
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
mDocument.swap(doc);
|
||||||
|
mKeepParsing = 1;
|
||||||
|
mCurrentlyParsing = 1;
|
||||||
|
mContext = context;
|
||||||
|
return mThread->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nsSpeculativeScriptThread::StopParsing(PRBool aFromDocWrite)
|
||||||
|
{
|
||||||
|
NS_ASSERTION(NS_IsMainThread(), "Can't stop parsing from another thread");
|
||||||
|
|
||||||
|
if (!mThread) {
|
||||||
|
// If we bailed early out of StartParsing, don't do anything.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
nsAutoLock al(mLock.get());
|
||||||
|
|
||||||
|
mKeepParsing = 0;
|
||||||
|
if (mCurrentlyParsing) {
|
||||||
|
PR_WaitCondVar(mCVar.get(), PR_INTERVAL_NO_TIMEOUT);
|
||||||
|
NS_ASSERTION(!mCurrentlyParsing, "Didn't actually stop parsing?");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The thread is now idle. It is now safe to touch mContext on the main
|
||||||
|
// thread.
|
||||||
|
if (mNumURIs) {
|
||||||
|
nsPreloadURIs::PreloadURIs(mURIs, this);
|
||||||
|
mNumURIs = 0;
|
||||||
|
mURIs.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: Currently, we pop the tokens off (see the comment in Run) so this
|
||||||
|
// isn't a problem. If and when we actually use the tokens created
|
||||||
|
// off-thread, we'll need to use aFromDocWrite for real.
|
||||||
|
(void)aFromDocWrite;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
nsSpeculativeScriptThread::ProcessToken(CToken *aToken)
|
||||||
|
{
|
||||||
|
// Only called on the speculative script thread.
|
||||||
|
|
||||||
|
CHTMLToken *token = static_cast<CHTMLToken *>(aToken);
|
||||||
|
switch (static_cast<eHTMLTokenTypes>(token->GetTokenType())) {
|
||||||
|
case eToken_start: {
|
||||||
|
CStartToken *start = static_cast<CStartToken *>(aToken);
|
||||||
|
nsHTMLTag tag = static_cast<nsHTMLTag>(start->GetTypeID());
|
||||||
|
PRInt16 attrs = start->GetAttributeCount();
|
||||||
|
PRInt16 i = 0;
|
||||||
|
nsAutoString src;
|
||||||
|
nsAutoString elementType;
|
||||||
|
nsAutoString charset;
|
||||||
|
PrefetchType ptype;
|
||||||
|
|
||||||
|
switch (tag) {
|
||||||
|
#if 0 // TODO Support stylesheet and image preloading.
|
||||||
|
case eHTMLTag_link: {
|
||||||
|
// If this is a <link rel=stylesheet> find the src.
|
||||||
|
PRBool isRelStylesheet = PR_FALSE;
|
||||||
|
for (; i < attrs; ++i) {
|
||||||
|
CAttributeToken *attr = static_cast<CAttributeToken *>(mTokenizer->PopToken());
|
||||||
|
NS_ASSERTION(attr->GetTokenType() == eToken_attribute, "Weird token");
|
||||||
|
|
||||||
|
if (attr->GetKey().EqualsLiteral("rel")) {
|
||||||
|
if (!attr->GetValue().EqualsLiteral("stylesheet")) {
|
||||||
|
IF_FREE(attr, mTokenAllocator);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
isRelStylesheet = PR_TRUE;
|
||||||
|
} else if (attr->GetKey().EqualsLiteral("src")) {
|
||||||
|
src.Assign(attr->GetValue());
|
||||||
|
if (isRelStylesheet) {
|
||||||
|
IF_FREE(attr, mTokenAllocator);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IF_FREE(attr, mTokenAllocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isRelStylesheet && !src.IsEmpty()) {
|
||||||
|
AddToPrefetchList(src, STYLESHEET);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case eHTMLTag_style:
|
||||||
|
ptype = STYLESHEET;
|
||||||
|
case eHTMLTag_img:
|
||||||
|
if (tag == eHTMLTag_img)
|
||||||
|
ptype = IMAGE;
|
||||||
|
#endif
|
||||||
|
case eHTMLTag_script:
|
||||||
|
if (tag == eHTMLTag_script)
|
||||||
|
ptype = SCRIPT;
|
||||||
|
|
||||||
|
for (; i < attrs; ++i) {
|
||||||
|
CAttributeToken *attr = static_cast<CAttributeToken *>(mTokenizer->PopToken());
|
||||||
|
NS_ASSERTION(attr->GetTokenType() == eToken_attribute, "Weird token");
|
||||||
|
|
||||||
|
if (attr->GetKey().EqualsLiteral("src")) {
|
||||||
|
src.Assign(attr->GetValue());
|
||||||
|
} else if (attr->GetKey().EqualsLiteral("charset")) {
|
||||||
|
charset.Assign(attr->GetValue());
|
||||||
|
} else if (attr->GetKey().EqualsLiteral("type")) {
|
||||||
|
elementType.Assign(attr->GetValue());
|
||||||
|
}
|
||||||
|
IF_FREE(attr, mTokenAllocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!src.IsEmpty()) {
|
||||||
|
AddToPrefetchList(src, charset, elementType, ptype);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; i < attrs; ++i) {
|
||||||
|
CToken *attr = mTokenizer->PopToken();
|
||||||
|
if (!attr) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
NS_ASSERTION(attr->GetTokenType() == eToken_attribute, "Weird token");
|
||||||
|
IF_FREE(attr, mTokenAllocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
IF_FREE(aToken, mTokenAllocator);
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nsSpeculativeScriptThread::AddToPrefetchList(const nsAString &src,
|
||||||
|
const nsAString &charset,
|
||||||
|
const nsAString &elementType,
|
||||||
|
PrefetchType type)
|
||||||
|
{
|
||||||
|
PrefetchEntry *pe = mURIs.InsertElementAt(mNumURIs++);
|
||||||
|
pe->type = type;
|
||||||
|
pe->uri = src;
|
||||||
|
pe->charset = charset;
|
||||||
|
pe->elementType = elementType;
|
||||||
|
|
||||||
|
if (mNumURIs == kBatchPrefetchURIs) {
|
||||||
|
nsCOMPtr<nsIRunnable> r = new nsPreloadURIs(mURIs, this);
|
||||||
|
|
||||||
|
mNumURIs = 0;
|
||||||
|
mURIs.Clear();
|
||||||
|
NS_DispatchToMainThread(r, NS_DISPATCH_NORMAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
nsICharsetAlias* nsParser::sCharsetAliasService = nsnull;
|
nsICharsetAlias* nsParser::sCharsetAliasService = nsnull;
|
||||||
nsICharsetConverterManager* nsParser::sCharsetConverterManager = nsnull;
|
nsICharsetConverterManager* nsParser::sCharsetConverterManager = nsnull;
|
||||||
|
|
||||||
|
@ -321,6 +801,10 @@ nsParser::Cleanup()
|
||||||
// destroyed since this flag implies a pending nsParserContinueEvent, which
|
// destroyed since this flag implies a pending nsParserContinueEvent, which
|
||||||
// has an owning reference to |this|.
|
// has an owning reference to |this|.
|
||||||
NS_ASSERTION(!(mFlags & NS_PARSER_FLAG_PENDING_CONTINUE_EVENT), "bad");
|
NS_ASSERTION(!(mFlags & NS_PARSER_FLAG_PENDING_CONTINUE_EVENT), "bad");
|
||||||
|
if (mSpeculativeScriptThread) {
|
||||||
|
mSpeculativeScriptThread->StopParsing(PR_FALSE);
|
||||||
|
mSpeculativeScriptThread = nsnull;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMPL_CYCLE_COLLECTION_CLASS(nsParser)
|
NS_IMPL_CYCLE_COLLECTION_CLASS(nsParser)
|
||||||
|
@ -1007,6 +1491,26 @@ nsParser::DidBuildModel(nsresult anErrorCode)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nsParser::SpeculativelyParse()
|
||||||
|
{
|
||||||
|
if (mParserContext->mParserCommand == eViewNormal &&
|
||||||
|
!mParserContext->mMimeType.EqualsLiteral("text/html")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mSpeculativeScriptThread) {
|
||||||
|
mSpeculativeScriptThread = new nsSpeculativeScriptThread(&mTokenAllocator);
|
||||||
|
if (!mSpeculativeScriptThread) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult rv = mSpeculativeScriptThread->StartParsing(this);
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
mSpeculativeScriptThread = nsnull;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method adds a new parser context to the list,
|
* This method adds a new parser context to the list,
|
||||||
|
@ -1107,6 +1611,9 @@ nsParser::Terminate(void)
|
||||||
// will reset it so DidBuildModel will call DidBuildModel on the DTD. Note:
|
// will reset it so DidBuildModel will call DidBuildModel on the DTD. Note:
|
||||||
// The IsComplete() call inside of DidBuildModel looks at the pendingContinueEvents flag.
|
// The IsComplete() call inside of DidBuildModel looks at the pendingContinueEvents flag.
|
||||||
CancelParsingEvents();
|
CancelParsingEvents();
|
||||||
|
if (mSpeculativeScriptThread) {
|
||||||
|
mSpeculativeScriptThread->StopParsing(PR_FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
// If we got interrupted in the middle of a document.write, then we might
|
// If we got interrupted in the middle of a document.write, then we might
|
||||||
// have more than one parser context on our parsercontext stack. This has
|
// have more than one parser context on our parsercontext stack. This has
|
||||||
|
@ -1163,6 +1670,10 @@ nsParser::ContinueInterruptedParsing()
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (mSpeculativeScriptThread) {
|
||||||
|
mSpeculativeScriptThread->StopParsing(PR_FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
PRBool isFinalChunk = mParserContext &&
|
PRBool isFinalChunk = mParserContext &&
|
||||||
mParserContext->mStreamListenerState == eOnStop;
|
mParserContext->mStreamListenerState == eOnStop;
|
||||||
|
|
||||||
|
@ -1295,6 +1806,7 @@ nsParser::Parse(nsIURI* aURL,
|
||||||
{
|
{
|
||||||
|
|
||||||
NS_PRECONDITION(aURL, "Error: Null URL given");
|
NS_PRECONDITION(aURL, "Error: Null URL given");
|
||||||
|
NS_ASSERTION(!mSpeculativeScriptThread, "Can't reuse a parser like this");
|
||||||
|
|
||||||
nsresult result=kBadURL;
|
nsresult result=kBadURL;
|
||||||
mObserver = aListener;
|
mObserver = aListener;
|
||||||
|
@ -1362,6 +1874,10 @@ nsParser::Parse(const nsAString& aSourceBuffer,
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mSpeculativeScriptThread) {
|
||||||
|
mSpeculativeScriptThread->StopParsing(PR_TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
// Hack to pass on to the dtd the caller's desire to
|
// Hack to pass on to the dtd the caller's desire to
|
||||||
// parse a fragment without worrying about containment rules
|
// parse a fragment without worrying about containment rules
|
||||||
if (aMode == eDTDMode_fragment)
|
if (aMode == eDTDMode_fragment)
|
||||||
|
@ -1482,6 +1998,8 @@ nsParser::ParseFragment(const nsAString& aSourceBuffer,
|
||||||
// Disable observers for fragments
|
// Disable observers for fragments
|
||||||
mFlags &= ~NS_PARSER_FLAG_OBSERVERS_ENABLED;
|
mFlags &= ~NS_PARSER_FLAG_OBSERVERS_ENABLED;
|
||||||
|
|
||||||
|
NS_ASSERTION(!mSpeculativeScriptThread, "Can't reuse a parser like this");
|
||||||
|
|
||||||
for (theIndex = 0; theIndex < theCount; theIndex++) {
|
for (theIndex = 0; theIndex < theCount; theIndex++) {
|
||||||
theContext.AppendLiteral("<");
|
theContext.AppendLiteral("<");
|
||||||
theContext.Append(aTagStack[theCount - theIndex - 1]);
|
theContext.Append(aTagStack[theCount - theIndex - 1]);
|
||||||
|
@ -1622,6 +2140,8 @@ nsParser::ResumeParse(PRBool allowIteration, PRBool aIsFinalChunk,
|
||||||
MOZ_TIMER_DEBUGLOG(("Start: Parse Time: nsParser::ResumeParse(), this=%p\n", this));
|
MOZ_TIMER_DEBUGLOG(("Start: Parse Time: nsParser::ResumeParse(), this=%p\n", this));
|
||||||
MOZ_TIMER_START(mParseTime);
|
MOZ_TIMER_START(mParseTime);
|
||||||
|
|
||||||
|
NS_ASSERTION(!mSpeculativeScriptThread || !mSpeculativeScriptThread->Parsing(), "Bad races happening");
|
||||||
|
|
||||||
result = WillBuildModel(mParserContext->mScanner->GetFilename());
|
result = WillBuildModel(mParserContext->mScanner->GetFilename());
|
||||||
if (NS_FAILED(result)) {
|
if (NS_FAILED(result)) {
|
||||||
mFlags &= ~NS_PARSER_FLAG_CAN_TOKENIZE;
|
mFlags &= ~NS_PARSER_FLAG_CAN_TOKENIZE;
|
||||||
|
@ -1671,6 +2191,7 @@ nsParser::ResumeParse(PRBool allowIteration, PRBool aIsFinalChunk,
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockParser();
|
BlockParser();
|
||||||
|
SpeculativelyParse();
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
if (NS_ERROR_HTMLPARSER_STOPPARSING == result) {
|
if (NS_ERROR_HTMLPARSER_STOPPARSING == result) {
|
||||||
|
@ -2260,6 +2781,11 @@ nsParser::OnDataAvailable(nsIRequest *request, nsISupports* aContext,
|
||||||
if (theContext) {
|
if (theContext) {
|
||||||
theContext->mStreamListenerState = eOnDataAvail;
|
theContext->mStreamListenerState = eOnDataAvail;
|
||||||
|
|
||||||
|
if ((mFlags & NS_PARSER_FLAG_PARSER_ENABLED) &&
|
||||||
|
mSpeculativeScriptThread) {
|
||||||
|
mSpeculativeScriptThread->StopParsing(PR_FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
if (eInvalidDetect == theContext->mAutoDetectStatus) {
|
if (eInvalidDetect == theContext->mAutoDetectStatus) {
|
||||||
if (theContext->mScanner) {
|
if (theContext->mScanner) {
|
||||||
nsScannerIterator iter;
|
nsScannerIterator iter;
|
||||||
|
@ -2304,6 +2830,10 @@ nsParser::OnStopRequest(nsIRequest *request, nsISupports* aContext,
|
||||||
{
|
{
|
||||||
nsresult rv = NS_OK;
|
nsresult rv = NS_OK;
|
||||||
|
|
||||||
|
if (mSpeculativeScriptThread) {
|
||||||
|
mSpeculativeScriptThread->StopParsing(PR_FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
if (eOnStart == mParserContext->mStreamListenerState) {
|
if (eOnStart == mParserContext->mStreamListenerState) {
|
||||||
// If you're here, then OnDataAvailable() never got called. Prior
|
// If you're here, then OnDataAvailable() never got called. Prior
|
||||||
// to necko, we never dealt with this case, but the problem may
|
// to necko, we never dealt with this case, but the problem may
|
||||||
|
@ -2417,9 +2947,11 @@ nsresult nsParser::Tokenize(PRBool aIsFinalChunk)
|
||||||
|
|
||||||
MOZ_TIMER_START(mTokenizeTime);
|
MOZ_TIMER_START(mTokenizeTime);
|
||||||
|
|
||||||
|
mParserContext->mNumConsumed = 0;
|
||||||
|
|
||||||
WillTokenize(aIsFinalChunk);
|
WillTokenize(aIsFinalChunk);
|
||||||
while (NS_SUCCEEDED(result)) {
|
while (NS_SUCCEEDED(result)) {
|
||||||
mParserContext->mScanner->Mark();
|
mParserContext->mNumConsumed += mParserContext->mScanner->Mark();
|
||||||
result = theTokenizer->ConsumeToken(*mParserContext->mScanner,
|
result = theTokenizer->ConsumeToken(*mParserContext->mScanner,
|
||||||
flushTokens);
|
flushTokens);
|
||||||
if (NS_FAILED(result)) {
|
if (NS_FAILED(result)) {
|
||||||
|
@ -2436,7 +2968,7 @@ nsresult nsParser::Tokenize(PRBool aIsFinalChunk)
|
||||||
// Flush tokens on seeing </SCRIPT> -- Ref: Bug# 22485 --
|
// Flush tokens on seeing </SCRIPT> -- Ref: Bug# 22485 --
|
||||||
// Also remember to update the marked position.
|
// Also remember to update the marked position.
|
||||||
mFlags |= NS_PARSER_FLAG_FLUSH_TOKENS;
|
mFlags |= NS_PARSER_FLAG_FLUSH_TOKENS;
|
||||||
mParserContext->mScanner->Mark();
|
mParserContext->mNumConsumed += mParserContext->mScanner->Mark();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,6 +95,7 @@ class nsICharsetAlias;
|
||||||
class nsIDTD;
|
class nsIDTD;
|
||||||
class nsScanner;
|
class nsScanner;
|
||||||
class nsIProgressEventSink;
|
class nsIProgressEventSink;
|
||||||
|
class nsSpeculativeScriptThread;
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#pragma warning( disable : 4275 )
|
#pragma warning( disable : 4275 )
|
||||||
|
@ -406,7 +407,9 @@ class nsParser : public nsIParser,
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
nsresult DidBuildModel(nsresult anErrorCode);
|
nsresult DidBuildModel(nsresult anErrorCode);
|
||||||
|
|
||||||
|
void SpeculativelyParse();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
/*******************************************
|
/*******************************************
|
||||||
|
@ -457,6 +460,7 @@ protected:
|
||||||
nsCOMPtr<nsIRequestObserver> mObserver;
|
nsCOMPtr<nsIRequestObserver> mObserver;
|
||||||
nsCOMPtr<nsIContentSink> mSink;
|
nsCOMPtr<nsIContentSink> mSink;
|
||||||
nsIRunnable* mContinueEvent; // weak ref
|
nsIRunnable* mContinueEvent; // weak ref
|
||||||
|
nsRefPtr<nsSpeculativeScriptThread> mSpeculativeScriptThread;
|
||||||
|
|
||||||
nsCOMPtr<nsIParserFilter> mParserFilter;
|
nsCOMPtr<nsIParserFilter> mParserFilter;
|
||||||
nsTokenAllocator mTokenAllocator;
|
nsTokenAllocator mTokenAllocator;
|
||||||
|
|
|
@ -146,16 +146,15 @@ nsScanner::nsScanner(nsString& aFilename,PRBool aCreateStream,
|
||||||
SetDocumentCharset(aCharset, aSource);
|
SetDocumentCharset(aCharset, aSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult nsScanner::SetDocumentCharset(const nsACString& aCharset , PRInt32 aSource) {
|
nsresult nsScanner::SetDocumentCharset(const nsACString& aCharset , PRInt32 aSource)
|
||||||
|
{
|
||||||
nsresult res = NS_OK;
|
if (aSource < mCharsetSource) // priority is lower the the current one , just
|
||||||
|
return NS_OK;
|
||||||
if( aSource < mCharsetSource) // priority is lower the the current one , just
|
|
||||||
return res;
|
|
||||||
|
|
||||||
nsICharsetAlias* calias = nsParser::GetCharsetAliasService();
|
nsICharsetAlias* calias = nsParser::GetCharsetAliasService();
|
||||||
NS_ASSERTION(calias, "Must have the charset alias service!");
|
NS_ASSERTION(calias, "Must have the charset alias service!");
|
||||||
|
|
||||||
|
nsresult res = NS_OK;
|
||||||
if (!mCharset.IsEmpty())
|
if (!mCharset.IsEmpty())
|
||||||
{
|
{
|
||||||
PRBool same;
|
PRBool same;
|
||||||
|
@ -185,17 +184,8 @@ nsresult nsScanner::SetDocumentCharset(const nsACString& aCharset , PRInt32 aSou
|
||||||
NS_ASSERTION(nsParser::GetCharsetConverterManager(),
|
NS_ASSERTION(nsParser::GetCharsetConverterManager(),
|
||||||
"Must have the charset converter manager!");
|
"Must have the charset converter manager!");
|
||||||
|
|
||||||
nsIUnicodeDecoder * decoder = nsnull;
|
return nsParser::GetCharsetConverterManager()->
|
||||||
res = nsParser::GetCharsetConverterManager()->
|
GetUnicodeDecoderRaw(mCharset.get(), getter_AddRefs(mUnicodeDecoder));
|
||||||
GetUnicodeDecoderRaw(mCharset.get(), &decoder);
|
|
||||||
if(NS_SUCCEEDED(res) && (nsnull != decoder))
|
|
||||||
{
|
|
||||||
NS_IF_RELEASE(mUnicodeDecoder);
|
|
||||||
|
|
||||||
mUnicodeDecoder = decoder;
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -213,8 +203,6 @@ nsScanner::~nsScanner() {
|
||||||
}
|
}
|
||||||
|
|
||||||
MOZ_COUNT_DTOR(nsScanner);
|
MOZ_COUNT_DTOR(nsScanner);
|
||||||
|
|
||||||
NS_IF_RELEASE(mUnicodeDecoder);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -244,14 +232,21 @@ void nsScanner::RewindToMark(void){
|
||||||
* @param
|
* @param
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
void nsScanner::Mark() {
|
PRInt32 nsScanner::Mark() {
|
||||||
|
PRInt32 distance = 0;
|
||||||
if (mSlidingBuffer) {
|
if (mSlidingBuffer) {
|
||||||
|
nsScannerIterator oldStart;
|
||||||
|
mSlidingBuffer->BeginReading(oldStart);
|
||||||
|
|
||||||
|
distance = Distance(oldStart, mCurrentPosition);
|
||||||
|
|
||||||
mSlidingBuffer->DiscardPrefix(mCurrentPosition);
|
mSlidingBuffer->DiscardPrefix(mCurrentPosition);
|
||||||
mSlidingBuffer->BeginReading(mCurrentPosition);
|
mSlidingBuffer->BeginReading(mCurrentPosition);
|
||||||
mMarkPosition = mCurrentPosition;
|
mMarkPosition = mCurrentPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return distance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Insert data to our underlying input buffer as
|
* Insert data to our underlying input buffer as
|
||||||
|
|
|
@ -207,7 +207,7 @@ class nsScanner {
|
||||||
* @param
|
* @param
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
void Mark(void);
|
PRInt32 Mark(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resets current offset position of input stream to marked position.
|
* Resets current offset position of input stream to marked position.
|
||||||
|
@ -338,8 +338,11 @@ class nsScanner {
|
||||||
PRInt32 mFirstNonWhitespacePosition;
|
PRInt32 mFirstNonWhitespacePosition;
|
||||||
PRInt32 mCharsetSource;
|
PRInt32 mCharsetSource;
|
||||||
nsCString mCharset;
|
nsCString mCharset;
|
||||||
nsIUnicodeDecoder *mUnicodeDecoder;
|
nsCOMPtr<nsIUnicodeDecoder> mUnicodeDecoder;
|
||||||
nsParser *mParser;
|
nsParser *mParser;
|
||||||
|
|
||||||
|
private:
|
||||||
|
nsScanner &operator =(const nsScanner &); // Not implemented.
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Загрузка…
Ссылка в новой задаче