/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set sw=2 ts=2 et tw=80: */ /* ***** 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/ * * 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.org code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ //#define ALLOW_TR_AS_CHILD_OF_TABLE //by setting this to true, TR is allowable directly in TABLE. #include "nsDebug.h" #include "nsIAtom.h" #include "CNavDTD.h" #include "nsHTMLTokens.h" #include "nsCRT.h" #include "nsParser.h" #include "nsIParser.h" #include "nsIHTMLContentSink.h" #include "nsScanner.h" #include "prenv.h" #include "prtypes.h" #include "prio.h" #include "plstr.h" #include "nsDTDUtils.h" #include "nsHTMLTokenizer.h" #include "nsTime.h" #include "nsParserNode.h" #include "nsHTMLEntities.h" #include "nsLinebreakConverter.h" #include "nsIFormProcessor.h" #include "nsVoidArray.h" #include "nsReadableUtils.h" #include "nsUnicharUtils.h" #include "prmem.h" #include "nsIServiceManager.h" #ifdef NS_DEBUG #include "nsLoggingSink.h" #endif /* * Ignore kFontStyle and kPhrase tags when the stack is deep, bug 58917. */ #define FONTSTYLE_IGNORE_DEPTH (MAX_REFLOW_DEPTH * 80 / 100) #define PHRASE_IGNORE_DEPTH (MAX_REFLOW_DEPTH * 90 / 100) static NS_DEFINE_CID(kFormProcessorCID, NS_FORMPROCESSOR_CID); #ifdef DEBUG static const char kNullToken[] = "Error: Null token given"; static const char kInvalidTagStackPos[] = "Error: invalid tag stack position"; #endif #include "nsElementTable.h" #ifdef MOZ_PERF_METRICS # define START_TIMER() \ if (mParser) MOZ_TIMER_START(mParser->mParseTime); \ if (mParser) MOZ_TIMER_START(mParser->mDTDTime); # define STOP_TIMER() \ if (mParser) MOZ_TIMER_STOP(mParser->mParseTime); \ if (mParser) MOZ_TIMER_STOP(mParser->mDTDTime); #else # define STOP_TIMER() # define START_TIMER() #endif // Some flags for use by the DTD. #define NS_DTD_FLAG_NONE 0x00000000 #define NS_DTD_FLAG_HAS_OPEN_HEAD 0x00000001 #define NS_DTD_FLAG_HAS_OPEN_BODY 0x00000002 #define NS_DTD_FLAG_HAS_OPEN_FORM 0x00000004 #define NS_DTD_FLAG_HAS_EXPLICIT_HEAD 0x00000008 #define NS_DTD_FLAG_HAD_BODY 0x00000010 #define NS_DTD_FLAG_HAD_FRAMESET 0x00000020 #define NS_DTD_FLAG_ENABLE_RESIDUAL_STYLE 0x00000040 #define NS_DTD_FLAG_ALTERNATE_CONTENT 0x00000080 // NOFRAMES, NOSCRIPT #define NS_DTD_FLAG_MISPLACED_CONTENT 0x00000100 #define NS_DTD_FLAG_IN_MISPLACED_CONTENT 0x00000200 #define NS_DTD_FLAG_STOP_PARSING 0x00000400 #define NS_DTD_FLAG_HAS_MAIN_CONTAINER (NS_DTD_FLAG_HAD_BODY | \ NS_DTD_FLAG_HAD_FRAMESET) NS_IMPL_ISUPPORTS2(CNavDTD, nsIDTD, CNavDTD) NS_DEFINE_STATIC_IID_ACCESSOR(CNavDTD, NS_INAVHTML_DTD_IID) CNavDTD::CNavDTD() : mMisplacedContent(0), mSink(0), mTokenAllocator(0), mBodyContext(new nsDTDContext()), mTempContext(0), mParser(0), mTokenizer(0), mDTDMode(eDTDMode_quirks), mDocType(eHTML3_Quirks), // why not eHTML_Quirks? mParserCommand(eViewNormal), mLineNumber(1), mOpenMapCount(0), mHeadContainerPosition(-1), mFlags(NS_DTD_FLAG_NONE) { } const nsIID& CNavDTD::GetMostDerivedIID() const { static nsIID kClassIID = NS_GET_IID(CNavDTD); return kClassIID; } #ifdef NS_DEBUG static nsLoggingSink* GetLoggingSink() { // This returns a content sink that is useful for following what calls the DTD // makes to the content sink. static PRBool checkForPath = PR_TRUE; static nsLoggingSink *theSink = nsnull; static const char* gLogPath = nsnull; if (checkForPath) { // Only check once per run. gLogPath = PR_GetEnv("PARSE_LOGFILE"); checkForPath = PR_FALSE; } if (gLogPath && !theSink) { static nsLoggingSink gLoggingSink; PRIntn theFlags = PR_CREATE_FILE | PR_RDWR; // Open the record file. PRFileDesc *theLogFile = PR_Open(gLogPath, theFlags, 0); gLoggingSink.SetOutputStream(theLogFile, PR_TRUE); theSink = &gLoggingSink; } return theSink; } #endif CNavDTD::~CNavDTD() { delete mBodyContext; delete mTempContext; #ifdef NS_DEBUG if (mSink) { nsLoggingSink *theLogSink = GetLoggingSink(); if (mSink == theLogSink) { theLogSink->ReleaseProxySink(); } } #endif NS_IF_RELEASE(mSink); } nsresult CNavDTD::CreateNewInstance(nsIDTD** aInstancePtrResult) { nsresult result = NS_NewNavHTMLDTD(aInstancePtrResult); NS_ENSURE_SUCCESS(result, result); CNavDTD* dtd = NS_STATIC_CAST(CNavDTD*, *aInstancePtrResult); dtd->mDTDMode = mDTDMode; dtd->mParserCommand = mParserCommand; dtd->mDocType = mDocType; return result; } NS_IMETHODIMP_(eAutoDetectResult) CNavDTD::CanParse(CParserContext& aParserContext) { NS_ASSERTION(!aParserContext.mMimeType.IsEmpty(), "How'd we get here with an unknown type?"); if (aParserContext.mParserCommand != eViewSource && aParserContext.mDocType != eXML) { // This means that we're // 1) Looking at a type the parser claimed to know how to handle (so XML // or HTML or a plaintext type) // 2) Not looking at XML // // Therefore, we want to handle this data with this DTD return ePrimaryDetect; } return eUnknownDetect; } nsresult CNavDTD::WillBuildModel(const CParserContext& aParserContext, nsITokenizer* aTokenizer, nsIContentSink* aSink) { nsresult result = NS_OK; mFilename = aParserContext.mScanner->GetFilename(); mFlags = NS_DTD_FLAG_ENABLE_RESIDUAL_STYLE; mLineNumber = 1; mDTDMode = aParserContext.mDTDMode; mParserCommand = aParserContext.mParserCommand; mMimeType = aParserContext.mMimeType; mDocType = aParserContext.mDocType; mTokenizer = aTokenizer; mBodyContext->SetNodeAllocator(&mNodeAllocator); if (!aParserContext.mPrevContext && aSink) { STOP_TIMER(); MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::WillBuildModel(), this=%p\n", this)); result = aSink->WillBuildModel(); MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::WillBuildModel(), this=%p\n", this)); START_TIMER(); if (NS_SUCCEEDED(result) && !mSink) { result = CallQueryInterface(aSink, &mSink); if (NS_FAILED(result)) { mFlags |= NS_DTD_FLAG_STOP_PARSING; return result; } } // Let's see if the environment is set up for us to write output to // a logging sink. If so, then we'll create one, and make it the // proxy for the real sink we're given from the parser. #ifdef NS_DEBUG nsLoggingSink *theLogSink = GetLoggingSink(); if (theLogSink) { theLogSink->SetProxySink(mSink); mSink = theLogSink; } #endif if (mSink) { PRBool enabled = PR_TRUE; mSink->IsEnabled(eHTMLTag_frameset, &enabled); if (enabled) { mFlags |= NS_IPARSER_FLAG_FRAMES_ENABLED; } mSink->IsEnabled(eHTMLTag_script, &enabled); if (enabled) { mFlags |= NS_IPARSER_FLAG_SCRIPT_ENABLED; } } } return result; } nsresult CNavDTD::BuildModel(nsIParser* aParser, nsITokenizer* aTokenizer, nsITokenObserver* anObserver, nsIContentSink* aSink) { NS_PRECONDITION(mBodyContext != nsnull, "Create a context before calling build model"); nsresult result = NS_OK; if (!aTokenizer || !aParser) { return NS_OK; } nsITokenizer* oldTokenizer = mTokenizer; mTokenizer = aTokenizer; mParser = (nsParser*)aParser; mTokenAllocator = mTokenizer->GetTokenAllocator(); if (!mSink) { return (mFlags & NS_DTD_FLAG_STOP_PARSING) ? NS_ERROR_HTMLPARSER_STOPPARSING : result; } if (mBodyContext->GetCount() == 0) { CToken* tempToken; if (ePlainText == mDocType) { tempToken = mTokenAllocator->CreateTokenOfType(eToken_start, eHTMLTag_pre); if (tempToken) { mTokenizer->PushTokenFront(tempToken); } } // Always open a body if frames are disabled. if (!(mFlags & NS_IPARSER_FLAG_FRAMES_ENABLED)) { tempToken = mTokenAllocator->CreateTokenOfType(eToken_start, eHTMLTag_body, NS_LITERAL_STRING("body")); if (tempToken) { mTokenizer->PushTokenFront(tempToken); } } // If the content model is empty, then begin by opening . CStartToken* theToken = (CStartToken*)mTokenizer->GetTokenAt(0); if (theToken) { eHTMLTags theTag = (eHTMLTags)theToken->GetTypeID(); eHTMLTokenTypes theType = eHTMLTokenTypes(theToken->GetTokenType()); if (theTag != eHTMLTag_html || theType != eToken_start) { tempToken = mTokenAllocator->CreateTokenOfType(eToken_start, eHTMLTag_html, NS_LITERAL_STRING("html")); if (tempToken) { mTokenizer->PushTokenFront(tempToken); } } } else { tempToken = mTokenAllocator->CreateTokenOfType(eToken_start, eHTMLTag_html, NS_LITERAL_STRING("html")); if (tempToken) { mTokenizer->PushTokenFront(tempToken); } } } mSink->WillProcessTokens(); while (NS_SUCCEEDED(result)) { if (!(mFlags & NS_DTD_FLAG_STOP_PARSING)) { CToken* theToken = mTokenizer->PopToken(); if (!theToken) { break; } result = HandleToken(theToken, aParser); } else { result = NS_ERROR_HTMLPARSER_STOPPARSING; 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 is processing a script's document.write we should not // allow it to be interrupted. // We also need to make sure that an interruption does not override // a request to block the parser. if (mParser->CanInterrupt() && !IsParserInDocWrite() && NS_SUCCEEDED(result)) { result = NS_ERROR_HTMLPARSER_INTERRUPTED; break; } } } mTokenizer = oldTokenizer; return result; } nsresult CNavDTD::BuildNeglectedTarget(eHTMLTags aTarget, eHTMLTokenTypes aType, nsIParser* aParser, nsIContentSink* aSink) { NS_ASSERTION(mTokenizer, "tokenizer is null! unable to build target."); NS_ASSERTION(mTokenAllocator, "unable to create tokens without an allocator."); if (!mTokenizer || !mTokenAllocator) { return NS_OK; } CToken* target = mTokenAllocator->CreateTokenOfType(aType, aTarget); mTokenizer->PushTokenFront(target); return BuildModel(aParser, mTokenizer, 0, aSink); } nsresult CNavDTD::DidBuildModel(nsresult anErrorCode, PRBool aNotifySink, nsIParser* aParser, nsIContentSink* aSink) { if (!aSink) { return NS_OK; } nsresult result = NS_OK; if (aParser && aNotifySink) { if (NS_OK == anErrorCode) { if (!(mFlags & NS_DTD_FLAG_HAS_MAIN_CONTAINER)) { // This document is not a frameset document, however, it did not contain // a body tag either. So, make one!. Note: Body tag is optional per spec.. // Also note: We ignore the return value of BuildNeglectedTarget, we // can't reasonably respond to errors (or requests to block) at this // point in the parsing process. BuildNeglectedTarget(eHTMLTag_body, eToken_start, aParser, aSink); } if (mFlags & NS_DTD_FLAG_MISPLACED_CONTENT) { // Looks like the misplaced contents are not processed yet. // Here is our last chance to handle the misplaced content. // Keep track of the top index. PRInt32 topIndex = mBodyContext->mContextTopIndex; // Loop until we've really consumed all of our misplaced content. do { mFlags &= ~NS_DTD_FLAG_MISPLACED_CONTENT; // mContextTopIndex refers to the misplaced content's legal parent index. result = HandleSavedTokens(mBodyContext->mContextTopIndex); NS_ENSURE_SUCCESS(result, result); // If we start handling misplaced content while handling misplaced // content, mContextTopIndex gets modified. However, this new index // necessarily points to the middle of a closed tag (since we close // new tags after handling the misplaced content). So we restore the // insertion point after every iteration. mBodyContext->mContextTopIndex = topIndex; } while (mFlags & NS_DTD_FLAG_MISPLACED_CONTENT); mBodyContext->mContextTopIndex = -1; } // Now let's disable style handling to save time when closing remaining // stack members. mFlags &= ~NS_DTD_FLAG_ENABLE_RESIDUAL_STYLE; while (mBodyContext->GetCount() > 0) { result = CloseContainersTo(mBodyContext->Last(), PR_FALSE); if (NS_FAILED(result)) { //No matter what, you need to call did build model. aSink->DidBuildModel(); return result; } } } else { // If you're here, then an error occured, but we still have nodes on the stack. // At a minimum, we should grab the nodes and recycle them. // Just to be correct, we'll also recycle the nodes. while (mBodyContext->GetCount() > 0) { nsEntryStack* theChildStyles = 0; nsCParserNode* theNode = mBodyContext->Pop(theChildStyles); IF_DELETE(theChildStyles, &mNodeAllocator); IF_FREE(theNode, &mNodeAllocator); } } // Now make sure the misplaced content list is empty, // by forcefully recycling any tokens we might find there. CToken* theToken = 0; while ((theToken = (CToken*)mMisplacedContent.Pop())) { IF_FREE(theToken, mTokenAllocator); } } // No matter what, you need to call did build model. return aSink->DidBuildModel(); } NS_IMETHODIMP_(void) CNavDTD::Terminate() { mFlags |= NS_DTD_FLAG_STOP_PARSING; } NS_IMETHODIMP_(PRInt32) CNavDTD::GetType() { return NS_IPARSER_FLAG_HTML; } /** * Text and some tags require a body when they're added, this function returns * true for those tags. * * @param aToken The current token that we care about. * @param aTokenizer A tokenizer that we can get the tags attributes off of. * @return PR_TRUE if aToken does indeed force the body to open. */ static PRBool DoesRequireBody(CToken* aToken, nsITokenizer* aTokenizer) { PRBool result = PR_FALSE; if (aToken) { eHTMLTags theTag = (eHTMLTags)aToken->GetTypeID(); if (gHTMLElements[theTag].HasSpecialProperty(kRequiresBody)) { if (theTag == eHTMLTag_input) { // IE & Nav4x opens up a body for type=text - Bug 66985 PRInt32 ac = aToken->GetAttributeCount(); for(PRInt32 i = 0; i < ac; ++i) { CAttributeToken* attr = NS_STATIC_CAST(CAttributeToken*, aTokenizer->GetTokenAt(i)); const nsSubstring& name = attr->GetKey(); const nsAString& value = attr->GetValue(); if ((name.EqualsLiteral("type") || name.EqualsLiteral("TYPE")) && !(value.EqualsLiteral("hidden") || value.EqualsLiteral("HIDDEN"))) { result = PR_TRUE; break; } } } else { result = PR_TRUE; } } } return result; } /** * Returns whether or not there is a tag of type aType open on aContext. */ static PRBool HasOpenTagOfType(PRInt32 aType, const nsDTDContext& aContext) { PRInt32 count = aContext.GetCount(); while (--count >= 0) { if (gHTMLElements[aContext.TagAt(count)].IsMemberOf(aType)) { return PR_TRUE; } } return PR_FALSE; } nsresult CNavDTD::HandleToken(CToken* aToken, nsIParser* aParser) { if (!aToken) { return NS_OK; } nsresult result = NS_OK; CHTMLToken* theToken = NS_STATIC_CAST(CHTMLToken*, aToken); eHTMLTokenTypes theType = eHTMLTokenTypes(theToken->GetTokenType()); eHTMLTags theTag = (eHTMLTags)theToken->GetTypeID(); aToken->SetLineNumber(mLineNumber); if (!IsParserInDocWrite()) { mLineNumber += aToken->GetNewlineCount(); } if (mFlags & NS_DTD_FLAG_MISPLACED_CONTENT) { // Included TD & TH to fix Bug# 20797 static eHTMLTags gLegalElements[] = { eHTMLTag_table, eHTMLTag_thead, eHTMLTag_tbody, eHTMLTag_tr, eHTMLTag_td, eHTMLTag_th, eHTMLTag_tfoot }; // Don't even try processing misplaced tokens if we're already // handling misplaced content. See bug 269095 if (mFlags & NS_DTD_FLAG_IN_MISPLACED_CONTENT) { PushIntoMisplacedStack(theToken); return NS_OK; } eHTMLTags theParentTag = mBodyContext->Last(); theTag = (eHTMLTags)theToken->GetTypeID(); if (FindTagInSet(theTag, gLegalElements, NS_ARRAY_LENGTH(gLegalElements)) || (gHTMLElements[theParentTag].CanContain(theTag, mDTDMode) && // Here's a problem. If theTag is legal in here, we don't move it // out. So if we're moving stuff out of here, the parent of theTag // gets closed at this point. But some things are legal // _everywhere_ and hence would effectively close out misplaced // content in tables. This is undesirable, so treat them as // illegal here so they'll be shipped out with their parents and // siblings. See bug 40855 for an explanation (that bug was for // comments, but the same issues arise with whitespace, newlines, // noscript, etc). Script is special, though. Shipping it out // breaks document.write stuff. See bug 243064. (!gHTMLElements[theTag].HasSpecialProperty(kLegalOpen) || theTag == eHTMLTag_script))) { // Reset the state since all the misplaced tokens are about to get // handled. mFlags &= ~NS_DTD_FLAG_MISPLACED_CONTENT; result = HandleSavedTokens(mBodyContext->mContextTopIndex); NS_ENSURE_SUCCESS(result, result); mBodyContext->mContextTopIndex = -1; } else { PushIntoMisplacedStack(theToken); return result; } } /* * This section of code is used to "move" misplaced content from one location * in our document model to another. (Consider what would happen if we found a *
tag in the head.) To move content, we throw it onto the * misplacedcontent deque until we can deal with it. */ switch(theTag) { case eHTMLTag_html: case eHTMLTag_noframes: case eHTMLTag_noscript: case eHTMLTag_script: case eHTMLTag_doctypeDecl: case eHTMLTag_instruction: break; default: if (!gHTMLElements[eHTMLTag_html].SectionContains(theTag, PR_FALSE)) { if (!(mFlags & (NS_DTD_FLAG_HAS_MAIN_CONTAINER | NS_DTD_FLAG_ALTERNATE_CONTENT))) { // For bug examples from this code, see bugs: 18928, 20989. // At this point we know the body/frameset aren't open. // If the child belongs in the head, then handle it (which may open the head); // otherwise, push it onto the misplaced stack. PRBool isExclusive = PR_FALSE; PRBool theChildBelongsInHead = gHTMLElements[eHTMLTag_head].IsChildOfHead(theTag, isExclusive); if (theChildBelongsInHead && !isExclusive) { if (mMisplacedContent.GetSize() == 0) { // This tag can either be in the body or the head. Since // there is no indication that the body should be open, // put this token in the head. break; } // Otherwise, we have received some indication that the body is // "open", so push this token onto the misplaced content stack. theChildBelongsInHead = PR_FALSE; } if (!theChildBelongsInHead) { eHTMLTags top = mBodyContext->Last(); NS_ASSERTION(top != eHTMLTag_userdefined, "Userdefined tags should act as leaves in the head"); if (top != eHTMLTag_html && gHTMLElements[top].CanContain(theTag, mDTDMode)) { // Some tags (such as
There PRBool theParentContains = -1; if (!CanOmit(theParentTag, theChildTag, theParentContains)) { CToken* theStartToken = mTokenAllocator->CreateTokenOfType(eToken_start, theChildTag); // This check for NS_DTD_FLAG_IN_MISPLACED_CONTENT was added // to fix bug 142965. if (!(mFlags & NS_DTD_FLAG_IN_MISPLACED_CONTENT)) { // We're not handling misplaced content right now, just push // these new tokens back on the stack and handle them in the // regular flow of HandleToken. IF_HOLD(aToken); mTokenizer->PushTokenFront(aToken); mTokenizer->PushTokenFront(theStartToken); } else { // Oops, we're in misplaced content. Handle these tokens // directly instead of trying to push them onto the tokenizer // stack. result = HandleToken(theStartToken, mParser); NS_ENSURE_SUCCESS(result, result); result = HandleToken(aToken, mParser); } } } return result; } if (result == NS_OK) { eHTMLTags theTarget = FindAutoCloseTargetForEndTag(theChildTag, *mBodyContext, mDTDMode); if (eHTMLTag_unknown != theTarget) { result = CloseContainersTo(theTarget, PR_FALSE); } } } } break; } return result; } /** * This method will be triggered when the end of a table is * encountered. Its primary purpose is to process all the * bad-contents pertaining a particular table. The position * of the table is the token bank ID. * * @update harishd 03/24/99 * @param aTag - This ought to be a table tag * */ nsresult CNavDTD::HandleSavedTokens(PRInt32 anIndex) { NS_PRECONDITION(mBodyContext != nsnull && mBodyContext->GetCount() > 0, "invalid context"); nsresult result = NS_OK; if (anIndex > kNotFound) { PRInt32 theBadTokenCount = mMisplacedContent.GetSize(); if (theBadTokenCount > 0) { mFlags |= NS_DTD_FLAG_IN_MISPLACED_CONTENT; if (!mTempContext && !(mTempContext = new nsDTDContext())) { return NS_ERROR_OUT_OF_MEMORY; } CToken* theToken; eHTMLTags theTag; PRInt32 attrCount; PRInt32 theTopIndex = anIndex + 1; PRInt32 theTagCount = mBodyContext->GetCount(); if (mSink && mSink->IsFormOnStack()) { // Do this to synchronize dtd stack and the sink stack. // Note: FORM is never on the dtd stack because its always // considered as a leaf. However, in the sink FORM can either // be a container or a leaf. Therefore, we have to check // with the sink -- Ref: Bug 20087. ++anIndex; } STOP_TIMER() MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::HandleSavedTokensAbove(), this=%p\n", this)); // Pause the main context and switch to the new context. mSink->BeginContext(anIndex); MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::HandleSavedTokensAbove(), this=%p\n", this)); START_TIMER() // The body context should contain contents only upto the marked position. mBodyContext->MoveEntries(*mTempContext, theTagCount - theTopIndex); // Now flush out all the bad contents. while (theBadTokenCount-- > 0){ theToken = (CToken*)mMisplacedContent.PopFront(); if (theToken) { theTag = (eHTMLTags)theToken->GetTypeID(); attrCount = theToken->GetAttributeCount(); // Put back attributes, which once got popped out, into the tokenizer for (PRInt32 j = 0; j < attrCount; ++j) { CToken* theAttrToken = (CToken*)mMisplacedContent.PopFront(); if (theAttrToken) { mTokenizer->PushTokenFront(theAttrToken); } theBadTokenCount--; } if (eToken_end == theToken->GetTokenType()) { // Ref: Bug 25202 // Make sure that the BeginContext() is ended only by the call to // EndContext(). Ex: