Added code to interrupt the parser's processing of tokens if a threshold is exceeded to improve interactivity during long page loads. Turned OFF by default. Can be enabled through a pref. bug 76722 r=harishd@netscape.com,rickg@netscape.com sr=vidur@netscape.com,attinasi@netscape.com a=chofmann@netscape.com

This commit is contained in:
kmcclusk%netscape.com 2001-06-21 02:06:23 +00:00
Родитель 208706e674
Коммит e3ef64bd96
21 изменённых файлов: 943 добавлений и 513 удалений

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

@ -102,6 +102,10 @@ public:
NS_IMETHOD DoFragment(PRBool aFlag);
NS_IMETHOD BeginContext(PRInt32 aPosition) { return NS_OK; }
NS_IMETHOD EndContext(PRInt32 aPosition) { return NS_OK; }
NS_IMETHOD WillProcessTokens(void) { return NS_OK; }
NS_IMETHOD DidProcessTokens(void) { return NS_OK; }
NS_IMETHOD WillProcessAToken(void) { return NS_OK; }
NS_IMETHOD DidProcessAToken(void) { return NS_OK; }
// nsIHTMLToTextSink
NS_IMETHOD Initialize(nsAWritableString* aOutString,

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

@ -170,6 +170,35 @@ static PRLogModuleInfo* gSinkLogModuleInfo;
#define NS_SINK_FLAG_SCRIPT_ENABLED 0x8
#define NS_SINK_FLAG_FRAMES_ENABLED 0x10
#define NS_SINK_FLAG_CAN_INTERRUPT_PARSER 0x20 //Interrupt parsing when mMaxTokenProcessingTime is exceeded
// Timer used to determine how long the content sink
// spends processing a tokens
class nsDelayTimer
{
public:
void Start(void) {
mStart = PR_IntervalToMicroseconds(PR_IntervalNow());
}
// Determine if the current time - start time is greater
// then aMaxDelayInMicroseconds
PRBool HasExceeded(PRUint32 aMaxDelayInMicroseconds) {
PRUint32 stop = PR_IntervalToMicroseconds(PR_IntervalNow());
if ((stop - mStart) > aMaxDelayInMicroseconds) {
return PR_TRUE;
}
return PR_FALSE;
}
private:
PRUint32 mStart;
};
class SinkContext;
@ -209,6 +238,11 @@ public:
NS_IMETHOD AddComment(const nsIParserNode& aNode);
NS_IMETHOD AddProcessingInstruction(const nsIParserNode& aNode);
NS_IMETHOD AddDocTypeDecl(const nsIParserNode& aNode, PRInt32 aMode=0);
NS_IMETHOD WillProcessTokens(void);
NS_IMETHOD DidProcessTokens(void);
NS_IMETHOD WillProcessAToken(void);
NS_IMETHOD DidProcessAToken(void);
// nsIHTMLContentSink
NS_IMETHOD BeginContext(PRInt32 aID);
@ -356,6 +390,7 @@ public:
nsSupportsArray mScriptElements;
PRBool mParserBlocked;
PRBool mNeedToBlockParser;
nsCOMPtr<nsIRequest> mDummyParserRequest;
nsCString mRef;
@ -368,6 +403,10 @@ public:
PRInt32 mInMonolithicContainer;
PRUint32 mFlags;
// Can interrupt parsing members
nsDelayTimer mDelayTimer;
PRInt32 mMaxTokenProcessingTime; // Interrupt parsing during token procesing after # of microseconds
void StartLayout();
void ScrollToRef();
@ -410,13 +449,134 @@ public:
nsIContent* aChildContent,
PRInt32 aIndexInContainer);
PRBool IsMonolithicContainer(nsHTMLTag aTag);
// CanInterrupt parsing related routines
nsresult AddDummyParserRequest(void);
nsresult RemoveDummyParserRequest(void);
#ifdef NS_DEBUG
void ForceReflow();
#endif
MOZ_TIMER_DECLARE(mWatch) // Measures content model creation time for current document
};
//----------------------------------------------------------------------
//
// DummyParserRequest
//
// This is a dummy request implementation that we add to the document's load
// group. It ensures that EndDocumentLoad() in the docshell doesn't fire
// before we've finished all of parsing and tokenizing of the document.
//
class DummyParserRequest : public nsIChannel
{
protected:
DummyParserRequest(nsIHTMLContentSink* aSink);
virtual ~DummyParserRequest();
static PRInt32 gRefCnt;
static nsIURI* gURI;
nsCOMPtr<nsILoadGroup> mLoadGroup;
nsIHTMLContentSink* mSink; // Weak reference
public:
static nsresult
Create(nsIRequest** aResult, nsIHTMLContentSink* aSink);
NS_DECL_ISUPPORTS
// nsIRequest
NS_IMETHOD GetName(PRUnichar* *result) {
*result = ToNewUnicode(NS_LITERAL_STRING("about:layout-dummy-request"));
return NS_OK;
}
NS_IMETHOD IsPending(PRBool *_retval) { *_retval = PR_TRUE; return NS_OK; }
NS_IMETHOD GetStatus(nsresult *status) { *status = NS_OK; return NS_OK; }
NS_IMETHOD Cancel(nsresult status);
NS_IMETHOD Suspend(void) { return NS_OK; }
NS_IMETHOD Resume(void) { return NS_OK; }
// nsIChannel
NS_IMETHOD GetOriginalURI(nsIURI* *aOriginalURI) { *aOriginalURI = gURI; NS_ADDREF(*aOriginalURI); return NS_OK; }
NS_IMETHOD SetOriginalURI(nsIURI* aOriginalURI) { gURI = aOriginalURI; NS_ADDREF(gURI); return NS_OK; }
NS_IMETHOD GetURI(nsIURI* *aURI) { *aURI = gURI; NS_ADDREF(*aURI); return NS_OK; }
NS_IMETHOD SetURI(nsIURI* aURI) { gURI = aURI; NS_ADDREF(gURI); return NS_OK; }
NS_IMETHOD Open(nsIInputStream **_retval) { *_retval = nsnull; return NS_OK; }
NS_IMETHOD AsyncOpen(nsIStreamListener *listener, nsISupports *ctxt) { return NS_OK; }
NS_IMETHOD GetLoadFlags(nsLoadFlags *aLoadFlags) { *aLoadFlags = nsIRequest::LOAD_NORMAL; return NS_OK; }
NS_IMETHOD SetLoadFlags(nsLoadFlags aLoadFlags) { return NS_OK; }
NS_IMETHOD GetOwner(nsISupports * *aOwner) { *aOwner = nsnull; return NS_OK; }
NS_IMETHOD SetOwner(nsISupports * aOwner) { return NS_OK; }
NS_IMETHOD GetLoadGroup(nsILoadGroup * *aLoadGroup) { *aLoadGroup = mLoadGroup; NS_IF_ADDREF(*aLoadGroup); return NS_OK; }
NS_IMETHOD SetLoadGroup(nsILoadGroup * aLoadGroup) { mLoadGroup = aLoadGroup; return NS_OK; }
NS_IMETHOD GetNotificationCallbacks(nsIInterfaceRequestor * *aNotificationCallbacks) { *aNotificationCallbacks = nsnull; return NS_OK; }
NS_IMETHOD SetNotificationCallbacks(nsIInterfaceRequestor * aNotificationCallbacks) { return NS_OK; }
NS_IMETHOD GetSecurityInfo(nsISupports * *aSecurityInfo) { *aSecurityInfo = nsnull; return NS_OK; }
NS_IMETHOD GetContentType(char * *aContentType) { *aContentType = nsnull; return NS_OK; }
NS_IMETHOD SetContentType(const char * aContentType) { return NS_OK; }
NS_IMETHOD GetContentLength(PRInt32 *aContentLength) { return NS_OK; }
NS_IMETHOD SetContentLength(PRInt32 aContentLength) { return NS_OK; }
};
PRInt32 DummyParserRequest::gRefCnt;
nsIURI* DummyParserRequest::gURI;
NS_IMPL_ADDREF(DummyParserRequest);
NS_IMPL_RELEASE(DummyParserRequest);
NS_IMPL_QUERY_INTERFACE2(DummyParserRequest, nsIRequest, nsIChannel);
nsresult
DummyParserRequest::Create(nsIRequest** aResult, nsIHTMLContentSink* aSink)
{
DummyParserRequest* request = new DummyParserRequest(aSink);
if (!request)
return NS_ERROR_OUT_OF_MEMORY;
return request->QueryInterface(NS_GET_IID(nsIRequest), (void**) aResult);
}
DummyParserRequest::DummyParserRequest(nsIHTMLContentSink* aSink)
{
NS_INIT_REFCNT();
if (gRefCnt++ == 0) {
nsresult rv;
rv = NS_NewURI(&gURI, "about:parser-dummy-request", nsnull);
NS_ASSERTION(NS_SUCCEEDED(rv), "unable to create about:parser-dummy-request");
}
mSink = aSink;
}
DummyParserRequest::~DummyParserRequest()
{
if (--gRefCnt == 0) {
NS_IF_RELEASE(gURI);
}
}
NS_IMETHODIMP
DummyParserRequest::Cancel(nsresult status)
{
// Cancel parser
nsresult rv = NS_OK;
HTMLContentSink* sink = NS_STATIC_CAST(HTMLContentSink*, mSink);
if ((sink) && (sink->mParser)) {
sink->mParser->CancelParsingEvents();
}
return rv;
}
class SinkContext {
public:
SinkContext(HTMLContentSink* aSink);
@ -2151,6 +2311,7 @@ HTMLContentSink::HTMLContentSink() {
mFlags=0;
mNeedToBlockParser = PR_FALSE;
mParserBlocked = PR_FALSE;
mDummyParserRequest = nsnull;
}
HTMLContentSink::~HTMLContentSink()
@ -2329,16 +2490,42 @@ HTMLContentSink::Init(nsIDocument* aDoc,
// The mNotificationInterval has a dramatic effect on how long it
// takes to initially display content for slow connections.
// The current value of 1/4 of second provides good
// The current value provides good
// incremental display of content without causing an increase
// in page load time. If this value is set below 1/10 of second
// it starts to impact page load performance.
// see bugzilla bug 72138 for more info.
mNotificationInterval = 250000;
mNotificationInterval = 120000;
if (prefs) {
prefs->GetIntPref("content.notify.interval", &mNotificationInterval);
}
// The mMaxTokenProcessingTime controls how long we stay away from
// the event loop when processing token. A lower value
// makes the app more responsive, but may increase page load time.
// The content sink mNotificationInterval gates how frequently the content
// is processed so it will also affect how interactive the app is during
// page load also. The mNotification prevents contents flushes from happening
// too frequently. while mMaxTokenProcessingTime prevents flushes from happening
// too infrequently.
// The current ratio of 3 to 1 was determined to be the lowest mMaxTokenProcessingTime
// which does not impact page load performance.
// See bugzilla bug 76722 for details.
mMaxTokenProcessingTime = mNotificationInterval * 3;
PRBool enableInterruptParsing = PR_FALSE;
if (prefs) {
prefs->GetBoolPref("content.interrupt.parsing", &enableInterruptParsing);
prefs->GetIntPref("content.max.tokenizing.time", &mMaxTokenProcessingTime);
}
if (enableInterruptParsing) {
mFlags |= NS_SINK_FLAG_CAN_INTERRUPT_PARSER;
}
// Changed from 8192 to greatly improve page loading performance on large
// pages. See bugzilla bug 77540.
mMaxTextRun = 8191;
@ -2420,6 +2607,16 @@ HTMLContentSink::Init(nsIDocument* aDoc,
NS_IMETHODIMP
HTMLContentSink::WillBuildModel(void)
{
if (mFlags & NS_SINK_FLAG_CAN_INTERRUPT_PARSER) {
nsresult rv = AddDummyParserRequest();
NS_ASSERTION(NS_SUCCEEDED(rv), "Adding dummy parser request failed");
if (NS_FAILED(rv)) {
// Don't return the error result, just reset flag which indicates that it can
// interrupt parsing. If AddDummyParserRequests fails it should not affect
// WillBuildModel.
mFlags &= ~NS_SINK_FLAG_CAN_INTERRUPT_PARSER;
}
}
// Notify document that the load is beginning
mDocument->BeginLoad();
return NS_OK;
@ -2428,6 +2625,7 @@ HTMLContentSink::WillBuildModel(void)
NS_IMETHODIMP
HTMLContentSink::DidBuildModel(PRInt32 aQualityLevel)
{
// NRA Dump stopwatch stop info here
#ifdef MOZ_PERF_METRICS
MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::DidBuildModel(), this=%p\n", this));
@ -2500,6 +2698,14 @@ HTMLContentSink::DidBuildModel(PRInt32 aQualityLevel)
// Drop our reference to the parser to get rid of a circular
// reference.
NS_IF_RELEASE(mParser);
if (mFlags & NS_SINK_FLAG_CAN_INTERRUPT_PARSER) {
// Note: Don't return value from RemoveDummyParserRequest,
// If RemoveDummyParserRequests fails it should not affect
// DidBuildModel. The remove can fail if the parser request
// was already removed by a DummyParserRequest::Cancel
RemoveDummyParserRequest();
}
return NS_OK;
}
@ -3111,7 +3317,7 @@ HTMLContentSink::CloseMap(const nsIParserNode& aNode)
NS_IMETHODIMP
HTMLContentSink::GetPref(PRInt32 aTag,PRBool& aPref) {
nsHTMLTag theHTMLTag = nsHTMLTag(aTag);
if (theHTMLTag == eHTMLTag_script) {
aPref = mFlags & NS_SINK_FLAG_SCRIPT_ENABLED;
}
@ -3494,6 +3700,33 @@ HTMLContentSink::AddDocTypeDecl(const nsIParserNode& aNode, PRInt32 aMode)
}
NS_IMETHODIMP
HTMLContentSink::WillProcessTokens(void) {
if (mFlags & NS_SINK_FLAG_CAN_INTERRUPT_PARSER) {
mDelayTimer.Start();
}
return NS_OK;
}
NS_IMETHODIMP
HTMLContentSink::DidProcessTokens(void) {
return NS_OK;
}
NS_IMETHODIMP
HTMLContentSink::WillProcessAToken(void) {
return NS_OK;
}
NS_IMETHODIMP
HTMLContentSink::DidProcessAToken(void) {
if ((mFlags & NS_SINK_FLAG_CAN_INTERRUPT_PARSER) && (mDelayTimer.HasExceeded(mMaxTokenProcessingTime))) {
return NS_ERROR_HTMLPARSER_INTERRUPTED;
}
return NS_OK;
}
void
HTMLContentSink::StartLayout()
{
@ -4881,3 +5114,62 @@ HTMLContentSink::DumpContentModel()
}
return result;
}
// If the content sink can interrupt the parser (@see mCanInteruptParsing)
// then it needs to schedule a dummy parser request to delay the document
// from firing onload handlers and other document done actions until all of the
// parsing has completed.
nsresult
HTMLContentSink::AddDummyParserRequest(void)
{
nsresult rv = NS_OK;
NS_ASSERTION(nsnull == mDummyParserRequest,"Already have a dummy parser request");
rv = DummyParserRequest::Create(getter_AddRefs(mDummyParserRequest), this);
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsILoadGroup> loadGroup;
if (mDocument) {
rv = mDocument->GetDocumentLoadGroup(getter_AddRefs(loadGroup));
if (NS_FAILED(rv)) return rv;
}
if (loadGroup) {
nsCOMPtr<nsIChannel> channel = do_QueryInterface(mDummyParserRequest);
if (channel) {
rv = channel->SetLoadGroup(loadGroup);
if (NS_FAILED(rv)) return rv;
rv = loadGroup->AddRequest(mDummyParserRequest, nsnull);
if (NS_FAILED(rv)) return rv;
} else {
return NS_ERROR_FAILURE;
}
}
return rv;
}
nsresult
HTMLContentSink::RemoveDummyParserRequest(void)
{
nsresult rv = NS_OK;
nsCOMPtr<nsILoadGroup> loadGroup;
if (mDocument) {
rv = mDocument->GetDocumentLoadGroup(getter_AddRefs(loadGroup));
if (NS_FAILED(rv)) return rv;
}
if (loadGroup && mDummyParserRequest) {
rv = loadGroup->RemoveRequest(mDummyParserRequest, nsnull, NS_OK);
if (NS_FAILED(rv)) {
return rv;
}
mDummyParserRequest = nsnull;
}
return rv;
}

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

@ -96,6 +96,10 @@ public:
NS_IMETHOD OpenMap(const nsIParserNode& aNode);
NS_IMETHOD CloseMap(const nsIParserNode& aNode);
NS_IMETHOD FlushPendingNotifications() { return NS_OK; }
NS_IMETHOD WillProcessTokens(void) { return NS_OK; }
NS_IMETHOD DidProcessTokens(void) { return NS_OK; }
NS_IMETHOD WillProcessAToken(void) { return NS_OK; }
NS_IMETHOD DidProcessAToken(void) { return NS_OK; }
NS_IMETHOD DoFragment(PRBool aFlag);

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

@ -86,6 +86,10 @@ public:
NS_IMETHOD WillResume(void) { return NS_OK; }
NS_IMETHOD SetParser(nsIParser* aParser) { return NS_OK; }
NS_IMETHOD FlushPendingNotifications() { return NS_OK; }
NS_IMETHOD WillProcessTokens(void) { return NS_OK; }
NS_IMETHOD DidProcessTokens(void) { return NS_OK; }
NS_IMETHOD WillProcessAToken(void) { return NS_OK; }
NS_IMETHOD DidProcessAToken(void) { return NS_OK; }
NS_IMETHOD DoFragment(PRBool aFlag);
NS_IMETHOD BeginContext(PRInt32 aPosition){ return NS_OK; }

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

@ -520,8 +520,12 @@ nsresult CNavDTD::BuildModel(nsIParser* aParser,nsITokenizer* aTokenizer,nsIToke
mTokenizer->PushTokenFront(theToken); //this token should get pushed on the context stack.
}
}
mSink->WillProcessTokens();
while(NS_SUCCEEDED(result)){
//Currently nsIHTMLContentSink does nothing with a call to WillProcessAToken.
//mSink->WillProcessAToken();
#if 0
int n=aTokenizer->GetCount();
@ -544,12 +548,34 @@ nsresult CNavDTD::BuildModel(nsIParser* aParser,nsITokenizer* aTokenizer,nsIToke
result=mDTDState;
break;
}
if ((NS_ERROR_HTMLPARSER_INTERRUPTED == mSink->DidProcessAToken())) {
// The content sink has requested that DTD interrupt processing tokens
// So we need to make sure the parser is in a state where it can be
// interrupted.
// The mParser->CanInterrupt will return TRUE if BuildModel was called
// from a place in the parser where it prepared to handle a return value of
// NS_ERROR_HTMLPARSER_INTERRUPTED.
// If the parser has mPrevContext then it may be processing
// Script so we should not allow it to be interrupted.
if ((mParser->CanInterrupt()) &&
(nsnull == mParser->PeekContext()->mPrevContext) &&
(eHTMLTag_unknown==mSkipTarget)) {
result = NS_ERROR_HTMLPARSER_INTERRUPTED;
break;
}
}
}//while
mTokenizer=oldTokenizer;
//Currently nsIHTMLContentSink does nothing with a call to DidProcessATokens().
//mSink->DidProcessTokens();
}
}
}
else result=NS_ERROR_HTMLPARSER_BADTOKENIZER;
return result;
}
@ -2219,7 +2245,6 @@ nsresult CNavDTD::HandleAttributeToken(CToken* aToken) {
return NS_OK;
}
/**
* This method gets called when a script token has been
* encountered in the parse process. n
@ -2236,6 +2261,8 @@ nsresult CNavDTD::HandleScriptToken(const nsIParserNode *aNode) {
nsresult result=AddLeaf(aNode);
mParser->SetCanInterrupt(PR_FALSE);
MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::HandleScriptToken(), this=%p\n", this));
START_TIMER();

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

@ -108,7 +108,6 @@ class nsITokenizer;
class nsCParserNode;
class nsTokenAllocator;
/***************************************************************
Now the main event: CNavDTD.

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

@ -71,6 +71,10 @@ public:
NS_IMETHOD OpenFrameset(const nsIParserNode& aNode);
NS_IMETHOD CloseFrameset(const nsIParserNode& aNode);
NS_IMETHOD GetPref(PRInt32 aTag,PRBool& aPref) { return NS_OK; }
NS_IMETHOD WillProcessTokens(void) { return NS_OK; }
NS_IMETHOD DidProcessTokens(void) { return NS_OK; }
NS_IMETHOD WillProcessAToken(void) { return NS_OK; }
NS_IMETHOD DidProcessAToken(void) { return NS_OK; }
NS_IMETHOD DoFragment(PRBool aFlag);
NS_IMETHOD BeginContext(PRInt32 aPosition);

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

@ -1,234 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape 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/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
#ifndef nsIHTMLContentSink_h___
#define nsIHTMLContentSink_h___
/**
* MODULE NOTES:
* @update gess 4/1/98
*
* This file declares the concrete HTMLContentSink class.
* This class is used during the parsing process as the
* primary interface between the parser and the content
* model.
*
* After the tokenizer completes, the parser iterates over
* the known token list. As the parser identifies valid
* elements, it calls the contentsink interface to notify
* the content model that a new node or child node is being
* created and added to the content model.
*
* The HTMLContentSink interface assumes 4 underlying
* containers: HTML, HEAD, BODY and FRAMESET. Before
* accessing any these, the parser will call the appropriate
* OpennsIHTMLContentSink method: OpenHTML,OpenHead,OpenBody,OpenFrameSet;
* likewise, the ClosensIHTMLContentSink version will be called when the
* parser is done with a given section.
*
* IMPORTANT: The parser may Open each container more than
* once! This is due to the irregular nature of HTML files.
* For example, it is possible to encounter plain text at
* the start of an HTML document (that preceeds the HTML tag).
* Such text is treated as if it were part of the body.
* In such cases, the parser will Open the body, pass the text-
* node in and then Close the body. The body will likely be
* re-Opened later when the actual <BODY> tag has been seen.
*
* Containers within the body are Opened and Closed
* using the OpenContainer(...) and CloseContainer(...) calls.
* It is assumed that the document or contentSink is
* maintaining its state to manage where new content should
* be added to the underlying document.
*
* NOTE: OpenHTML() and OpenBody() may get called multiple times
* in the same document. That's fine, and it doesn't mean
* that we have multiple bodies or HTML's.
*
* NOTE: I haven't figured out how sub-documents (non-frames)
* are going to be handled. Stay tuned.
*/
#include "nsIParserNode.h"
#include "nsIContentSink.h"
#define NS_IHTML_CONTENT_SINK_IID \
{ 0xa6cf9051, 0x15b3, 0x11d2,{0x93, 0x2e, 0x00, 0x80, 0x5f, 0x8a, 0xdd, 0x32}}
#ifdef XP_MAC
#define MAX_REFLOW_DEPTH 75 //setting to 75 to prevent layout from crashing on mac. Bug 55095.
#else
#define MAX_REFLOW_DEPTH 200 //windows and linux (etc) can do much deeper structures.
#endif
class nsIHTMLContentSink : public nsIContentSink {
public:
static const nsIID& GetIID() { static nsIID iid = NS_IHTML_CONTENT_SINK_IID; return iid; }
/**
* This method gets called by the parser when it encounters
* a title tag and wants to set the document title in the sink.
*
* @update 4/1/98 gess
* @param nsString reference to new title value
*/
NS_IMETHOD SetTitle(const nsString& aValue)=0;
/**
* This method is used to open the outer HTML container.
*
* @update 4/1/98 gess
* @param nsIParserNode reference to parser node interface
*/
NS_IMETHOD OpenHTML(const nsIParserNode& aNode)=0;
/**
* This method is used to close the outer HTML container.
*
* @update 4/1/98 gess
* @param nsIParserNode reference to parser node interface
*/
NS_IMETHOD CloseHTML(const nsIParserNode& aNode)=0;
/**
* This method is used to open the only HEAD container.
*
* @update 4/1/98 gess
* @param nsIParserNode reference to parser node interface
*/
NS_IMETHOD OpenHead(const nsIParserNode& aNode)=0;
/**
* This method is used to close the only HEAD container.
*
* @update 4/1/98 gess
* @param nsIParserNode reference to parser node interface
*/
NS_IMETHOD CloseHead(const nsIParserNode& aNode)=0;
/**
* This method is used to open the main BODY container.
*
* @update 4/1/98 gess
* @param nsIParserNode reference to parser node interface
*/
NS_IMETHOD OpenBody(const nsIParserNode& aNode)=0;
/**
* This method is used to close the main BODY container.
*
* @update 4/1/98 gess
* @param nsIParserNode reference to parser node interface
*/
NS_IMETHOD CloseBody(const nsIParserNode& aNode)=0;
/**
* This method is used to open a new FORM container.
*
* @update 4/1/98 gess
* @param nsIParserNode reference to parser node interface
*/
NS_IMETHOD OpenForm(const nsIParserNode& aNode)=0;
/**
* This method is used to close the outer FORM container.
*
* @update 4/1/98 gess
* @param nsIParserNode reference to parser node interface
*/
NS_IMETHOD CloseForm(const nsIParserNode& aNode)=0;
/**
* This method is used to open a new MAP container.
*
* @update 4/1/98 gess
* @param nsIParserNode reference to parser node interface
*/
NS_IMETHOD OpenMap(const nsIParserNode& aNode)=0;
/**
* This method is used to close the MAP container.
*
* @update 4/1/98 gess
* @param nsIParserNode reference to parser node interface
*/
NS_IMETHOD CloseMap(const nsIParserNode& aNode)=0;
/**
* This method is used to open the FRAMESET container.
*
* @update 4/1/98 gess
* @param nsIParserNode reference to parser node interface
*/
NS_IMETHOD OpenFrameset(const nsIParserNode& aNode)=0;
/**
* This method is used to close the FRAMESET container.
*
* @update 4/1/98 gess
* @param nsIParserNode reference to parser node interface
*/
NS_IMETHOD CloseFrameset(const nsIParserNode& aNode)=0;
/**
* This method tells the sink whether or not it is
* encoding an HTML fragment or the whole document.
* By default, the entire document is encoded.
*
* @update 03/14/99 gpk
* @param aFlag set to true if only encoding a fragment
*/
NS_IMETHOD DoFragment(PRBool aFlag)=0;
/**
* This gets called when handling illegal contents, especially
* in dealing with tables. This method creates a new context.
*
* @update 04/04/99 harishd
* @param aPosition - The position from where the new context begins.
*/
NS_IMETHOD BeginContext(PRInt32 aPosition)=0;
/**
* This method terminates any new context that got created by
* BeginContext and switches back to the main context.
*
* @update 04/04/99 harishd
* @param aPosition - Validates the end of a context.
*/
NS_IMETHOD EndContext(PRInt32 aPosition)=0;
/**
* Use this method to retrieve pref. for the tag.
*
* @update 04/11/01 harishd
* @param aTag - Check pref. for this tag.
*/
NS_IMETHOD GetPref(PRInt32 aTag,PRBool& aPref)=0;
};
extern NS_HTMLPARS nsresult NS_NewHTMLNullSink(nsIContentSink** aInstancePtrResult);
#endif /* nsIHTMLContentSink_h___ */

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

@ -110,7 +110,7 @@ public:
virtual PRUint32 GetSize(void)=0;
};
/**
/**
* FOR DEBUG PURPOSE ONLY
*
* Use this interface to query objects that contain content information.
@ -233,6 +233,7 @@ class nsIParser : public nsISupports {
virtual void UnblockParser() =0;
virtual PRBool IsParserEnabled() =0;
virtual PRBool IsComplete() =0;
virtual nsresult Parse(nsIURI* aURL,nsIRequestObserver* aListener = nsnull,PRBool aEnableVerify=PR_FALSE, void* aKey=0,nsDTDMode aMode=eDTDMode_autodetect) = 0;
virtual nsresult Parse(nsIInputStream& aStream, const nsString& aMimeType,PRBool aEnableVerify=PR_FALSE, void* aKey=0,nsDTDMode aMode=eDTDMode_autodetect) = 0;
@ -276,6 +277,18 @@ class nsIParser : public nsISupports {
eParserCommands aCommand,
const nsString* aMimeType=nsnull,
nsDTDMode aDTDMode=eDTDMode_unknown)=0;
/**
* Call this method to cancel any pending parsing events.
* Parsing events may be pending if all of the document's content
* has been passed to the parser but the parser has been interrupted
* because processing the tokens took too long.
*
* @update kmcclusk 05/18/01
* @return NS_OK if succeeded else ERROR.
*/
NS_IMETHOD CancelParsingEvents()=0;
};
/* ===========================================================*
@ -304,6 +317,7 @@ class nsIParser : public nsISupports {
#define NS_ERROR_HTMLPARSER_UNTERMINATEDSTRINGLITERAL NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_HTMLPARSER,1016)
#define NS_ERROR_HTMLPARSER_HIERARCHYTOODEEP NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_HTMLPARSER,1017)
#define NS_ERROR_HTMLPARSER_CONTINUE NS_OK

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

@ -76,6 +76,10 @@ public:
NS_IMETHOD DoFragment(PRBool aFlag);
NS_IMETHOD BeginContext(PRInt32 aPosition);
NS_IMETHOD EndContext(PRInt32 aPosition);
NS_IMETHOD WillProcessTokens(void) { return NS_OK; }
NS_IMETHOD DidProcessTokens(void) { return NS_OK; }
NS_IMETHOD WillProcessAToken(void) { return NS_OK; }
NS_IMETHOD DidProcessAToken(void) { return NS_OK; }
// nsILoggingSink
NS_IMETHOD SetOutputStream(PRFileDesc *aStream,PRBool autoDelete=PR_FALSE);

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

@ -46,6 +46,8 @@
#include "prenv.h"
#include "nsParserCIID.h"
#include "nsCOMPtr.h"
#include "nsIEventQueue.h"
#include "nsIEventQueueService.h"
//#define rickgdebug
static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID);
@ -57,6 +59,7 @@ static NS_DEFINE_CID(kWellFormedDTDCID, NS_WELLFORMEDDTD_CID);
static NS_DEFINE_CID(kNavDTDCID, NS_CNAVDTD_CID);
static NS_DEFINE_CID(kCOtherDTDCID, NS_COTHER_DTD_CID);
static NS_DEFINE_CID(kViewSourceDTDCID, NS_VIEWSOURCE_DTD_CID);
static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID);
static const char* kNullURL = "Error: Null URL given";
static const char* kOnStartNotCalled = "Error: OnStartRequest() must be called before OnDataAvailable()";
@ -150,6 +153,99 @@ public:
nsIDTD *mOtherDTD; //it's ok to leak this; the deque contains a copy too.
};
//-------------- Begin ParseContinue Event Definition ------------------------
/*
The parser can be explicitly interrupted by passing a return value of NS_ERROR_HTMLPARSER_INTERRUPTED
from BuildModel on the DTD. This will cause the parser to stop processing and allow
the application to return to the event loop. The data which was left at the time of
interruption will be processed the next time OnDataAvailable is called. If the parser
has received its final chunk of data then OnDataAvailable will no longer be called by the
networking module, so the parser will schedule a nsParserContinueEvent which will call
the parser to process the remaining data after returning to the event loop. If the parser
is interrupted while processing the remaining data it will schedule another
ParseContinueEvent. The processing of data followed by scheduling of the continue events
will proceed until either:
1) All of the remaining data can be processed without interrupting
2) The parser has been cancelled.
This capability is currently used in CNavDTD and nsHTMLContentSink. The nsHTMLContentSink is
notified by CNavDTD when a chunk of tokens is going to be processed and when each token
is processed. The nsHTML content sink records the time when the chunk has started
processing and will return NS_ERROR_HTMLPARSER_INTERRUPTED if the token processing time
has exceeded a threshold called max tokenizing processing time. This allows the content
sink to limit how much data is processed in a single chunk which in turn gates how much
time is spent away from the event loop. Processing smaller chunks of data also reduces
the time spent in subsequent reflows.
This capability is most apparent when loading large documents. If the maximum token
processing time is set small enough the application will remain responsive during
document load.
A side-effect of this capability is that document load is not complete when the last chunk
of data is passed to OnDataAvailable since the parser may have been interrupted when
the last chunk of data arrived. The document is complete when all of the document has
been tokenized and there aren't any pending nsParserContinueEvents. This can cause
problems if the application assumes that it can monitor the load requests to determine
when the document load has been completed. This is what happens in Mozilla. The document
is considered completely loaded when all of the load requests have been satisfied. To delay the
document load until all of the parsing has been completed the nsHTMLContentSink adds a
dummy parser load request which is not removed until the nsHTMLContentSink's DidBuildModel
is called. The CNavDTD will not call DidBuildModel until the final chunk of data has been
passed to the parser through the OnDataAvailable and there aren't any pending
nsParserContineEvents.
Currently the parser is ignores requests to be interrupted during the processing of script.
This is because a document.write followed by JavaScript calls to manipulate the DOM may
fail if the parser was interrupted during the document.write.
For more details @see bugzilla bug 76722
*/
struct nsParserContinueEvent : public PLEvent {
nsParserContinueEvent(nsIParser* aParser);
~nsParserContinueEvent() { }
void HandleEvent() {
if (mParser) {
nsParser* parser = NS_STATIC_CAST(nsParser*, mParser);
parser->HandleParserContinueEvent();
NS_RELEASE(mParser);
}
};
nsIParser* mParser;
};
static void PR_CALLBACK HandlePLEvent(nsParserContinueEvent* aEvent)
{
NS_ASSERTION(nsnull != aEvent,"Event is null");
aEvent->HandleEvent();
}
static void PR_CALLBACK DestroyPLEvent(nsParserContinueEvent* aEvent)
{
NS_ASSERTION(nsnull != aEvent,"Event is null");
delete aEvent;
}
nsParserContinueEvent::nsParserContinueEvent(nsIParser* aParser)
{
NS_ASSERTION(aParser, "null parameter");
mParser = aParser;
PL_InitEvent(this, aParser,
(PLHandleEventProc) ::HandlePLEvent,
(PLDestroyEventProc) ::DestroyPLEvent);
}
//-------------- End ParseContinue Event Definition ------------------------
static CSharedParserObjects* gSharedParserObjects=0;
@ -247,11 +343,24 @@ nsParser::nsParser(nsITokenObserver* anObserver) {
mCommand=eViewNormal;
mParserEnabled=PR_TRUE;
mBundle=nsnull;
mPendingContinueEvent=PR_FALSE;
mCanInterrupt=PR_FALSE;
MOZ_TIMER_DEBUGLOG(("Reset: Parse Time: nsParser::nsParser(), this=%p\n", this));
MOZ_TIMER_RESET(mParseTime);
MOZ_TIMER_RESET(mDTDTime);
MOZ_TIMER_RESET(mTokenizeTime);
nsresult rv = NS_OK;
if (mEventQueue == nsnull) {
// Cache the event queue of the current UI thread
NS_WITH_SERVICE(nsIEventQueueService, eventService, kEventQueueServiceCID, &rv);
if (NS_SUCCEEDED(rv) && (eventService)) { // XXX this implies that the UI is the current thread.
rv = eventService->GetThreadEventQueue(NS_CURRENT_THREAD, getter_AddRefs(mEventQueue));
}
NS_ASSERTION(mEventQueue, "event queue is null");
}
}
/**
@ -286,6 +395,11 @@ nsParser::~nsParser() {
//don't forget to add code here to delete
//what may be several contexts...
delete mParserContext;
if (mPendingContinueEvent) {
NS_ASSERTION(mEventQueue != nsnull,"Event queue is null");
mEventQueue->RevokeEvents(this);
}
}
@ -339,6 +453,24 @@ nsresult nsParser::QueryInterface(const nsIID& aIID, void** aInstancePtr)
return NS_OK;
}
// The parser continue event is posted only if
// all of the data to parse has been passed to ::OnDataAvailable
// and the parser has been interrupted by the content sink
// because the processing of tokens took too long.
nsresult
nsParser::PostContinueEvent()
{
if ((! mPendingContinueEvent) && (mEventQueue)) {
nsParserContinueEvent* ev = new nsParserContinueEvent(NS_STATIC_CAST(nsIParser*, this));
NS_ENSURE_TRUE(ev,NS_ERROR_OUT_OF_MEMORY);
NS_ADDREF(this);
mEventQueue->PostEvent(ev);
mPendingContinueEvent = PR_TRUE;
}
return NS_OK;
}
/**
*
@ -1240,6 +1372,19 @@ NS_IMETHODIMP nsParser::CreateCompatibleDTD(nsIDTD** aDTD,
}
NS_IMETHODIMP
nsParser::CancelParsingEvents() {
if (mPendingContinueEvent) {
NS_ASSERTION(mEventQueue,"Event queue is null");
// Revoke all pending continue parsing events
if (mEventQueue != nsnull) {
mEventQueue->RevokeEvents(this);
}
mPendingContinueEvent=PR_FALSE;
}
return NS_OK;
}
//#define TEST_DOCTYPES
#ifdef TEST_DOCTYPES
static const char* doctypes[] = {
@ -1433,13 +1578,15 @@ nsresult nsParser::DidBuildModel(nsresult anErrorCode) {
//One last thing...close any open containers.
nsresult result=anErrorCode;
if(mParserContext && !mParserContext->mPrevContext) {
if(mParserContext->mDTD) {
result=mParserContext->mDTD->DidBuildModel(anErrorCode,PRBool(0==mParserContext->mPrevContext),this,mSink);
}
//Ref. to bug 61462.
NS_IF_RELEASE(mBundle);
}//if
if (IsComplete()) {
if(mParserContext && !mParserContext->mPrevContext) {
if(mParserContext->mDTD) {
result=mParserContext->mDTD->DidBuildModel(anErrorCode,PRBool(0==mParserContext->mPrevContext),this,mSink);
}
//Ref. to bug 61462.
NS_IF_RELEASE(mBundle);
}//if
}
return result;
}
@ -1540,7 +1687,7 @@ nsresult nsParser::ContinueParsing(){
if(result!=NS_OK)
result=mInternalState;
return result;
}
@ -1582,6 +1729,29 @@ PRBool nsParser::IsParserEnabled() {
return mParserEnabled;
}
/**
* Call this to query whether the parser thinks it's done with parsing.
*
* @update rickg 5/12/01
* @return complete state
*/
PRBool nsParser::IsComplete() {
return (! mPendingContinueEvent);
}
void nsParser::HandleParserContinueEvent() {
mPendingContinueEvent = PR_FALSE;
ContinueParsing();
}
PRBool nsParser::CanInterrupt(void) {
return mCanInterrupt;
}
void nsParser::SetCanInterrupt(PRBool aCanInterrupt) {
mCanInterrupt = aCanInterrupt;
}
/**
* This is the main controlling routine in the parsing process.
@ -1679,7 +1849,16 @@ aMimeType,PRBool aVerifyEnabled,PRBool aLastCall,nsDTDMode aMode){
//NOTE: Make sure that updates to this method don't cause
// bug #2361 to break again!
nsresult result=NS_OK;
nsresult result=NS_OK;
if(aLastCall && (0==aSourceBuffer.Length())) {
// Nothing is being passed to the parser so return
// immediately. mUnusedInput will get processed when
// some data is actually passed in.
return result;
}
nsParser* me = this;
// Maintain a reference to ourselves so we don't go away
// till we're completely done.
@ -1687,7 +1866,7 @@ aMimeType,PRBool aVerifyEnabled,PRBool aLastCall,nsDTDMode aMode){
if(aSourceBuffer.Length() || mUnusedInput.Length()) {
mDTDVerification=aVerifyEnabled;
CParserContext* pc=0;
CParserContext* pc=0;
if((!mParserContext) || (mParserContext->mKey!=aKey)) {
//only make a new context if we dont have one, OR if we do, but has a different context key...
@ -1867,10 +2046,19 @@ nsresult nsParser::ResumeParse(PRBool allowIteration, PRBool aIsFinalChunk) {
}
}
//Only allow parsing to be interuptted in the subsequent call
//to build model.
SetCanInterrupt(PR_TRUE);
nsresult theTokenizerResult=Tokenize(aIsFinalChunk); // kEOF==2152596456
result=BuildModel();
theIterationIsOk=PRBool(kEOF!=theTokenizerResult);
if(result==NS_ERROR_HTMLPARSER_INTERRUPTED) {
if(aIsFinalChunk)
PostContinueEvent();
}
SetCanInterrupt(PR_FALSE);
theIterationIsOk=PRBool((kEOF!=theTokenizerResult) && (result!=NS_ERROR_HTMLPARSER_INTERRUPTED));
// Make sure not to stop parsing too early. Therefore, before shutting down the
// parser, it's important to check whether the input buffer has been scanned to
@ -1895,7 +2083,7 @@ nsresult nsParser::ResumeParse(PRBool allowIteration, PRBool aIsFinalChunk) {
break;
}
else if((NS_OK==result) && (theTokenizerResult==kEOF)){
else if(((NS_OK==result) && (theTokenizerResult==kEOF)) || (result==NS_ERROR_HTMLPARSER_INTERRUPTED)){
PRBool theContextIsStringBased=PRBool(CParserContext::eCTString==mParserContext->mContextType);
if( (eOnStop==mParserContext->mStreamListenerState) ||
@ -1918,11 +2106,11 @@ nsresult nsParser::ResumeParse(PRBool allowIteration, PRBool aIsFinalChunk) {
MOZ_TIMER_LOG(("Tokenize Time: "));
MOZ_TIMER_PRINT(mTokenizeTime);
return result;
return NS_OK;
}
}
else {
else {
CParserContext* theContext=PopContext();
if(theContext) {
@ -1933,6 +2121,8 @@ nsresult nsParser::ResumeParse(PRBool allowIteration, PRBool aIsFinalChunk) {
delete theContext;
}
result = mInternalState;
aIsFinalChunk=(mParserContext && mParserContext->mStreamListenerState==eOnStop)? PR_TRUE:PR_FALSE;
//...then intentionally fall through to WillInterruptParse()...
}
@ -1940,10 +2130,12 @@ nsresult nsParser::ResumeParse(PRBool allowIteration, PRBool aIsFinalChunk) {
}
if(kEOF==theTokenizerResult) {
if((kEOF==theTokenizerResult) || (result==NS_ERROR_HTMLPARSER_INTERRUPTED)) {
result = (result == NS_ERROR_HTMLPARSER_INTERRUPTED) ? NS_OK : result;
mParserContext->mDTD->WillInterruptParse();
}
}//while
}//if
else {
@ -1954,7 +2146,7 @@ nsresult nsParser::ResumeParse(PRBool allowIteration, PRBool aIsFinalChunk) {
MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: nsParser::ResumeParse(), this=%p\n", this));
MOZ_TIMER_STOP(mParseTime);
return result;
return (result==NS_ERROR_HTMLPARSER_INTERRUPTED) ? NS_OK : result;
}
/**

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

@ -70,6 +70,7 @@
#include "nsDTDUtils.h"
#include "nsTimer.h"
#include "nsIProgressEventSink.h"
#include "nsIEventQueue.h"
class IContentSink;
class nsIDTD;
@ -225,6 +226,14 @@ class nsParser : public nsIParser,
*/
virtual PRBool IsParserEnabled();
/**
* Call this to query whether the parser thinks it's done with parsing.
*
* @update rickg 5/12/01
* @return complete state
*/
virtual PRBool IsComplete();
/**
* This rather arcane method (hack) is used as a signal between the
* DTD and the parser. It allows the DTD to tell the parser that content
@ -319,6 +328,44 @@ class nsParser : public nsIParser,
const nsString* aMimeType=nsnull,
nsDTDMode aDTDMode=eDTDMode_unknown);
/**
* Removes continue parsing events
* @update kmcclusk 5/18/98
*/
NS_IMETHODIMP CancelParsingEvents();
/**
* Indicates whether the parser is in a state where it
* can be interrupted.
* @return PR_TRUE if parser can be interrupted, PR_FALSE if it can not be interrupted.
* @update kmcclusk 5/18/98
*/
PRBool CanInterrupt(void);
/**
* Set to parser state to indicate whether parsing tokens can be interrupted
* @param aCanInterrupt PR_TRUE if parser can be interrupted, PR_FALSE if it can not be interrupted.
* @update kmcclusk 5/18/98
*/
void SetCanInterrupt(PRBool aCanInterrupt);
/**
* This is called when the final chunk has been
* passed to the parser and the content sink has
* interrupted token processing. It schedules
* a ParserContinue PL_Event which will ask the parser
* to HandleParserContinueEvent when it is handled.
* @update kmcclusk6/1/2001
*/
nsresult PostContinueEvent();
/**
* Fired when the continue parse event is triggered.
* @update kmcclusk 5/18/98
*/
void HandleParserContinueEvent(void);
protected:
/**
@ -383,6 +430,8 @@ private:
* @return TRUE if all went well
*/
PRBool DidTokenize(PRBool aIsFinalChunk = PR_FALSE);
protected:
//*********************************************
// And now, some data members...
@ -396,6 +445,7 @@ protected:
nsIRequestObserver* mObserver;
nsIProgressEventSink* mProgressEventSink;
nsIContentSink* mSink;
nsIParserFilter* mParserFilter;
PRBool mDTDVerification;
eParserCommands mCommand;
@ -412,7 +462,12 @@ protected:
nsParserBundle* mBundle;
nsTokenAllocator mTokenAllocator;
public:
nsCOMPtr<nsIEventQueue> mEventQueue;
PRPackedBool mPendingContinueEvent;
PRPackedBool mCanInterrupt;
public:
MOZ_TIMER_DECLARE(mParseTime)
MOZ_TIMER_DECLARE(mDTDTime)
MOZ_TIMER_DECLARE(mTokenizeTime)

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

@ -86,6 +86,10 @@ public:
NS_IMETHOD WillResume(void) { return NS_OK; }
NS_IMETHOD SetParser(nsIParser* aParser) { return NS_OK; }
NS_IMETHOD FlushPendingNotifications() { return NS_OK; }
NS_IMETHOD WillProcessTokens(void) { return NS_OK; }
NS_IMETHOD DidProcessTokens(void) { return NS_OK; }
NS_IMETHOD WillProcessAToken(void) { return NS_OK; }
NS_IMETHOD DidProcessAToken(void) { return NS_OK; }
NS_IMETHOD DoFragment(PRBool aFlag);
NS_IMETHOD BeginContext(PRInt32 aPosition){ return NS_OK; }

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

@ -520,8 +520,12 @@ nsresult CNavDTD::BuildModel(nsIParser* aParser,nsITokenizer* aTokenizer,nsIToke
mTokenizer->PushTokenFront(theToken); //this token should get pushed on the context stack.
}
}
mSink->WillProcessTokens();
while(NS_SUCCEEDED(result)){
//Currently nsIHTMLContentSink does nothing with a call to WillProcessAToken.
//mSink->WillProcessAToken();
#if 0
int n=aTokenizer->GetCount();
@ -544,12 +548,34 @@ nsresult CNavDTD::BuildModel(nsIParser* aParser,nsITokenizer* aTokenizer,nsIToke
result=mDTDState;
break;
}
if ((NS_ERROR_HTMLPARSER_INTERRUPTED == mSink->DidProcessAToken())) {
// The content sink has requested that DTD interrupt processing tokens
// So we need to make sure the parser is in a state where it can be
// interrupted.
// The mParser->CanInterrupt will return TRUE if BuildModel was called
// from a place in the parser where it prepared to handle a return value of
// NS_ERROR_HTMLPARSER_INTERRUPTED.
// If the parser has mPrevContext then it may be processing
// Script so we should not allow it to be interrupted.
if ((mParser->CanInterrupt()) &&
(nsnull == mParser->PeekContext()->mPrevContext) &&
(eHTMLTag_unknown==mSkipTarget)) {
result = NS_ERROR_HTMLPARSER_INTERRUPTED;
break;
}
}
}//while
mTokenizer=oldTokenizer;
//Currently nsIHTMLContentSink does nothing with a call to DidProcessATokens().
//mSink->DidProcessTokens();
}
}
}
else result=NS_ERROR_HTMLPARSER_BADTOKENIZER;
return result;
}
@ -2219,7 +2245,6 @@ nsresult CNavDTD::HandleAttributeToken(CToken* aToken) {
return NS_OK;
}
/**
* This method gets called when a script token has been
* encountered in the parse process. n
@ -2236,6 +2261,8 @@ nsresult CNavDTD::HandleScriptToken(const nsIParserNode *aNode) {
nsresult result=AddLeaf(aNode);
mParser->SetCanInterrupt(PR_FALSE);
MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::HandleScriptToken(), this=%p\n", this));
START_TIMER();

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

@ -108,7 +108,6 @@ class nsITokenizer;
class nsCParserNode;
class nsTokenAllocator;
/***************************************************************
Now the main event: CNavDTD.

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

@ -71,6 +71,10 @@ public:
NS_IMETHOD OpenFrameset(const nsIParserNode& aNode);
NS_IMETHOD CloseFrameset(const nsIParserNode& aNode);
NS_IMETHOD GetPref(PRInt32 aTag,PRBool& aPref) { return NS_OK; }
NS_IMETHOD WillProcessTokens(void) { return NS_OK; }
NS_IMETHOD DidProcessTokens(void) { return NS_OK; }
NS_IMETHOD WillProcessAToken(void) { return NS_OK; }
NS_IMETHOD DidProcessAToken(void) { return NS_OK; }
NS_IMETHOD DoFragment(PRBool aFlag);
NS_IMETHOD BeginContext(PRInt32 aPosition);

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

@ -1,234 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape 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/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
#ifndef nsIHTMLContentSink_h___
#define nsIHTMLContentSink_h___
/**
* MODULE NOTES:
* @update gess 4/1/98
*
* This file declares the concrete HTMLContentSink class.
* This class is used during the parsing process as the
* primary interface between the parser and the content
* model.
*
* After the tokenizer completes, the parser iterates over
* the known token list. As the parser identifies valid
* elements, it calls the contentsink interface to notify
* the content model that a new node or child node is being
* created and added to the content model.
*
* The HTMLContentSink interface assumes 4 underlying
* containers: HTML, HEAD, BODY and FRAMESET. Before
* accessing any these, the parser will call the appropriate
* OpennsIHTMLContentSink method: OpenHTML,OpenHead,OpenBody,OpenFrameSet;
* likewise, the ClosensIHTMLContentSink version will be called when the
* parser is done with a given section.
*
* IMPORTANT: The parser may Open each container more than
* once! This is due to the irregular nature of HTML files.
* For example, it is possible to encounter plain text at
* the start of an HTML document (that preceeds the HTML tag).
* Such text is treated as if it were part of the body.
* In such cases, the parser will Open the body, pass the text-
* node in and then Close the body. The body will likely be
* re-Opened later when the actual <BODY> tag has been seen.
*
* Containers within the body are Opened and Closed
* using the OpenContainer(...) and CloseContainer(...) calls.
* It is assumed that the document or contentSink is
* maintaining its state to manage where new content should
* be added to the underlying document.
*
* NOTE: OpenHTML() and OpenBody() may get called multiple times
* in the same document. That's fine, and it doesn't mean
* that we have multiple bodies or HTML's.
*
* NOTE: I haven't figured out how sub-documents (non-frames)
* are going to be handled. Stay tuned.
*/
#include "nsIParserNode.h"
#include "nsIContentSink.h"
#define NS_IHTML_CONTENT_SINK_IID \
{ 0xa6cf9051, 0x15b3, 0x11d2,{0x93, 0x2e, 0x00, 0x80, 0x5f, 0x8a, 0xdd, 0x32}}
#ifdef XP_MAC
#define MAX_REFLOW_DEPTH 75 //setting to 75 to prevent layout from crashing on mac. Bug 55095.
#else
#define MAX_REFLOW_DEPTH 200 //windows and linux (etc) can do much deeper structures.
#endif
class nsIHTMLContentSink : public nsIContentSink {
public:
static const nsIID& GetIID() { static nsIID iid = NS_IHTML_CONTENT_SINK_IID; return iid; }
/**
* This method gets called by the parser when it encounters
* a title tag and wants to set the document title in the sink.
*
* @update 4/1/98 gess
* @param nsString reference to new title value
*/
NS_IMETHOD SetTitle(const nsString& aValue)=0;
/**
* This method is used to open the outer HTML container.
*
* @update 4/1/98 gess
* @param nsIParserNode reference to parser node interface
*/
NS_IMETHOD OpenHTML(const nsIParserNode& aNode)=0;
/**
* This method is used to close the outer HTML container.
*
* @update 4/1/98 gess
* @param nsIParserNode reference to parser node interface
*/
NS_IMETHOD CloseHTML(const nsIParserNode& aNode)=0;
/**
* This method is used to open the only HEAD container.
*
* @update 4/1/98 gess
* @param nsIParserNode reference to parser node interface
*/
NS_IMETHOD OpenHead(const nsIParserNode& aNode)=0;
/**
* This method is used to close the only HEAD container.
*
* @update 4/1/98 gess
* @param nsIParserNode reference to parser node interface
*/
NS_IMETHOD CloseHead(const nsIParserNode& aNode)=0;
/**
* This method is used to open the main BODY container.
*
* @update 4/1/98 gess
* @param nsIParserNode reference to parser node interface
*/
NS_IMETHOD OpenBody(const nsIParserNode& aNode)=0;
/**
* This method is used to close the main BODY container.
*
* @update 4/1/98 gess
* @param nsIParserNode reference to parser node interface
*/
NS_IMETHOD CloseBody(const nsIParserNode& aNode)=0;
/**
* This method is used to open a new FORM container.
*
* @update 4/1/98 gess
* @param nsIParserNode reference to parser node interface
*/
NS_IMETHOD OpenForm(const nsIParserNode& aNode)=0;
/**
* This method is used to close the outer FORM container.
*
* @update 4/1/98 gess
* @param nsIParserNode reference to parser node interface
*/
NS_IMETHOD CloseForm(const nsIParserNode& aNode)=0;
/**
* This method is used to open a new MAP container.
*
* @update 4/1/98 gess
* @param nsIParserNode reference to parser node interface
*/
NS_IMETHOD OpenMap(const nsIParserNode& aNode)=0;
/**
* This method is used to close the MAP container.
*
* @update 4/1/98 gess
* @param nsIParserNode reference to parser node interface
*/
NS_IMETHOD CloseMap(const nsIParserNode& aNode)=0;
/**
* This method is used to open the FRAMESET container.
*
* @update 4/1/98 gess
* @param nsIParserNode reference to parser node interface
*/
NS_IMETHOD OpenFrameset(const nsIParserNode& aNode)=0;
/**
* This method is used to close the FRAMESET container.
*
* @update 4/1/98 gess
* @param nsIParserNode reference to parser node interface
*/
NS_IMETHOD CloseFrameset(const nsIParserNode& aNode)=0;
/**
* This method tells the sink whether or not it is
* encoding an HTML fragment or the whole document.
* By default, the entire document is encoded.
*
* @update 03/14/99 gpk
* @param aFlag set to true if only encoding a fragment
*/
NS_IMETHOD DoFragment(PRBool aFlag)=0;
/**
* This gets called when handling illegal contents, especially
* in dealing with tables. This method creates a new context.
*
* @update 04/04/99 harishd
* @param aPosition - The position from where the new context begins.
*/
NS_IMETHOD BeginContext(PRInt32 aPosition)=0;
/**
* This method terminates any new context that got created by
* BeginContext and switches back to the main context.
*
* @update 04/04/99 harishd
* @param aPosition - Validates the end of a context.
*/
NS_IMETHOD EndContext(PRInt32 aPosition)=0;
/**
* Use this method to retrieve pref. for the tag.
*
* @update 04/11/01 harishd
* @param aTag - Check pref. for this tag.
*/
NS_IMETHOD GetPref(PRInt32 aTag,PRBool& aPref)=0;
};
extern NS_HTMLPARS nsresult NS_NewHTMLNullSink(nsIContentSink** aInstancePtrResult);
#endif /* nsIHTMLContentSink_h___ */

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

@ -110,7 +110,7 @@ public:
virtual PRUint32 GetSize(void)=0;
};
/**
/**
* FOR DEBUG PURPOSE ONLY
*
* Use this interface to query objects that contain content information.
@ -233,6 +233,7 @@ class nsIParser : public nsISupports {
virtual void UnblockParser() =0;
virtual PRBool IsParserEnabled() =0;
virtual PRBool IsComplete() =0;
virtual nsresult Parse(nsIURI* aURL,nsIRequestObserver* aListener = nsnull,PRBool aEnableVerify=PR_FALSE, void* aKey=0,nsDTDMode aMode=eDTDMode_autodetect) = 0;
virtual nsresult Parse(nsIInputStream& aStream, const nsString& aMimeType,PRBool aEnableVerify=PR_FALSE, void* aKey=0,nsDTDMode aMode=eDTDMode_autodetect) = 0;
@ -276,6 +277,18 @@ class nsIParser : public nsISupports {
eParserCommands aCommand,
const nsString* aMimeType=nsnull,
nsDTDMode aDTDMode=eDTDMode_unknown)=0;
/**
* Call this method to cancel any pending parsing events.
* Parsing events may be pending if all of the document's content
* has been passed to the parser but the parser has been interrupted
* because processing the tokens took too long.
*
* @update kmcclusk 05/18/01
* @return NS_OK if succeeded else ERROR.
*/
NS_IMETHOD CancelParsingEvents()=0;
};
/* ===========================================================*
@ -304,6 +317,7 @@ class nsIParser : public nsISupports {
#define NS_ERROR_HTMLPARSER_UNTERMINATEDSTRINGLITERAL NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_HTMLPARSER,1016)
#define NS_ERROR_HTMLPARSER_HIERARCHYTOODEEP NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_HTMLPARSER,1017)
#define NS_ERROR_HTMLPARSER_CONTINUE NS_OK

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

@ -76,6 +76,10 @@ public:
NS_IMETHOD DoFragment(PRBool aFlag);
NS_IMETHOD BeginContext(PRInt32 aPosition);
NS_IMETHOD EndContext(PRInt32 aPosition);
NS_IMETHOD WillProcessTokens(void) { return NS_OK; }
NS_IMETHOD DidProcessTokens(void) { return NS_OK; }
NS_IMETHOD WillProcessAToken(void) { return NS_OK; }
NS_IMETHOD DidProcessAToken(void) { return NS_OK; }
// nsILoggingSink
NS_IMETHOD SetOutputStream(PRFileDesc *aStream,PRBool autoDelete=PR_FALSE);

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

@ -46,6 +46,8 @@
#include "prenv.h"
#include "nsParserCIID.h"
#include "nsCOMPtr.h"
#include "nsIEventQueue.h"
#include "nsIEventQueueService.h"
//#define rickgdebug
static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID);
@ -57,6 +59,7 @@ static NS_DEFINE_CID(kWellFormedDTDCID, NS_WELLFORMEDDTD_CID);
static NS_DEFINE_CID(kNavDTDCID, NS_CNAVDTD_CID);
static NS_DEFINE_CID(kCOtherDTDCID, NS_COTHER_DTD_CID);
static NS_DEFINE_CID(kViewSourceDTDCID, NS_VIEWSOURCE_DTD_CID);
static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID);
static const char* kNullURL = "Error: Null URL given";
static const char* kOnStartNotCalled = "Error: OnStartRequest() must be called before OnDataAvailable()";
@ -150,6 +153,99 @@ public:
nsIDTD *mOtherDTD; //it's ok to leak this; the deque contains a copy too.
};
//-------------- Begin ParseContinue Event Definition ------------------------
/*
The parser can be explicitly interrupted by passing a return value of NS_ERROR_HTMLPARSER_INTERRUPTED
from BuildModel on the DTD. This will cause the parser to stop processing and allow
the application to return to the event loop. The data which was left at the time of
interruption will be processed the next time OnDataAvailable is called. If the parser
has received its final chunk of data then OnDataAvailable will no longer be called by the
networking module, so the parser will schedule a nsParserContinueEvent which will call
the parser to process the remaining data after returning to the event loop. If the parser
is interrupted while processing the remaining data it will schedule another
ParseContinueEvent. The processing of data followed by scheduling of the continue events
will proceed until either:
1) All of the remaining data can be processed without interrupting
2) The parser has been cancelled.
This capability is currently used in CNavDTD and nsHTMLContentSink. The nsHTMLContentSink is
notified by CNavDTD when a chunk of tokens is going to be processed and when each token
is processed. The nsHTML content sink records the time when the chunk has started
processing and will return NS_ERROR_HTMLPARSER_INTERRUPTED if the token processing time
has exceeded a threshold called max tokenizing processing time. This allows the content
sink to limit how much data is processed in a single chunk which in turn gates how much
time is spent away from the event loop. Processing smaller chunks of data also reduces
the time spent in subsequent reflows.
This capability is most apparent when loading large documents. If the maximum token
processing time is set small enough the application will remain responsive during
document load.
A side-effect of this capability is that document load is not complete when the last chunk
of data is passed to OnDataAvailable since the parser may have been interrupted when
the last chunk of data arrived. The document is complete when all of the document has
been tokenized and there aren't any pending nsParserContinueEvents. This can cause
problems if the application assumes that it can monitor the load requests to determine
when the document load has been completed. This is what happens in Mozilla. The document
is considered completely loaded when all of the load requests have been satisfied. To delay the
document load until all of the parsing has been completed the nsHTMLContentSink adds a
dummy parser load request which is not removed until the nsHTMLContentSink's DidBuildModel
is called. The CNavDTD will not call DidBuildModel until the final chunk of data has been
passed to the parser through the OnDataAvailable and there aren't any pending
nsParserContineEvents.
Currently the parser is ignores requests to be interrupted during the processing of script.
This is because a document.write followed by JavaScript calls to manipulate the DOM may
fail if the parser was interrupted during the document.write.
For more details @see bugzilla bug 76722
*/
struct nsParserContinueEvent : public PLEvent {
nsParserContinueEvent(nsIParser* aParser);
~nsParserContinueEvent() { }
void HandleEvent() {
if (mParser) {
nsParser* parser = NS_STATIC_CAST(nsParser*, mParser);
parser->HandleParserContinueEvent();
NS_RELEASE(mParser);
}
};
nsIParser* mParser;
};
static void PR_CALLBACK HandlePLEvent(nsParserContinueEvent* aEvent)
{
NS_ASSERTION(nsnull != aEvent,"Event is null");
aEvent->HandleEvent();
}
static void PR_CALLBACK DestroyPLEvent(nsParserContinueEvent* aEvent)
{
NS_ASSERTION(nsnull != aEvent,"Event is null");
delete aEvent;
}
nsParserContinueEvent::nsParserContinueEvent(nsIParser* aParser)
{
NS_ASSERTION(aParser, "null parameter");
mParser = aParser;
PL_InitEvent(this, aParser,
(PLHandleEventProc) ::HandlePLEvent,
(PLDestroyEventProc) ::DestroyPLEvent);
}
//-------------- End ParseContinue Event Definition ------------------------
static CSharedParserObjects* gSharedParserObjects=0;
@ -247,11 +343,24 @@ nsParser::nsParser(nsITokenObserver* anObserver) {
mCommand=eViewNormal;
mParserEnabled=PR_TRUE;
mBundle=nsnull;
mPendingContinueEvent=PR_FALSE;
mCanInterrupt=PR_FALSE;
MOZ_TIMER_DEBUGLOG(("Reset: Parse Time: nsParser::nsParser(), this=%p\n", this));
MOZ_TIMER_RESET(mParseTime);
MOZ_TIMER_RESET(mDTDTime);
MOZ_TIMER_RESET(mTokenizeTime);
nsresult rv = NS_OK;
if (mEventQueue == nsnull) {
// Cache the event queue of the current UI thread
NS_WITH_SERVICE(nsIEventQueueService, eventService, kEventQueueServiceCID, &rv);
if (NS_SUCCEEDED(rv) && (eventService)) { // XXX this implies that the UI is the current thread.
rv = eventService->GetThreadEventQueue(NS_CURRENT_THREAD, getter_AddRefs(mEventQueue));
}
NS_ASSERTION(mEventQueue, "event queue is null");
}
}
/**
@ -286,6 +395,11 @@ nsParser::~nsParser() {
//don't forget to add code here to delete
//what may be several contexts...
delete mParserContext;
if (mPendingContinueEvent) {
NS_ASSERTION(mEventQueue != nsnull,"Event queue is null");
mEventQueue->RevokeEvents(this);
}
}
@ -339,6 +453,24 @@ nsresult nsParser::QueryInterface(const nsIID& aIID, void** aInstancePtr)
return NS_OK;
}
// The parser continue event is posted only if
// all of the data to parse has been passed to ::OnDataAvailable
// and the parser has been interrupted by the content sink
// because the processing of tokens took too long.
nsresult
nsParser::PostContinueEvent()
{
if ((! mPendingContinueEvent) && (mEventQueue)) {
nsParserContinueEvent* ev = new nsParserContinueEvent(NS_STATIC_CAST(nsIParser*, this));
NS_ENSURE_TRUE(ev,NS_ERROR_OUT_OF_MEMORY);
NS_ADDREF(this);
mEventQueue->PostEvent(ev);
mPendingContinueEvent = PR_TRUE;
}
return NS_OK;
}
/**
*
@ -1240,6 +1372,19 @@ NS_IMETHODIMP nsParser::CreateCompatibleDTD(nsIDTD** aDTD,
}
NS_IMETHODIMP
nsParser::CancelParsingEvents() {
if (mPendingContinueEvent) {
NS_ASSERTION(mEventQueue,"Event queue is null");
// Revoke all pending continue parsing events
if (mEventQueue != nsnull) {
mEventQueue->RevokeEvents(this);
}
mPendingContinueEvent=PR_FALSE;
}
return NS_OK;
}
//#define TEST_DOCTYPES
#ifdef TEST_DOCTYPES
static const char* doctypes[] = {
@ -1433,13 +1578,15 @@ nsresult nsParser::DidBuildModel(nsresult anErrorCode) {
//One last thing...close any open containers.
nsresult result=anErrorCode;
if(mParserContext && !mParserContext->mPrevContext) {
if(mParserContext->mDTD) {
result=mParserContext->mDTD->DidBuildModel(anErrorCode,PRBool(0==mParserContext->mPrevContext),this,mSink);
}
//Ref. to bug 61462.
NS_IF_RELEASE(mBundle);
}//if
if (IsComplete()) {
if(mParserContext && !mParserContext->mPrevContext) {
if(mParserContext->mDTD) {
result=mParserContext->mDTD->DidBuildModel(anErrorCode,PRBool(0==mParserContext->mPrevContext),this,mSink);
}
//Ref. to bug 61462.
NS_IF_RELEASE(mBundle);
}//if
}
return result;
}
@ -1540,7 +1687,7 @@ nsresult nsParser::ContinueParsing(){
if(result!=NS_OK)
result=mInternalState;
return result;
}
@ -1582,6 +1729,29 @@ PRBool nsParser::IsParserEnabled() {
return mParserEnabled;
}
/**
* Call this to query whether the parser thinks it's done with parsing.
*
* @update rickg 5/12/01
* @return complete state
*/
PRBool nsParser::IsComplete() {
return (! mPendingContinueEvent);
}
void nsParser::HandleParserContinueEvent() {
mPendingContinueEvent = PR_FALSE;
ContinueParsing();
}
PRBool nsParser::CanInterrupt(void) {
return mCanInterrupt;
}
void nsParser::SetCanInterrupt(PRBool aCanInterrupt) {
mCanInterrupt = aCanInterrupt;
}
/**
* This is the main controlling routine in the parsing process.
@ -1679,7 +1849,16 @@ aMimeType,PRBool aVerifyEnabled,PRBool aLastCall,nsDTDMode aMode){
//NOTE: Make sure that updates to this method don't cause
// bug #2361 to break again!
nsresult result=NS_OK;
nsresult result=NS_OK;
if(aLastCall && (0==aSourceBuffer.Length())) {
// Nothing is being passed to the parser so return
// immediately. mUnusedInput will get processed when
// some data is actually passed in.
return result;
}
nsParser* me = this;
// Maintain a reference to ourselves so we don't go away
// till we're completely done.
@ -1687,7 +1866,7 @@ aMimeType,PRBool aVerifyEnabled,PRBool aLastCall,nsDTDMode aMode){
if(aSourceBuffer.Length() || mUnusedInput.Length()) {
mDTDVerification=aVerifyEnabled;
CParserContext* pc=0;
CParserContext* pc=0;
if((!mParserContext) || (mParserContext->mKey!=aKey)) {
//only make a new context if we dont have one, OR if we do, but has a different context key...
@ -1867,10 +2046,19 @@ nsresult nsParser::ResumeParse(PRBool allowIteration, PRBool aIsFinalChunk) {
}
}
//Only allow parsing to be interuptted in the subsequent call
//to build model.
SetCanInterrupt(PR_TRUE);
nsresult theTokenizerResult=Tokenize(aIsFinalChunk); // kEOF==2152596456
result=BuildModel();
theIterationIsOk=PRBool(kEOF!=theTokenizerResult);
if(result==NS_ERROR_HTMLPARSER_INTERRUPTED) {
if(aIsFinalChunk)
PostContinueEvent();
}
SetCanInterrupt(PR_FALSE);
theIterationIsOk=PRBool((kEOF!=theTokenizerResult) && (result!=NS_ERROR_HTMLPARSER_INTERRUPTED));
// Make sure not to stop parsing too early. Therefore, before shutting down the
// parser, it's important to check whether the input buffer has been scanned to
@ -1895,7 +2083,7 @@ nsresult nsParser::ResumeParse(PRBool allowIteration, PRBool aIsFinalChunk) {
break;
}
else if((NS_OK==result) && (theTokenizerResult==kEOF)){
else if(((NS_OK==result) && (theTokenizerResult==kEOF)) || (result==NS_ERROR_HTMLPARSER_INTERRUPTED)){
PRBool theContextIsStringBased=PRBool(CParserContext::eCTString==mParserContext->mContextType);
if( (eOnStop==mParserContext->mStreamListenerState) ||
@ -1918,11 +2106,11 @@ nsresult nsParser::ResumeParse(PRBool allowIteration, PRBool aIsFinalChunk) {
MOZ_TIMER_LOG(("Tokenize Time: "));
MOZ_TIMER_PRINT(mTokenizeTime);
return result;
return NS_OK;
}
}
else {
else {
CParserContext* theContext=PopContext();
if(theContext) {
@ -1933,6 +2121,8 @@ nsresult nsParser::ResumeParse(PRBool allowIteration, PRBool aIsFinalChunk) {
delete theContext;
}
result = mInternalState;
aIsFinalChunk=(mParserContext && mParserContext->mStreamListenerState==eOnStop)? PR_TRUE:PR_FALSE;
//...then intentionally fall through to WillInterruptParse()...
}
@ -1940,10 +2130,12 @@ nsresult nsParser::ResumeParse(PRBool allowIteration, PRBool aIsFinalChunk) {
}
if(kEOF==theTokenizerResult) {
if((kEOF==theTokenizerResult) || (result==NS_ERROR_HTMLPARSER_INTERRUPTED)) {
result = (result == NS_ERROR_HTMLPARSER_INTERRUPTED) ? NS_OK : result;
mParserContext->mDTD->WillInterruptParse();
}
}//while
}//if
else {
@ -1954,7 +2146,7 @@ nsresult nsParser::ResumeParse(PRBool allowIteration, PRBool aIsFinalChunk) {
MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: nsParser::ResumeParse(), this=%p\n", this));
MOZ_TIMER_STOP(mParseTime);
return result;
return (result==NS_ERROR_HTMLPARSER_INTERRUPTED) ? NS_OK : result;
}
/**

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

@ -70,6 +70,7 @@
#include "nsDTDUtils.h"
#include "nsTimer.h"
#include "nsIProgressEventSink.h"
#include "nsIEventQueue.h"
class IContentSink;
class nsIDTD;
@ -225,6 +226,14 @@ class nsParser : public nsIParser,
*/
virtual PRBool IsParserEnabled();
/**
* Call this to query whether the parser thinks it's done with parsing.
*
* @update rickg 5/12/01
* @return complete state
*/
virtual PRBool IsComplete();
/**
* This rather arcane method (hack) is used as a signal between the
* DTD and the parser. It allows the DTD to tell the parser that content
@ -319,6 +328,44 @@ class nsParser : public nsIParser,
const nsString* aMimeType=nsnull,
nsDTDMode aDTDMode=eDTDMode_unknown);
/**
* Removes continue parsing events
* @update kmcclusk 5/18/98
*/
NS_IMETHODIMP CancelParsingEvents();
/**
* Indicates whether the parser is in a state where it
* can be interrupted.
* @return PR_TRUE if parser can be interrupted, PR_FALSE if it can not be interrupted.
* @update kmcclusk 5/18/98
*/
PRBool CanInterrupt(void);
/**
* Set to parser state to indicate whether parsing tokens can be interrupted
* @param aCanInterrupt PR_TRUE if parser can be interrupted, PR_FALSE if it can not be interrupted.
* @update kmcclusk 5/18/98
*/
void SetCanInterrupt(PRBool aCanInterrupt);
/**
* This is called when the final chunk has been
* passed to the parser and the content sink has
* interrupted token processing. It schedules
* a ParserContinue PL_Event which will ask the parser
* to HandleParserContinueEvent when it is handled.
* @update kmcclusk6/1/2001
*/
nsresult PostContinueEvent();
/**
* Fired when the continue parse event is triggered.
* @update kmcclusk 5/18/98
*/
void HandleParserContinueEvent(void);
protected:
/**
@ -383,6 +430,8 @@ private:
* @return TRUE if all went well
*/
PRBool DidTokenize(PRBool aIsFinalChunk = PR_FALSE);
protected:
//*********************************************
// And now, some data members...
@ -396,6 +445,7 @@ protected:
nsIRequestObserver* mObserver;
nsIProgressEventSink* mProgressEventSink;
nsIContentSink* mSink;
nsIParserFilter* mParserFilter;
PRBool mDTDVerification;
eParserCommands mCommand;
@ -412,7 +462,12 @@ protected:
nsParserBundle* mBundle;
nsTokenAllocator mTokenAllocator;
public:
nsCOMPtr<nsIEventQueue> mEventQueue;
PRPackedBool mPendingContinueEvent;
PRPackedBool mCanInterrupt;
public:
MOZ_TIMER_DECLARE(mParseTime)
MOZ_TIMER_DECLARE(mDTDTime)
MOZ_TIMER_DECLARE(mTokenizeTime)