/* -*- 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. #define ENABLE_RESIDUALSTYLE #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" //this is here for debug reasons... #include "prtypes.h" //this is here for debug reasons... #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_IID(kISupportsIID, NS_ISUPPORTS_IID); static NS_DEFINE_IID(kIDTDIID, NS_IDTD_IID); static NS_DEFINE_IID(kClassIID, NS_INAVHTML_DTD_IID); 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 /************************************************************************ And now for the main class -- CNavDTD... ************************************************************************/ #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) /** * This method gets called as part of our COM-like interfaces. * Its purpose is to create an interface to parser object * of some type. * * @update gess 4/8/98 * @param nsIID id of object to discover * @param aInstancePtr ptr to newly discovered interface * @return NS_xxx result code */ nsresult CNavDTD::QueryInterface(const nsIID& aIID, void** aInstancePtr) { if (NULL == aInstancePtr) { return NS_ERROR_NULL_POINTER; } if(aIID.Equals(kISupportsIID)) { //do IUnknown... *aInstancePtr = (nsIDTD*)(this); } else if(aIID.Equals(kIDTDIID)) { //do IParser base class... *aInstancePtr = (nsIDTD*)(this); } else if(aIID.Equals(kClassIID)) { //do this class... *aInstancePtr = (CNavDTD*)(this); } else { *aInstancePtr=0; return NS_NOINTERFACE; } NS_ADDREF_THIS(); return NS_OK; } NS_IMPL_ADDREF(CNavDTD) NS_IMPL_RELEASE(CNavDTD) /** * Default constructor * * @update gess 4/9/98 * @param * @return */ CNavDTD::CNavDTD() : nsIDTD(), mMisplacedContent(0), mSink(0), mTokenAllocator(0), 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) { mBodyContext=new nsDTDContext(); } /** * * @update gess1/8/99 * @param * @return */ const nsIID& CNavDTD::GetMostDerivedIID(void)const { return kClassIID; } #ifdef NS_DEBUG nsLoggingSink* GetLoggingSink() { //these are used when you want to generate a log file for contentsink construction... static PRBool checkForPath=PR_TRUE; static nsLoggingSink *theSink=0; static const char* gLogPath=0; if(checkForPath) { // we're only going to check the environment once per session. gLogPath = /* "c:/temp/parse.log"; */ PR_GetEnv("PARSE_LOGFILE"); checkForPath=PR_FALSE; } if(gLogPath && (!theSink)) { static nsLoggingSink gLoggingSink; PRIntn theFlags = 0; // create the file exists, only open for read/write // otherwise, create it if(PR_Access(gLogPath,PR_ACCESS_EXISTS) != PR_SUCCESS) theFlags = PR_CREATE_FILE; theFlags |= PR_RDWR; // open the record file PRFileDesc *theLogFile = PR_Open(gLogPath,theFlags,0); gLoggingSink.SetOutputStream(theLogFile,PR_TRUE); theSink=&gLoggingSink; } return theSink; } #endif /** * Default destructor * * @update gess 4/9/98 * @param * @return */ CNavDTD::~CNavDTD(){ if(mBodyContext) { delete mBodyContext; mBodyContext=0; } if(mTempContext) { delete mTempContext; mTempContext=0; } #ifdef NS_DEBUG if(mSink) { nsLoggingSink *theLogSink=GetLoggingSink(); if(mSink==theLogSink) { theLogSink->ReleaseProxySink(); } } #endif NS_IF_RELEASE(mSink); } /** * Call this method if you want the DTD to construct a fresh * instance of itself. * @update gess 25May2000 * @param * @return */ 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; } /** * This method is called to determine if the given DTD can parse * a document in a given source-type. * NOTE: Parsing always assumes that the end result will involve * storing the result in the main content model. * @param aParserContext -- the context for this document (knows * the content type, document type, parser command, etc). * @return eUnknownDetect if you don't know how to parse it, * eValidDetect if you do, but someone may have a better idea, * ePrimaryDetect if you think you know best */ 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; } /** * The parser uses a code sandwich to wrap the parsing process. Before * the process begins, WillBuildModel() is called. Afterwards the parser * calls DidBuildModel(). * @update rickg 03.20.2000 * @param aParserContext * @param aSink * @return error code (almost always 0) */ 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; // residual style is always on. This will also reset the flags 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; } /** * This is called when it's time to read as many tokens from the tokenizer * as you can. Not all tokens may make sense, so you may not be able to * read them all (until more come in later). * * @update gess5/18/98 * @param aParser is the parser object that's driving this process * @return error code (almost always NS_OK) */ 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) { nsITokenizer* oldTokenizer = mTokenizer; mTokenizer = aTokenizer; mParser = (nsParser*)aParser; mTokenAllocator = mTokenizer->GetTokenAllocator(); if (mSink) { if (mBodyContext->GetCount() == 0) { CStartToken* theToken=nsnull; if(ePlainText==mDocType) { //we do this little trick for text files, in both normal and viewsource mode... theToken=NS_STATIC_CAST(CStartToken*,mTokenAllocator->CreateTokenOfType(eToken_start,eHTMLTag_pre)); if(theToken) { mTokenizer->PushTokenFront(theToken); } } // always open a body if frames are disabled.... if(!(mFlags & NS_IPARSER_FLAG_FRAMES_ENABLED)) { theToken=NS_STATIC_CAST(CStartToken*,mTokenAllocator->CreateTokenOfType(eToken_start,eHTMLTag_body,NS_LITERAL_STRING("body"))); mTokenizer->PushTokenFront(theToken); } //if the content model is empty, then begin by opening ... theToken = (CStartToken*)mTokenizer->GetTokenAt(0); if (theToken) { eHTMLTags theTag = (eHTMLTags)theToken->GetTypeID(); eHTMLTokenTypes theType = eHTMLTokenTypes(theToken->GetTokenType()); if (theTag != eHTMLTag_html || theType != eToken_start) { theToken = NS_STATIC_CAST(CStartToken*,mTokenAllocator->CreateTokenOfType(eToken_start,eHTMLTag_html,NS_LITERAL_STRING("html"))); if (theToken) { mTokenizer->PushTokenFront(theToken); //this token should get pushed on the context stack. } } } else { theToken = NS_STATIC_CAST(CStartToken*,mTokenAllocator->CreateTokenOfType(eToken_start,eHTMLTag_html,NS_LITERAL_STRING("html"))); if (theToken) { mTokenizer->PushTokenFront(theToken); //this token should get pushed on the context stack. } } } mSink->WillProcessTokens(); while (NS_SUCCEEDED(result)) { if (!(mFlags & NS_DTD_FLAG_STOP_PARSING)) { CToken* theToken = mTokenizer->PopToken(); if (theToken) { result = HandleToken(theToken,aParser); } else break; } 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; } } }//while mTokenizer = oldTokenizer; } else { result = mFlags & NS_DTD_FLAG_STOP_PARSING ? NS_ERROR_HTMLPARSER_STOPPARSING : result; } } return result; } /** * @param aTarget - Tag that was neglected in the document. * @param aType - Specifies the type of the target. Ex. start, end, text, etc. * @param aParser - Parser to drive this process * @param aSink - HTML Content sink */ 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; CHTMLToken* target = NS_STATIC_CAST(CHTMLToken*, mTokenAllocator->CreateTokenOfType(aType, aTarget)); mTokenizer->PushTokenFront(target); return BuildModel(aParser, mTokenizer, 0, aSink); } /** * * @update gess5/18/98 * @param * @return */ 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.. result = BuildNeglectedTarget(eHTMLTag_body, eToken_start, aParser, aSink); NS_ENSURE_SUCCESS(result , result); } 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); } } //if aparser //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; } /** * --- Backwards compatibility --- * Use this method to determine if the tag in question needs a BODY. * --- */ 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; iGetTokenAt(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; } }//for } 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; } /** * This big dispatch method is used to route token handler calls to the right place. * What's wrong with it? This table, and the dispatch methods themselves need to be * moved over to the delegate. Ah, so much to do... * * @update gess 12/1/99 * @param aToken * @param aParser * @return */ nsresult CNavDTD::HandleToken(CToken* aToken,nsIParser* aParser){ nsresult result=NS_OK; if(aToken) { 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}; if(theToken) { // 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 result; } 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))) { mFlags &= ~NS_DTD_FLAG_MISPLACED_CONTENT; // reset the state since all the misplaced tokens are about to get handled. 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 and text 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 and so we won't be // closing the wrong tag. if (mBodyContext->Last() != eHTMLTag_script) { // Except if we're here, then there's probably a stray script tag. NS_ASSERTION(mBodyContext->LastOf(eHTMLTag_script) == kNotFound, "Mishandling scripts in CNavDTD"); break; } mBodyContext->Pop(); result = CloseContainer(eHTMLTag_script, theChildTag, PR_FALSE); break; default: { //now check to see if this token should be omitted, or //if it's gated from closing by the presence of another tag. if(gHTMLElements[theChildTag].CanOmitEndTag()) { PopStyle(theChildTag); } else { eHTMLTags theParentTag=mBodyContext->Last(); if(kNotFound==nsHTMLElement::GetIndexOfChildOrSynonym(*mBodyContext,theChildTag)) { // Ref: bug 30487 // Make sure that we don't cross boundaries, of certain elements, // to close stylistic information. // Ex.
some text... // In the above ex. the orphaned FONT tag, inside TD, should cross TD boundaryto // close the FONT tag above TABLE. static eHTMLTags gBarriers[]={eHTMLTag_thead,eHTMLTag_tbody,eHTMLTag_tfoot,eHTMLTag_table}; if(!FindTagInSet(theParentTag,gBarriers,sizeof(gBarriers)/sizeof(theParentTag))) { if(nsHTMLElement::IsResidualStyleTag(theChildTag)) { mBodyContext->RemoveStyle(theChildTag); // fix bug 77746 } } // If the bit kHandleStrayTag is set then we automatically open up a matching // start tag ( compatibility ). Currently this bit is set on P tag. // This also fixes Bug: 22623 if(gHTMLElements[theChildTag].HasSpecialProperty(kHandleStrayTag) && mDTDMode != eDTDMode_full_standards && mDTDMode != eDTDMode_almost_standards) { // Oh boy!! we found a "stray" tag. Nav4.x and IE introduce line break in // such cases. So, let's simulate that effect for compatibility. // Ex. Hello

There PRBool theParentContains=-1; //set to -1 to force canomit to recompute. if(!CanOmit(theParentTag,theChildTag,theParentContains)) { CHTMLToken* theStartToken = NS_STATIC_CAST(CHTMLToken*,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); //put this end token back... mTokenizer->PushTokenFront(theStartToken); //put the new token onto the stack... } 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) { if (nsHTMLElement::IsResidualStyleTag(theChildTag)) { result=OpenTransientStyles(theChildTag); if(NS_FAILED(result)) { return result; } } 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==nsnull) mTempContext=new nsDTDContext(); 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;jPushTokenFront(theAttrToken); } theBadTokenCount--; } if(eToken_end==theToken->GetTokenType()) { // Ref: Bug 25202 // Make sure that the BeginContext() is ended only by the call to // EndContext(). Ex:
. // In the Ex. above should not close
above table. // Doing so will cause the current context to get closed prematurely. PRInt32 theIndex=mBodyContext->LastOf(theTag); if(theIndex!=kNotFound && theIndex<=mBodyContext->mContextTopIndex) { IF_FREE(theToken, mTokenAllocator); continue; } } result=HandleToken(theToken,mParser); } }//while if(theTopIndex != mBodyContext->GetCount()) { CloseContainersTo(theTopIndex,mBodyContext->TagAt(theTopIndex),PR_TRUE); } // Bad-contents were successfully processed. Now, itz time to get // back to the original body context state. mTempContext->MoveEntries(*mBodyContext, theTagCount - theTopIndex); STOP_TIMER() MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::HandleSavedTokensAbove(), this=%p\n", this)); // Terminate the new context and switch back to the main context mSink->EndContext(anIndex); MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::HandleSavedTokensAbove(), this=%p\n", this)); START_TIMER() mFlags &= ~NS_DTD_FLAG_IN_MISPLACED_CONTENT; } } return result; } /** * This method gets called when an entity token has been * encountered in the parse process. * * @update gess 3/25/98 * @param aToken -- next (start) token to be handled * @return PR_TRUE if all went well; PR_FALSE if error occured */ nsresult CNavDTD::HandleEntityToken(CToken* aToken) { NS_PRECONDITION(0!=aToken,kNullToken); nsresult result=NS_OK; const nsSubstring& theStr = aToken->GetStringValue(); if((kHashsign!=theStr.First()) && (-1==nsHTMLEntities::EntityToUnicode(theStr))){ //if you're here we have a bogus entity. //convert it into a text token. CToken *theToken=0; nsAutoString entityName; entityName.AssignLiteral("&"); entityName.Append(theStr); //should append the entity name; fix bug 51161. theToken = mTokenAllocator->CreateTokenOfType(eToken_text,eHTMLTag_text,entityName); return HandleToken(theToken,mParser); //theToken should get recycled automagically... } eHTMLTags theParentTag=mBodyContext->Last(); nsCParserNode* theNode=mNodeAllocator.CreateNode(aToken, mTokenAllocator); if(theNode) { PRBool theParentContains=-1; //set to -1 to force CanOmit to recompute... if(CanOmit(theParentTag,eHTMLTag_entity,theParentContains)) { eHTMLTags theCurrTag=(eHTMLTags)aToken->GetTypeID(); result=HandleOmittedTag(aToken,theCurrTag,theParentTag,theNode); } else { result=AddLeaf(theNode); } IF_FREE(theNode, &mNodeAllocator); } return result; } /** * This method gets called when a comment token has been * encountered in the parse process. After making sure * we're somewhere in the body, we handle the comment * in the same code that we use for text. * * @update gess 3/25/98 * @param aToken -- next (start) token to be handled * @return PR_TRUE if all went well; PR_FALSE if error occured */ nsresult CNavDTD::HandleCommentToken(CToken* aToken) { NS_PRECONDITION(0!=aToken,kNullToken); nsresult result=NS_OK; nsCParserNode* theNode=mNodeAllocator.CreateNode(aToken, mTokenAllocator); if(theNode) { STOP_TIMER(); MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::HandleCommentToken(), this=%p\n", this)); result=(mSink) ? mSink->AddComment(*theNode) : NS_OK; IF_FREE(theNode, &mNodeAllocator); MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::HandleCommentToken(), this=%p\n", this)); START_TIMER(); } return result; } /** * This method gets called when an attribute token has been * encountered in the parse process. This is an error, since * all attributes should have been accounted for in the prior * start or end tokens * * @update gess 3/25/98 * @param aToken -- next (start) token to be handled * @return PR_TRUE if all went well; PR_FALSE if error occured */ nsresult CNavDTD::HandleAttributeToken(CToken* aToken) { NS_PRECONDITION(0!=aToken,kNullToken); NS_ERROR("attribute encountered -- this shouldn't happen unless the attribute was not part of a start tag!"); return NS_OK; } /** * This method gets called when an "instruction" token has been * encountered in the parse process. * * @update gess 3/25/98 * @param aToken -- next (start) token to be handled * @return PR_TRUE if all went well; PR_FALSE if error occured */ nsresult CNavDTD::HandleProcessingInstructionToken(CToken* aToken){ NS_PRECONDITION(0!=aToken,kNullToken); nsresult result=NS_OK; nsCParserNode* theNode=mNodeAllocator.CreateNode(aToken, mTokenAllocator); if(theNode) { STOP_TIMER(); MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::HandleProcessingInstructionToken(), this=%p\n", this)); result=(mSink) ? mSink->AddProcessingInstruction(*theNode) : NS_OK; IF_FREE(theNode, &mNodeAllocator); MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::HandleProcessingInstructionToken(), this=%p\n", this)); START_TIMER(); } return result; } /** * This method gets called when a DOCTYPE token has been * encountered in the parse process. * * @update harishd 09/02/99 * @param aToken -- The very first token to be handled * @return PR_TRUE if all went well; PR_FALSE if error occured */ nsresult CNavDTD::HandleDocTypeDeclToken(CToken* aToken){ NS_PRECONDITION(0!=aToken,kNullToken); nsresult result=NS_OK; CDoctypeDeclToken* theToken = NS_STATIC_CAST(CDoctypeDeclToken*,aToken); nsAutoString docTypeStr(theToken->GetStringValue()); if (!IsParserInDocWrite()) { mLineNumber += docTypeStr.CountChar(kNewLine); } PRInt32 len=docTypeStr.Length(); PRInt32 pos=docTypeStr.RFindChar(kGreaterThan); if(pos>-1) { docTypeStr.Cut(pos,len-pos);// First remove '>' from the end. } docTypeStr.Cut(0,2); // Now remove "SetStringValue(docTypeStr); nsCParserNode* theNode=mNodeAllocator.CreateNode(aToken, mTokenAllocator); if(theNode) { STOP_TIMER(); MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::HandleDocTypeDeclToken(), this=%p\n", this)); result = (mSink)? mSink->AddDocTypeDecl(*theNode):NS_OK; IF_FREE(theNode, &mNodeAllocator); MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::HandleDocTypeDeclToken(), this=%p\n", this)); START_TIMER(); } return result; } /** * Retrieve the attributes for this node, and add then into * the node. * * @update gess4/22/98 * @param aNode is the node you want to collect attributes for * @param aCount is the # of attributes you're expecting * @return error code (should be 0) */ nsresult CNavDTD::CollectAttributes(nsIParserNode *aNode,eHTMLTags aTag,PRInt32 aCount){ int attr=0; nsresult result=NS_OK; int theAvailTokenCount=mTokenizer->GetCount(); if(aCount<=theAvailTokenCount) { CToken* theToken=0; for(attr=0;attrPopToken(); if(theToken) { eHTMLTokenTypes theType=eHTMLTokenTypes(theToken->GetTokenType()); if(theType!=eToken_attribute) { // If you're here then it means that the token does not // belong to this node. Put the token back into the tokenizer // and let it go thro' the regular path. Bug: 59189. mTokenizer->PushTokenFront(theToken); break; } if (IsParserInDocWrite()) { mLineNumber += theToken->GetNewlineCount(); } if(aNode) { // Sanitize the key for it might contain some non-alpha-non-digit characters // at its end. Ex.
  • so that the bullets // inherit the proper colors. PRBool li_tag = aTag == eHTMLTag_li; if (rs_tag || li_tag) { /*********************************************************************** * Here's an interesting problem: * * If there's an on the RS-stack, and you're trying to open * another , the one on the RS-stack should be discarded. * * I'm updating OpenTransientStyles to throw old 's away. * ***********************************************************************/ OpenTransientStyles(aTag, !li_tag); } switch (aTag) { case eHTMLTag_html: result=OpenHTML(aNode); break; case eHTMLTag_head: result=OpenHead(aNode); break; case eHTMLTag_body: { eHTMLTags theParent=mBodyContext->Last(); if (!gHTMLElements[aTag].IsSpecialParent(theParent)) { mFlags |= NS_DTD_FLAG_HAS_OPEN_BODY; result = OpenBody(aNode); } else { done = PR_FALSE; } } break; case eHTMLTag_map: result = OpenMap(aNode); break; case eHTMLTag_form: result = OpenForm(aNode); break; case eHTMLTag_frameset: result = OpenFrameset(aNode); break; case eHTMLTag_noembed: // is unconditionally alternate content. done = PR_FALSE; mFlags |= NS_DTD_FLAG_ALTERNATE_CONTENT; break; case eHTMLTag_noscript: // we want to make sure that OpenContainer gets called below since we're // not doing it here done=PR_FALSE; // If the script is disabled noscript should not be // in the content model until the layout can somehow // turn noscript's display property to block <-- bug 67899 if(mFlags & NS_IPARSER_FLAG_SCRIPT_ENABLED) { mFlags |= NS_DTD_FLAG_ALTERNATE_CONTENT; } break; case eHTMLTag_iframe: // Bug 84491 case eHTMLTag_noframes: done=PR_FALSE; if(mFlags & NS_IPARSER_FLAG_FRAMES_ENABLED) { mFlags |= NS_DTD_FLAG_ALTERNATE_CONTENT; } break; default: done=PR_FALSE; break; } if (!done) { STOP_TIMER(); MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::OpenContainer(), this=%p\n", this)); result=(mSink) ? mSink->OpenContainer(*aNode) : NS_OK; MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::OpenContainer(), this=%p\n", this)); START_TIMER(); // For residual style tags rs_tag will be true and hence // the body context will hold an extra reference to the node. mBodyContext->Push(NS_CONST_CAST(nsCParserNode*, aNode), aStyleStack, rs_tag); } return result; } /** * This method does two things: 1st, help construct * our own internal model of the content-stack; and * 2nd, pass this message on to the sink. * @update gess4/6/98 * @param aNode -- next node to be removed from our model * @param aTag -- id of tag to be closed * @param aClosedByStartTag -- ONLY TRUE if the container is being closed by opening of another container. * @return TRUE if ok, FALSE if error */ nsresult CNavDTD::CloseContainer(const eHTMLTags aTag, eHTMLTags aTarget,PRBool aClosedByStartTag) { nsresult result = NS_OK; switch (aTag) { case eHTMLTag_html: result=CloseHTML(); break; case eHTMLTag_head: result=CloseHead(); break; case eHTMLTag_body: result=CloseBody(); break; case eHTMLTag_map: result=CloseMap(); break; case eHTMLTag_form: result=CloseForm(); break; case eHTMLTag_frameset: result=CloseFrameset(); break; case eHTMLTag_iframe: case eHTMLTag_noembed: case eHTMLTag_noscript: case eHTMLTag_noframes: // switch from alternate content state to regular state mFlags &= ~NS_DTD_FLAG_ALTERNATE_CONTENT; // falling thro' intentionally.... default: STOP_TIMER(); MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::CloseContainer(), this=%p\n", this)); result = mSink ? mSink->CloseContainer(aTag) : NS_OK; // XXX Can this case really happen? // If we were dealing with a head container in the body, make sure to // close the head context now, so that body content doesn't get sucked // into the head. if (mBodyContext->GetCount() == mHeadContainerPosition) { nsresult headresult = CloseHead(); // Note: we could be assigning NS_OK into NS_OK here, but that's ok. // This test is to avoid a successful CloseHead result stomping over a // request to block the parser. if (NS_SUCCEEDED(result)) { result = headresult; } mHeadContainerPosition = -1; } MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::CloseContainer(), this=%p\n", this)); START_TIMER(); break; } return result; } /** * This method does two things: 1st, help construct * our own internal model of the content-stack; and * 2nd, pass this message on to the sink. * @update gess4/6/98 * @param anIndex * @param aTag * @param aClosedByStartTag -- if TRUE, then we're closing something because a start tag caused it * @return TRUE if ok, FALSE if error */ nsresult CNavDTD::CloseContainersTo(PRInt32 anIndex,eHTMLTags aTarget, PRBool aClosedByStartTag) { NS_PRECONDITION(mBodyContext->GetCount() > 0, kInvalidTagStackPos); nsresult result = NS_OK; if ((anIndex<mBodyContext->GetCount()) && (anIndex>=0)) { PRInt32 count = 0; while ((count = mBodyContext->GetCount()) > anIndex) { nsEntryStack* theChildStyleStack = 0; eHTMLTags theTag = mBodyContext->Last(); nsCParserNode* theNode = mBodyContext->Pop(theChildStyleStack); result = CloseContainer(theTag, aTarget,aClosedByStartTag); #ifdef ENABLE_RESIDUALSTYLE PRBool theTagIsStyle=nsHTMLElement::IsResidualStyleTag(theTag); // If the current tag cannot leak out then we shouldn't leak out of the target - Fix 40713 PRBool theStyleDoesntLeakOut = gHTMLElements[theTag].HasSpecialProperty(kNoStyleLeaksOut); if(!theStyleDoesntLeakOut) { theStyleDoesntLeakOut = gHTMLElements[aTarget].HasSpecialProperty(kNoStyleLeaksOut); } /************************************************************* Do not invoke residual style handling when dealing with alternate content. This fixes bug 25214. *************************************************************/ if(theTagIsStyle && !(mFlags & NS_DTD_FLAG_ALTERNATE_CONTENT)) { NS_ASSERTION(theNode, "residual style node should not be null"); if (!theNode) { if (theChildStyleStack) mBodyContext->PushStyles(theChildStyleStack); return NS_OK; } PRBool theTargetTagIsStyle = nsHTMLElement::IsResidualStyleTag(aTarget); if(aClosedByStartTag) { /*********************************************************** Handle closure due to new start tag. The cases we're handing here: 1. <body><b><DIV> //<b> gets pushed onto <body>.mStyles. 2. <body><a>text<a> //in this case, the target matches, so don't push style ***************************************************************************/ if (theNode->mUseCount == 0){ if (theTag != aTarget) { //don't push if thechild==theTarget if (theChildStyleStack) theChildStyleStack->PushFront(theNode); else mBodyContext->PushStyle(theNode); } } else if (theTag == aTarget && !gHTMLElements[aTarget].CanContainSelf()) { //here's a case we missed: <a><div>text<a>text</a></div> //The <div> pushes the 1st <a> onto the rs-stack, then the 2nd <a> //pops the 1st <a> from the rs-stack altogether. nsCParserNode* node = mBodyContext->PopStyle(theTag); IF_FREE(node, &mNodeAllocator); } if (theChildStyleStack) { mBodyContext->PushStyles(theChildStyleStack); } } else { //Handle closure due to another close tag. /*********************************************************** if you're here, then we're dealing with the closure of tags caused by a close tag (as opposed to an open tag). At a minimum, we should consider pushing residual styles up up the stack or popping and recycling displaced nodes. Known cases: 1. <body><b><div>text</DIV> Here the <b> will leak into <div> (see case given above), and when <div> closes the <b> is dropped since it's already residual. 2. <body><div><b>text</div> Here the <b> will leak out of the <div> and get pushed onto the RS stack for the <body>, since it originated in the <div>. 3. <body><span><b>text</span> In this case, the the <b> get's pushed onto the style stack. Later we deal with RS styles stored on the <span> 4. <body><span><b>text</i> Here we the <b>is closed by a (synonymous) style tag. In this case, the <b> is simply closed. ***************************************************************************/ if (theChildStyleStack) { if (!theStyleDoesntLeakOut) { if (theTag != aTarget) { if (theNode->mUseCount == 0) { theChildStyleStack->PushFront(theNode); } } else if (theNode->mUseCount == 1) { // This fixes bug 30885,29626. // Make sure that the node, which is about to // get released does not stay on the style stack... // Also be sure to remove the correct style off the // style stack. - Ref. bug 94208. // Ex <FONT><B><I></FONT><FONT></B></I></FONT> // Make sure that </B> removes B off the style stack. mBodyContext->RemoveStyle(theTag); } mBodyContext->PushStyles(theChildStyleStack); } else{ IF_DELETE(theChildStyleStack,&mNodeAllocator); } } else if (theNode->mUseCount == 0) { //The old version of this only pushed if the targettag wasn't style. //But that misses this case: <font><b>text</font>, where the b should leak if (aTarget != theTag) { mBodyContext->PushStyle(theNode); } } else { //Ah, at last, the final case. If you're here, then we just popped a //style tag that got onto that tag stack from a stylestack somewhere. //Pop it from the stylestack if the target is also a style tag. //Make sure to remove the matching style. In the following example //<FONT><B><I></FONT><FONT color=red></B></I></FONT> make sure that //</I> does not remove <FONT color=red> off the style stack. - bug 94208 if (theTargetTagIsStyle && theTag == aTarget) { mBodyContext->RemoveStyle(theTag); } } } } //if else { //the tag is not a style tag... if (theChildStyleStack) { if (theStyleDoesntLeakOut) IF_DELETE(theChildStyleStack,&mNodeAllocator); else mBodyContext->PushStyles(theChildStyleStack); } } #endif IF_FREE(theNode, &mNodeAllocator); } } //if return result; } /** * This method does two things: 1st, help construct * our own internal model of the content-stack; and * 2nd, pass this message on to the sink. * @update gess4/6/98 * @param aTag -- * @param aClosedByStartTag -- ONLY TRUE if the container is being closed by opening of another container. * @return TRUE if ok, FALSE if error */ nsresult CNavDTD::CloseContainersTo(eHTMLTags aTag,PRBool aClosedByStartTag){ NS_PRECONDITION(mBodyContext->GetCount() > 0, kInvalidTagStackPos); PRInt32 pos=mBodyContext->LastOf(aTag); if(kNotFound!=pos) { //the tag is indeed open, so close it. return CloseContainersTo(pos,aTag,aClosedByStartTag); } eHTMLTags theTopTag=mBodyContext->Last(); PRBool theTagIsSynonymous=((nsHTMLElement::IsResidualStyleTag(aTag)) && (nsHTMLElement::IsResidualStyleTag(theTopTag))); if(!theTagIsSynonymous){ theTagIsSynonymous=(gHTMLElements[aTag].IsMemberOf(kHeading) && gHTMLElements[theTopTag].IsMemberOf(kHeading)); } if(theTagIsSynonymous) { //if you're here, it's because we're trying to close one tag, //but a different (synonymous) one is actually open. Because this is NAV4x //compatibililty mode, we must close the one that's really open. aTag=theTopTag; pos=mBodyContext->LastOf(aTag); if(kNotFound!=pos) { //the tag is indeed open, so close it. return CloseContainersTo(pos,aTag,aClosedByStartTag); } } nsresult result=NS_OK; const TagList* theRootTags=gHTMLElements[aTag].GetRootTags(); eHTMLTags theParentTag=(theRootTags) ? theRootTags->mTags[0] : eHTMLTag_unknown; pos=mBodyContext->LastOf(theParentTag); if(kNotFound!=pos) { //the parent container is open, so close it instead result=CloseContainersTo(pos+1,aTag,aClosedByStartTag); } return result; } /** * This method does two things: 1st, help construct * our own internal model of the content-stack; and * 2nd, pass this message on to the sink. * @update gess4/6/98 * @param aNode -- next node to be added to model * @return error code; 0 means OK */ nsresult CNavDTD::AddLeaf(const nsIParserNode *aNode){ nsresult result=NS_OK; if(mSink){ eHTMLTags theTag=(eHTMLTags)aNode->GetNodeType(); OpenTransientStyles(theTag); STOP_TIMER(); MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::AddLeaf(), this=%p\n", this)); result=mSink->AddLeaf(*aNode); MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::AddLeaf(), this=%p\n", this)); START_TIMER(); } return result; } /** * Call this method ONLY when you want to write a leaf * into the head container. * * @update gess 03/14/99 * @param aNode -- next node to be added to model * @return error code; 0 means OK */ nsresult CNavDTD::AddHeadContent(nsIParserNode *aNode){ nsresult result=NS_OK; static eHTMLTags gNoXTags[] = {eHTMLTag_noembed,eHTMLTag_noframes}; eHTMLTags theTag = (eHTMLTags)aNode->GetNodeType(); // XXX - SCRIPT inside NOTAGS should not get executed unless the pref. // says so. Since we don't have this support yet..lets ignore the // SCRIPT inside NOTAGS. Ref Bug 25880. if (eHTMLTag_meta == theTag || eHTMLTag_script == theTag) { if (HasOpenContainer(gNoXTags,sizeof(gNoXTags)/sizeof(eHTMLTag_unknown))) { return result; } } if (mSink) { STOP_TIMER(); MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::AddHeadContent(), this=%p\n", this)); // Make sure the head is opened. This might just be a no-op. result = OpenHead(nsnull); // Note: userdefined tags in the head are treated as leaves. if (!nsHTMLElement::IsContainer(theTag) || theTag == eHTMLTag_userdefined) { result = mSink->AddLeaf(*aNode); if (mFlags & NS_DTD_FLAG_HAS_MAIN_CONTAINER) { // Close the head now so that body content doesn't get sucked into it. CloseHead(); } } else { if ((mFlags & NS_DTD_FLAG_HAS_MAIN_CONTAINER) && mHeadContainerPosition == -1) { // Keep track of this so that we know when to close the head, when // this tag is done with. mHeadContainerPosition = mBodyContext->GetCount(); } mBodyContext->Push(NS_STATIC_CAST(nsCParserNode*, aNode), nsnull, PR_FALSE); // Note: The head context is already opened. result = mSink->OpenContainer(*aNode); } MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::AddHeadContent(), this=%p\n", this)); START_TIMER(); } return result; } /** * This method gets called to create a valid context stack * for the given child. We compare the current stack to the * default needs of the child, and push new guys onto the * stack until the child can be properly placed. * * @update gess 4/8/98 * @param aChild is the child for whom we need to * create a new context vector * @return true if we succeeded, otherwise false */ nsresult CNavDTD::CreateContextStackFor(eHTMLTags aChild){ mScratch.Truncate(); nsresult result=(nsresult)kContextMismatch; eHTMLTags theTop=mBodyContext->Last(); PRBool bResult=ForwardPropagate(mScratch,theTop,aChild); if(PR_FALSE==bResult){ if(eHTMLTag_unknown!=theTop) { if(theTop!=aChild) //dont even bother if we're already inside a similar element... bResult=BackwardPropagate(mScratch,theTop,aChild); } //if else bResult=BackwardPropagate(mScratch,eHTMLTag_html,aChild); } //elseif PRInt32 theLen=mScratch.Length(); eHTMLTags theTag=(eHTMLTags)mScratch[--theLen]; if((0==mBodyContext->GetCount()) || (mBodyContext->Last()==theTag)) result=NS_OK; //now, build up the stack according to the tags //you have that aren't in the stack... if(PR_TRUE==bResult){ while(theLen) { theTag=(eHTMLTags)mScratch[--theLen]; #ifdef ALLOW_TR_AS_CHILD_OF_TABLE if((eHTML3_Quirks==mDocType) && (eHTMLTag_tbody==theTag)) { //the prev. condition prevents us from emitting tbody in html3.2 docs; fix bug 30378 continue; } #endif CStartToken *theToken=(CStartToken*)mTokenAllocator->CreateTokenOfType(eToken_start,theTag); HandleToken(theToken,mParser); //these should all wind up on contextstack, so don't recycle. } result=NS_OK; } return result; } /** * * @update gess5/18/98 * @param * @return */ nsresult CNavDTD::WillResumeParse(nsIContentSink* aSink){ STOP_TIMER(); MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::WillResumeParse(), this=%p\n", this)); nsresult result=(aSink) ? aSink->WillResume() : NS_OK; MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::WillResumeParse(), this=%p\n", this)); START_TIMER(); return result; } /** * This method gets called when the parsing process is interrupted * due to lack of data (waiting for netlib). * @update gess5/18/98 * @return error code */ nsresult CNavDTD::WillInterruptParse(nsIContentSink* aSink){ STOP_TIMER(); MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::WillInterruptParse(), this=%p\n", this)); nsresult result=(aSink) ? aSink->WillInterrupt() : NS_OK; MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::WillInterruptParse(), this=%p\n", this)); START_TIMER(); return result; }