--- a/content/base/src/nsScriptLoader.cpp
+++ b/content/base/src/nsScriptLoader.cpp
@@ -91,16 +91,21 @@ public:
{
mElement->ScriptAvailable(aResult, mElement, mIsInline, mURI, mLineNo);
}
void FireScriptEvaluated(nsresult aResult)
{
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?
PRPackedBool mIsInline; // Is the script inline or loaded?
nsString mScriptText; // Holds script for loaded scripts
PRUint32 mJSVersion;
nsCOMPtr<nsIURI> mURI;
PRInt32 mLineNo;
@@ -185,16 +190,82 @@ IsScriptEventHandler(nsIScriptElement *a
return PR_TRUE;
}
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)
{
// We need a document to evaluate scripts.
NS_ENSURE_TRUE(mDocument, NS_ERROR_FAILURE);
// Check to see if scripts has been turned off.
if (!mEnabled || !mDocument->IsScriptEnabled()) {
return NS_ERROR_NOT_AVAILABLE;
@@ -371,84 +442,65 @@ nsScriptLoader::ProcessScriptElement(nsI
!nsContentUtils::IsChromeDoc(mDocument)) {
NS_WARNING("Untrusted language called from non-chrome - ignored");
return NS_ERROR_NOT_AVAILABLE;
}
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;
request->mURI = mDocument->GetDocumentURI();
request->mLineNo = aElement->GetScriptLineNumber();
// If we've got existing pending requests, add ourselves
@@ -809,18 +861,21 @@ nsScriptLoader::OnStreamComplete(nsIStre
{
nsScriptLoadRequest* request = static_cast<nsScriptLoadRequest*>(aContext);
NS_ASSERTION(request, "null request in stream complete handler");
NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
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
ProcessPendingRequests();
return NS_OK;
}
@@ -855,32 +910,40 @@ nsScriptLoader::PrepareLoadedRequest(nsS
return NS_ERROR_NOT_AVAILABLE;
}
}
nsCOMPtr<nsIChannel> channel = do_QueryInterface(req);
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);
NS_ENSURE_SUCCESS(rv, rv);
if (!ShouldExecuteScript(mDocument, channel)) {
return NS_ERROR_NOT_AVAILABLE;
}
}
// This assertion could fire errorously if we ran out of memory when
// 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
aRequest->mLoading = PR_FALSE;
return NS_OK;
}
@@ -913,14 +976,38 @@ nsScriptLoader::ShouldExecuteScript(nsID
rv = channelPrincipal->Subsumes(docPrincipal, &subsumes);
return NS_SUCCEEDED(rv) && subsumes;
}
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;
+}
--- a/content/base/src/nsScriptLoader.h
+++ b/content/base/src/nsScriptLoader.h
@@ -200,18 +200,33 @@ public:
* Stops defering scripts and immediately processes the mDeferredRequests
* queue.
*
* WARNING: This function will syncronously execute content scripts, so be
* prepared that the world might change around you.
*/
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
* requests.
*/
virtual void ProcessPendingRequestsAsync();
/**
* If true, the loader is ready to execute scripts, and so are all its
@@ -248,16 +263,35 @@ protected:
const PRUint8* aString);
// Returns the first pending (non deferred) request
nsScriptLoadRequest* GetFirstPendingRequest();
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;
PRUint32 mBlockerCount;
PRPackedBool mEnabled;
PRPackedBool mDeferEnabled;
};
--- a/parser/htmlparser/src/CParserContext.cpp
+++ b/parser/htmlparser/src/CParserContext.cpp
@@ -58,17 +58,17 @@ CParserContext::CParserContext(nsScanner
mScanner(aScanner),
mDTDMode(eDTDMode_unknown),
mStreamListenerState(eNone),
mContextType(eCTNone),
mAutoDetectStatus(aStatus),
mParserCommand(aCommand),
mMultipart(PR_TRUE),
mCopyUnused(aCopyUnused),
- mTransferBufferSize(eTransferBufferSize)
+ mNumConsumed(0)
{
MOZ_COUNT_CTOR(CParserContext);
}
CParserContext::~CParserContext()
{
// It's ok to simply ingore the PrevContext.
MOZ_COUNT_DTOR(CParserContext);
--- a/parser/htmlparser/src/CParserContext.h
+++ b/parser/htmlparser/src/CParserContext.h
@@ -55,54 +55,51 @@
#include "nsAutoPtr.h"
/**
* Note that the parser is given FULL access to all
* data in a parsercontext. Hey, that what it's for!
*/
class CParserContext {
-
public:
-
- enum {eTransferBufferSize=4096};
- enum eContextType {eCTNone,eCTURL,eCTString,eCTStream};
+ 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,
nsIContentSink* aSink,
nsITokenizer*& aTokenizer);
void SetMimeType(const nsACString& aMimeType);
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
--- a/parser/htmlparser/src/nsParser.cpp
+++ b/parser/htmlparser/src/nsParser.cpp
@@ -1,10 +1,10 @@
/* -*- 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
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
@@ -47,25 +47,33 @@
#include "nsIChannel.h"
#include "nsICachingChannel.h"
#include "nsICacheEntryDescriptor.h"
#include "nsICharsetAlias.h"
#include "nsICharsetConverterManager.h"
#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"
#include "nsExpatDriver.h"
#include "nsIServiceManager.h"
#include "nsICategoryManager.h"
#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"
#endif
#define NS_PARSER_FLAG_PARSER_ENABLED 0x00000002
#define NS_PARSER_FLAG_OBSERVERS_ENABLED 0x00000004
#define NS_PARSER_FLAG_PENDING_CONTINUE_EVENT 0x00000008
@@ -150,16 +158,488 @@ public:
{
mParser->HandleParserContinueEvent(this);
return NS_OK;
}
};
//-------------- 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;
/**
* This gets called when the htmlparser module is initialized.
*/
// static
nsresult
@@ -316,16 +796,20 @@ nsParser::Cleanup()
delete mParserContext;
mParserContext = pc;
}
// It should not be possible for this flag to be set when we are getting
// 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)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsParser)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mSink)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mObserver)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
@@ -1002,16 +1486,36 @@ nsParser::DidBuildModel(nsresult anError
//Ref. to bug 61462.
mParserContext->mRequest = 0;
}
}
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,
* pushing the current one to the next position.
*
* @param ptr to new context
*/
void
@@ -1102,16 +1606,19 @@ nsParser::Terminate(void)
mInternalState = result = NS_ERROR_HTMLPARSER_STOPPARSING;
// CancelParsingEvents must be called to avoid leaking the nsParser object
// @see bug 108049
// If NS_PARSER_FLAG_PENDING_CONTINUE_EVENT is set then CancelParsingEvents
// 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
// the effect of making DidBuildModel a no-op, meaning that we never call
// our sink's DidBuildModel and break the reference cycle, causing a leak.
// Since we're getting terminated, we manually clean up our context stack.
while (mParserContext && mParserContext->mPrevContext) {
CParserContext *prev = mParserContext->mPrevContext;
@@ -1158,16 +1665,20 @@ nsParser::ContinueInterruptedParsing()
nsCOMPtr<nsIParser> kungFuDeathGrip(this);
#ifdef DEBUG
if (!(mFlags & NS_PARSER_FLAG_PARSER_ENABLED)) {
NS_WARNING("Don't call ContinueInterruptedParsing on a blocked parser.");
}
#endif
+ if (mSpeculativeScriptThread) {
+ mSpeculativeScriptThread->StopParsing(PR_FALSE);
+ }
+
PRBool isFinalChunk = mParserContext &&
mParserContext->mStreamListenerState == eOnStop;
result = ResumeParse(PR_TRUE, isFinalChunk); // Ref. bug 57999
if (result != NS_OK) {
result=mInternalState;
}
@@ -1290,16 +1801,17 @@ nsParser::SetCanInterrupt(PRBool aCanInt
NS_IMETHODIMP
nsParser::Parse(nsIURI* aURL,
nsIRequestObserver* aListener,
void* aKey,
nsDTDMode aMode)
{
NS_PRECONDITION(aURL, "Error: Null URL given");
+ NS_ASSERTION(!mSpeculativeScriptThread, "Can't reuse a parser like this");
nsresult result=kBadURL;
mObserver = aListener;
if (aURL) {
nsCAutoString spec;
nsresult rv = aURL->GetSpec(spec);
if (rv != NS_OK) {
@@ -1357,16 +1869,20 @@ nsParser::Parse(const nsAString& aSource
// Nothing is being passed to the parser so return
// immediately. mUnusedInput will get processed when
// some data is actually passed in.
// But if this is the last call, make sure to finish up
// stuff correctly.
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)
mCommand = eViewFragment;
// Maintain a reference to ourselves so we don't go away
// till we're completely done.
nsCOMPtr<nsIParser> kungFuDeathGrip(this);
@@ -1477,16 +1993,18 @@ nsParser::ParseFragment(const nsAString&
nsresult result = NS_OK;
nsAutoString theContext;
PRUint32 theCount = aTagStack.Length();
PRUint32 theIndex = 0;
// 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]);
theContext.AppendLiteral(">");
}
if (theCount == 0) {
// Ensure that the buffer is not empty. Because none of the DTDs care
@@ -1617,16 +2135,18 @@ nsParser::ResumeParse(PRBool allowIterat
{
nsresult result = NS_OK;
if ((mFlags & NS_PARSER_FLAG_PARSER_ENABLED) &&
mInternalState != NS_ERROR_HTMLPARSER_STOPPARSING) {
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;
return result;
}
if (mParserContext->mDTD) {
mParserContext->mDTD->WillResumeParse(mSink);
@@ -1666,16 +2186,17 @@ nsParser::ResumeParse(PRBool allowIterat
// If we're told to block the parser, we disable all further parsing
// (and cache any data coming in) until the parser is re-enabled.
if (NS_ERROR_HTMLPARSER_BLOCK == result) {
if (mParserContext->mDTD) {
mParserContext->mDTD->WillInterruptParse(mSink);
}
BlockParser();
+ SpeculativelyParse();
return NS_OK;
}
if (NS_ERROR_HTMLPARSER_STOPPARSING == result) {
// Note: Parser Terminate() calls DidBuildModel.
if (mInternalState != NS_ERROR_HTMLPARSER_STOPPARSING) {
DidBuildModel(mStreamStatus);
mInternalState = result;
}
@@ -2255,16 +2776,21 @@ nsParser::OnDataAvailable(nsIRequest *re
while (theContext && theContext->mRequest != request) {
theContext = theContext->mPrevContext;
}
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;
theContext->mScanner->EndReading(iter);
theContext->mScanner->SetPosition(iter, PR_TRUE);
}
}
@@ -2299,16 +2825,20 @@ nsParser::OnDataAvailable(nsIRequest *re
* has been collected from the net.
*/
nsresult
nsParser::OnStopRequest(nsIRequest *request, nsISupports* aContext,
nsresult status)
{
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
// have existed. Everybody can live with an empty input stream, so
// just resume parsing.
rv = ResumeParse(PR_TRUE, PR_TRUE);
}
@@ -2412,36 +2942,38 @@ nsresult nsParser::Tokenize(PRBool aIsFi
// Reset since the tokens have been flushed.
mFlags &= ~NS_PARSER_FLAG_FLUSH_TOKENS;
}
PRBool flushTokens = PR_FALSE;
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)) {
mParserContext->mScanner->RewindToMark();
if (kEOF == result){
break;
}
if (NS_ERROR_HTMLPARSER_STOPPARSING == result) {
result = Terminate();
break;
}
} else if (flushTokens && (mFlags & NS_PARSER_FLAG_OBSERVERS_ENABLED)) {
// I added the extra test of NS_PARSER_FLAG_OBSERVERS_ENABLED to fix Bug# 23931.
// 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;
}
}
DidTokenize(aIsFinalChunk);
MOZ_TIMER_STOP(mTokenizeTime);
} else {
result = mInternalState = NS_ERROR_HTMLPARSER_BADTOKENIZER;
--- a/parser/htmlparser/src/nsParser.h
+++ b/parser/htmlparser/src/nsParser.h
@@ -90,16 +90,17 @@
#include "nsIUnicharStreamListener.h"
#include "nsCycleCollectionParticipant.h"
class nsICharsetConverterManager;
class nsICharsetAlias;
class nsIDTD;
class nsScanner;
class nsIProgressEventSink;
+class nsSpeculativeScriptThread;
#ifdef _MSC_VER
#pragma warning( disable : 4275 )
#endif
class nsParser : public nsIParser,
public nsIStreamListener{
@@ -401,17 +402,19 @@ class nsParser : public nsIParser,
/**
*
* @update gess5/18/98
* @param
* @return
*/
nsresult DidBuildModel(nsresult anErrorCode);
-
+
+ void SpeculativelyParse();
+
private:
/*******************************************
These are the tokenization methods...
*******************************************/
/**
* Part of the code sandwich, this gets called right before
@@ -452,16 +455,17 @@ protected:
// And now, some data members...
//*********************************************
CParserContext* mParserContext;
nsCOMPtr<nsIRequestObserver> mObserver;
nsCOMPtr<nsIContentSink> mSink;
nsIRunnable* mContinueEvent; // weak ref
+ nsRefPtr<nsSpeculativeScriptThread> mSpeculativeScriptThread;
nsCOMPtr<nsIParserFilter> mParserFilter;
nsTokenAllocator mTokenAllocator;
eParserCommands mCommand;
nsresult mInternalState;
PRInt32 mStreamStatus;
PRInt32 mCharsetSource;
--- a/parser/htmlparser/src/nsScanner.cpp
+++ b/parser/htmlparser/src/nsScanner.cpp
@@ -141,26 +141,25 @@ nsScanner::nsScanner(nsString& aFilename
mFirstNonWhitespacePosition = -1;
mCountRemaining = 0;
mUnicodeDecoder = 0;
mCharsetSource = kCharsetUninitialized;
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;
res = calias->Equals(aCharset, mCharset, &same);
if(NS_SUCCEEDED(res) && same)
{
return NS_OK; // no difference, don't change it
}
@@ -180,27 +179,18 @@ nsresult nsScanner::SetDocumentCharset(c
mCharset.Assign(charsetName);
}
mCharsetSource = aSource;
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));
}
/**
* default destructor
*
* @update gess 3/25/98
* @param
@@ -208,18 +198,16 @@ nsresult nsScanner::SetDocumentCharset(c
*/
nsScanner::~nsScanner() {
if (mSlidingBuffer) {
delete mSlidingBuffer;
}
MOZ_COUNT_DTOR(nsScanner);
-
- NS_IF_RELEASE(mUnicodeDecoder);
}
/**
* Resets current offset position of input stream to marked position.
* This allows us to back up to this point if the need should arise,
* such as when tokenization gets interrupted.
* NOTE: IT IS REALLY BAD FORM TO CALL RELEASE WITHOUT CALLING MARK FIRST!
*
@@ -239,24 +227,31 @@ void nsScanner::RewindToMark(void){
* Records current offset position in input stream. This allows us
* to back up to this point if the need should arise, such as when
* tokenization gets interrupted.
*
* @update gess 7/29/98
* @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
* if it were read from an input stream.
*
* @update harishd 01/12/99
* @return error code
*/
--- a/parser/htmlparser/src/nsScanner.h
+++ b/parser/htmlparser/src/nsScanner.h
@@ -202,17 +202,17 @@ class nsScanner {
* Records current offset position in input stream. This allows us
* to back up to this point if the need should arise, such as when
* tokenization gets interrupted.
*
* @update gess 5/12/98
* @param
* @return
*/
- void Mark(void);
+ PRInt32 Mark(void);
/**
* Resets current offset position of input stream to marked position.
* This allows us to back up to this point if the need should arise,
* such as when tokenization gets interrupted.
* NOTE: IT IS REALLY BAD FORM TO CALL RELEASE WITHOUT CALLING MARK FIRST!
*
* @update gess 5/12/98
@@ -333,15 +333,18 @@ class nsScanner {
nsScannerIterator mEndPosition; // The current end of the scanner buffer
nsString mFilename;
PRUint32 mCountRemaining; // The number of bytes still to be read
// from the scanner buffer
PRPackedBool mIncremental;
PRInt32 mFirstNonWhitespacePosition;
PRInt32 mCharsetSource;
nsCString mCharset;
- nsIUnicodeDecoder *mUnicodeDecoder;
+ nsCOMPtr<nsIUnicodeDecoder> mUnicodeDecoder;
nsParser *mParser;
+
+ private:
+ nsScanner &operator =(const nsScanner &); // Not implemented.
};
#endif