зеркало из 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);
|
||||
}
|
||||
|
||||
PRBool IsPreload()
|
||||
{
|
||||
return mElement == nsnull;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIScriptElement> mElement;
|
||||
PRPackedBool mLoading; // Are we still waiting for a load to complete?
|
||||
PRPackedBool mDefer; // Is execution defered?
|
||||
|
@ -189,6 +194,72 @@ IsScriptEventHandler(nsIScriptElement *aScriptElement)
|
|||
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
|
||||
nsScriptLoader::ProcessScriptElement(nsIScriptElement *aElement)
|
||||
{
|
||||
|
@ -376,74 +447,55 @@ nsScriptLoader::ProcessScriptElement(nsIScriptElement *aElement)
|
|||
nsCOMPtr<nsIContent> eltContent(do_QueryInterface(aElement));
|
||||
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
|
||||
nsRefPtr<nsScriptLoadRequest> request = new nsScriptLoadRequest(aElement, version);
|
||||
request = new nsScriptLoadRequest(aElement, version);
|
||||
NS_ENSURE_TRUE(request, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
request->mDefer = mDeferEnabled && aElement->GetScriptDeferred();
|
||||
|
||||
PRBool hadPendingRequests = !!GetFirstPendingRequest();
|
||||
|
||||
// First check to see if this is an external script
|
||||
nsCOMPtr<nsIURI> scriptURI = aElement->GetScriptURI();
|
||||
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->mIsInline = PR_FALSE;
|
||||
request->mLoading = PR_TRUE;
|
||||
|
||||
nsCOMPtr<nsILoadGroup> loadGroup = mDocument->GetDocumentLoadGroup();
|
||||
nsCOMPtr<nsIStreamLoader> loader;
|
||||
|
||||
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 = StartLoad(request, type);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = NS_NewStreamLoader(getter_AddRefs(loader), this);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = channel->AsyncOpen(loader, request);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
} else {
|
||||
request->mLoading = PR_FALSE;
|
||||
request->mIsInline = PR_TRUE;
|
||||
|
@ -814,8 +866,11 @@ nsScriptLoader::OnStreamComplete(nsIStreamLoader* aLoader,
|
|||
nsresult rv = PrepareLoadedRequest(request, aLoader, aStatus, aStringLen,
|
||||
aString);
|
||||
if (NS_FAILED(rv)) {
|
||||
mRequests.RemoveObject(request);
|
||||
FireScriptAvailable(rv, request);
|
||||
if (!mRequests.RemoveObject(request)) {
|
||||
mPreloads.RemoveElement(request, PreloadRequestComparator());
|
||||
} else {
|
||||
FireScriptAvailable(rv, request);
|
||||
}
|
||||
}
|
||||
|
||||
// Process our request and/or any pending ones
|
||||
|
@ -860,7 +915,14 @@ nsScriptLoader::PrepareLoadedRequest(nsScriptLoadRequest* aRequest,
|
|||
if (aStringLen) {
|
||||
// Check the charset attribute to determine script charset.
|
||||
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,
|
||||
aRequest->mScriptText);
|
||||
|
||||
|
@ -875,7 +937,8 @@ nsScriptLoader::PrepareLoadedRequest(nsScriptLoadRequest* aRequest,
|
|||
// 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
|
||||
// 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!");
|
||||
|
||||
// Mark this as loaded
|
||||
|
@ -918,9 +981,33 @@ void
|
|||
nsScriptLoader::EndDeferringScripts()
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
/**
|
||||
* 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:
|
||||
/**
|
||||
* 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
|
||||
* are any. Note that this is a no-op if there aren't any currently pending
|
||||
|
@ -253,6 +268,25 @@ protected:
|
|||
nsIDocument* mDocument; // [WEAK]
|
||||
nsCOMArray<nsIScriptLoaderObserver> mObservers;
|
||||
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;
|
||||
// XXXbz do we want to cycle-collect these or something? Not sure.
|
||||
nsTArray< nsRefPtr<nsScriptLoader> > mPendingChildLoaders;
|
||||
|
|
|
@ -63,7 +63,7 @@ CParserContext::CParserContext(nsScanner* aScanner,
|
|||
mParserCommand(aCommand),
|
||||
mMultipart(PR_TRUE),
|
||||
mCopyUnused(aCopyUnused),
|
||||
mTransferBufferSize(eTransferBufferSize)
|
||||
mNumConsumed(0)
|
||||
{
|
||||
MOZ_COUNT_CTOR(CParserContext);
|
||||
}
|
||||
|
|
|
@ -60,20 +60,17 @@
|
|||
*/
|
||||
|
||||
class CParserContext {
|
||||
|
||||
public:
|
||||
enum eContextType {eCTNone,eCTURL,eCTString,eCTStream};
|
||||
|
||||
enum {eTransferBufferSize=4096};
|
||||
enum eContextType {eCTNone,eCTURL,eCTString,eCTStream};
|
||||
CParserContext(nsScanner* aScanner,
|
||||
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();
|
||||
|
||||
nsresult GetTokenizer(PRInt32 aType,
|
||||
|
@ -83,26 +80,26 @@ public:
|
|||
|
||||
nsCOMPtr<nsIRequest> mRequest; // provided by necko to differnciate different input streams
|
||||
// why is mRequest strongly referenced? see bug 102376.
|
||||
nsCOMPtr<nsIDTD> mDTD;
|
||||
nsCOMPtr<nsIDTD> mDTD;
|
||||
nsCOMPtr<nsIRequestObserver> mListener;
|
||||
nsAutoArrayPtr<char> mTransferBuffer;
|
||||
void* mKey;
|
||||
nsCOMPtr<nsITokenizer> mTokenizer;
|
||||
CParserContext* mPrevContext;
|
||||
nsAutoPtr<nsScanner> mScanner;
|
||||
|
||||
|
||||
nsCString mMimeType;
|
||||
nsDTDMode mDTDMode;
|
||||
|
||||
|
||||
eParserDocType mDocType;
|
||||
eStreamState mStreamListenerState; //this is really only here for debug purposes.
|
||||
eStreamState mStreamListenerState;
|
||||
eContextType mContextType;
|
||||
eAutoDetectResult mAutoDetectStatus;
|
||||
eParserCommands mParserCommand; //tells us to viewcontent/viewsource/viewerrors...
|
||||
eParserCommands mParserCommand;
|
||||
|
||||
PRPackedBool mMultipart;
|
||||
PRPackedBool mCopyUnused;
|
||||
PRUint32 mTransferBufferSize;
|
||||
|
||||
PRUint32 mNumConsumed;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* -*- 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 *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
|
@ -52,6 +52,9 @@
|
|||
#include "nsIInputStream.h"
|
||||
#include "CNavDTD.h"
|
||||
#include "prenv.h"
|
||||
#include "prlock.h"
|
||||
#include "prcvar.h"
|
||||
#include "nsAutoLock.h"
|
||||
#include "nsParserCIID.h"
|
||||
#include "nsReadableUtils.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
@ -61,6 +64,11 @@
|
|||
#include "nsISupportsPrimitives.h"
|
||||
#include "nsIFragmentContentSink.h"
|
||||
#include "nsStreamUtils.h"
|
||||
#include "nsHTMLTokenizer.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsScriptLoader.h"
|
||||
#include "nsDataHashtable.h"
|
||||
|
||||
#ifdef MOZ_VIEW_SOURCE
|
||||
#include "nsViewSourceHTML.h"
|
||||
|
@ -155,6 +163,478 @@ public:
|
|||
|
||||
//-------------- 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;
|
||||
nsICharsetConverterManager* nsParser::sCharsetConverterManager = nsnull;
|
||||
|
||||
|
@ -321,6 +801,10 @@ nsParser::Cleanup()
|
|||
// destroyed since this flag implies a pending nsParserContinueEvent, which
|
||||
// has an owning reference to |this|.
|
||||
NS_ASSERTION(!(mFlags & NS_PARSER_FLAG_PENDING_CONTINUE_EVENT), "bad");
|
||||
if (mSpeculativeScriptThread) {
|
||||
mSpeculativeScriptThread->StopParsing(PR_FALSE);
|
||||
mSpeculativeScriptThread = nsnull;
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(nsParser)
|
||||
|
@ -1007,6 +1491,26 @@ nsParser::DidBuildModel(nsresult anErrorCode)
|
|||
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,
|
||||
|
@ -1107,6 +1611,9 @@ nsParser::Terminate(void)
|
|||
// will reset it so DidBuildModel will call DidBuildModel on the DTD. Note:
|
||||
// The IsComplete() call inside of DidBuildModel looks at the pendingContinueEvents flag.
|
||||
CancelParsingEvents();
|
||||
if (mSpeculativeScriptThread) {
|
||||
mSpeculativeScriptThread->StopParsing(PR_FALSE);
|
||||
}
|
||||
|
||||
// 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
|
||||
|
@ -1163,6 +1670,10 @@ nsParser::ContinueInterruptedParsing()
|
|||
}
|
||||
#endif
|
||||
|
||||
if (mSpeculativeScriptThread) {
|
||||
mSpeculativeScriptThread->StopParsing(PR_FALSE);
|
||||
}
|
||||
|
||||
PRBool isFinalChunk = mParserContext &&
|
||||
mParserContext->mStreamListenerState == eOnStop;
|
||||
|
||||
|
@ -1295,6 +1806,7 @@ nsParser::Parse(nsIURI* aURL,
|
|||
{
|
||||
|
||||
NS_PRECONDITION(aURL, "Error: Null URL given");
|
||||
NS_ASSERTION(!mSpeculativeScriptThread, "Can't reuse a parser like this");
|
||||
|
||||
nsresult result=kBadURL;
|
||||
mObserver = aListener;
|
||||
|
@ -1362,6 +1874,10 @@ nsParser::Parse(const nsAString& aSourceBuffer,
|
|||
return result;
|
||||
}
|
||||
|
||||
if (mSpeculativeScriptThread) {
|
||||
mSpeculativeScriptThread->StopParsing(PR_TRUE);
|
||||
}
|
||||
|
||||
// Hack to pass on to the dtd the caller's desire to
|
||||
// parse a fragment without worrying about containment rules
|
||||
if (aMode == eDTDMode_fragment)
|
||||
|
@ -1482,6 +1998,8 @@ nsParser::ParseFragment(const nsAString& aSourceBuffer,
|
|||
// Disable observers for fragments
|
||||
mFlags &= ~NS_PARSER_FLAG_OBSERVERS_ENABLED;
|
||||
|
||||
NS_ASSERTION(!mSpeculativeScriptThread, "Can't reuse a parser like this");
|
||||
|
||||
for (theIndex = 0; theIndex < theCount; theIndex++) {
|
||||
theContext.AppendLiteral("<");
|
||||
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_START(mParseTime);
|
||||
|
||||
NS_ASSERTION(!mSpeculativeScriptThread || !mSpeculativeScriptThread->Parsing(), "Bad races happening");
|
||||
|
||||
result = WillBuildModel(mParserContext->mScanner->GetFilename());
|
||||
if (NS_FAILED(result)) {
|
||||
mFlags &= ~NS_PARSER_FLAG_CAN_TOKENIZE;
|
||||
|
@ -1671,6 +2191,7 @@ nsParser::ResumeParse(PRBool allowIteration, PRBool aIsFinalChunk,
|
|||
}
|
||||
|
||||
BlockParser();
|
||||
SpeculativelyParse();
|
||||
return NS_OK;
|
||||
}
|
||||
if (NS_ERROR_HTMLPARSER_STOPPARSING == result) {
|
||||
|
@ -2260,6 +2781,11 @@ nsParser::OnDataAvailable(nsIRequest *request, nsISupports* aContext,
|
|||
if (theContext) {
|
||||
theContext->mStreamListenerState = eOnDataAvail;
|
||||
|
||||
if ((mFlags & NS_PARSER_FLAG_PARSER_ENABLED) &&
|
||||
mSpeculativeScriptThread) {
|
||||
mSpeculativeScriptThread->StopParsing(PR_FALSE);
|
||||
}
|
||||
|
||||
if (eInvalidDetect == theContext->mAutoDetectStatus) {
|
||||
if (theContext->mScanner) {
|
||||
nsScannerIterator iter;
|
||||
|
@ -2304,6 +2830,10 @@ nsParser::OnStopRequest(nsIRequest *request, nsISupports* aContext,
|
|||
{
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
if (mSpeculativeScriptThread) {
|
||||
mSpeculativeScriptThread->StopParsing(PR_FALSE);
|
||||
}
|
||||
|
||||
if (eOnStart == mParserContext->mStreamListenerState) {
|
||||
// If you're here, then OnDataAvailable() never got called. Prior
|
||||
// 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);
|
||||
|
||||
mParserContext->mNumConsumed = 0;
|
||||
|
||||
WillTokenize(aIsFinalChunk);
|
||||
while (NS_SUCCEEDED(result)) {
|
||||
mParserContext->mScanner->Mark();
|
||||
mParserContext->mNumConsumed += mParserContext->mScanner->Mark();
|
||||
result = theTokenizer->ConsumeToken(*mParserContext->mScanner,
|
||||
flushTokens);
|
||||
if (NS_FAILED(result)) {
|
||||
|
@ -2436,7 +2968,7 @@ nsresult nsParser::Tokenize(PRBool aIsFinalChunk)
|
|||
// Flush tokens on seeing </SCRIPT> -- Ref: Bug# 22485 --
|
||||
// Also remember to update the marked position.
|
||||
mFlags |= NS_PARSER_FLAG_FLUSH_TOKENS;
|
||||
mParserContext->mScanner->Mark();
|
||||
mParserContext->mNumConsumed += mParserContext->mScanner->Mark();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -95,6 +95,7 @@ class nsICharsetAlias;
|
|||
class nsIDTD;
|
||||
class nsScanner;
|
||||
class nsIProgressEventSink;
|
||||
class nsSpeculativeScriptThread;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning( disable : 4275 )
|
||||
|
@ -406,7 +407,9 @@ class nsParser : public nsIParser,
|
|||
* @return
|
||||
*/
|
||||
nsresult DidBuildModel(nsresult anErrorCode);
|
||||
|
||||
|
||||
void SpeculativelyParse();
|
||||
|
||||
private:
|
||||
|
||||
/*******************************************
|
||||
|
@ -457,6 +460,7 @@ protected:
|
|||
nsCOMPtr<nsIRequestObserver> mObserver;
|
||||
nsCOMPtr<nsIContentSink> mSink;
|
||||
nsIRunnable* mContinueEvent; // weak ref
|
||||
nsRefPtr<nsSpeculativeScriptThread> mSpeculativeScriptThread;
|
||||
|
||||
nsCOMPtr<nsIParserFilter> mParserFilter;
|
||||
nsTokenAllocator mTokenAllocator;
|
||||
|
|
|
@ -146,16 +146,15 @@ nsScanner::nsScanner(nsString& aFilename,PRBool aCreateStream,
|
|||
SetDocumentCharset(aCharset, 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 res;
|
||||
nsresult nsScanner::SetDocumentCharset(const nsACString& aCharset , PRInt32 aSource)
|
||||
{
|
||||
if (aSource < mCharsetSource) // priority is lower the the current one , just
|
||||
return NS_OK;
|
||||
|
||||
nsICharsetAlias* calias = nsParser::GetCharsetAliasService();
|
||||
NS_ASSERTION(calias, "Must have the charset alias service!");
|
||||
|
||||
nsresult res = NS_OK;
|
||||
if (!mCharset.IsEmpty())
|
||||
{
|
||||
PRBool same;
|
||||
|
@ -185,17 +184,8 @@ nsresult nsScanner::SetDocumentCharset(const nsACString& aCharset , PRInt32 aSou
|
|||
NS_ASSERTION(nsParser::GetCharsetConverterManager(),
|
||||
"Must have the charset converter manager!");
|
||||
|
||||
nsIUnicodeDecoder * decoder = nsnull;
|
||||
res = nsParser::GetCharsetConverterManager()->
|
||||
GetUnicodeDecoderRaw(mCharset.get(), &decoder);
|
||||
if(NS_SUCCEEDED(res) && (nsnull != decoder))
|
||||
{
|
||||
NS_IF_RELEASE(mUnicodeDecoder);
|
||||
|
||||
mUnicodeDecoder = decoder;
|
||||
}
|
||||
|
||||
return res;
|
||||
return nsParser::GetCharsetConverterManager()->
|
||||
GetUnicodeDecoderRaw(mCharset.get(), getter_AddRefs(mUnicodeDecoder));
|
||||
}
|
||||
|
||||
|
||||
|
@ -213,8 +203,6 @@ nsScanner::~nsScanner() {
|
|||
}
|
||||
|
||||
MOZ_COUNT_DTOR(nsScanner);
|
||||
|
||||
NS_IF_RELEASE(mUnicodeDecoder);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -244,14 +232,21 @@ void nsScanner::RewindToMark(void){
|
|||
* @param
|
||||
* @return
|
||||
*/
|
||||
void nsScanner::Mark() {
|
||||
PRInt32 nsScanner::Mark() {
|
||||
PRInt32 distance = 0;
|
||||
if (mSlidingBuffer) {
|
||||
nsScannerIterator oldStart;
|
||||
mSlidingBuffer->BeginReading(oldStart);
|
||||
|
||||
distance = Distance(oldStart, mCurrentPosition);
|
||||
|
||||
mSlidingBuffer->DiscardPrefix(mCurrentPosition);
|
||||
mSlidingBuffer->BeginReading(mCurrentPosition);
|
||||
mMarkPosition = mCurrentPosition;
|
||||
}
|
||||
|
||||
return distance;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Insert data to our underlying input buffer as
|
||||
|
|
|
@ -207,7 +207,7 @@ class nsScanner {
|
|||
* @param
|
||||
* @return
|
||||
*/
|
||||
void Mark(void);
|
||||
PRInt32 Mark(void);
|
||||
|
||||
/**
|
||||
* Resets current offset position of input stream to marked position.
|
||||
|
@ -338,8 +338,11 @@ class nsScanner {
|
|||
PRInt32 mFirstNonWhitespacePosition;
|
||||
PRInt32 mCharsetSource;
|
||||
nsCString mCharset;
|
||||
nsIUnicodeDecoder *mUnicodeDecoder;
|
||||
nsCOMPtr<nsIUnicodeDecoder> mUnicodeDecoder;
|
||||
nsParser *mParser;
|
||||
|
||||
private:
|
||||
nsScanner &operator =(const nsScanner &); // Not implemented.
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
Загрузка…
Ссылка в новой задаче