/* -*- 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.org 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): */ //#define ENABLE_CRC //#define RICKG_DEBUG //#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 "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 "nsTagHandler.h" #include "nsHTMLTokenizer.h" #include "nsTime.h" #include "nsIElementObserver.h" #include "nsViewSourceHTML.h" #include "nsParserNode.h" #include "nsHTMLEntities.h" #include "nsLinebreakConverter.h" #include "nsIFormProcessor.h" #include "nsVoidArray.h" #include "nsReadableUtils.h" #include "prmem.h" #ifdef NS_DEBUG #include "nsLoggingSink.h" #endif static NS_DEFINE_IID(kIHTMLContentSinkIID, NS_IHTML_CONTENT_SINK_IID); 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); static const char* kNullToken = "Error: Null token given"; static const char* kInvalidTagStackPos = "Error: invalid tag stack position"; #ifdef ENABLE_CRC static char gShowCRC; #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_PARSER_FLAG_NONE 0x00000000 #define NS_PARSER_FLAG_HAS_OPEN_BODY 0x00000001 #define NS_PARSER_FLAG_HAS_OPEN_FORM 0x00000002 #define NS_PARSER_FLAG_HAS_OPEN_SCRIPT 0x00000004 #define NS_PARSER_FLAG_HAD_BODY 0x00000008 #define NS_PARSER_FLAG_HAD_FRAMESET 0x00000010 #define NS_PARSER_FLAG_ENABLE_RESIDUAL_STYLE 0x00000020 #define NS_PARSER_FLAG_REQUESTED_HEAD 0x00000040 #define NS_PARSER_FLAG_IS_FORM_CONTAINER 0x00000080 #define NS_PARSER_FLAG_SCRIPT_ENABLED 0x00000100 #define NS_PARSER_FLAG_FRAMES_ENABLED 0x00000200 #define NS_PARSER_FLAG_ALTERNATE_CONTENT 0x00000400 // NOFRAMES, NOSCRIPT /** * 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), mSkippedContent(0), mSink(0), mTokenAllocator(0), mTempContext(0), mOpenHeadCount(0), mOpenMapCount(0), mParser(0), mTokenizer(0), mDTDMode(eDTDMode_quirks), mDocType(eHTML3_Quirks), // why not eHTML_Quirks? mParserCommand(eViewNormal), mSkipTarget(eHTMLTag_unknown), mDTDState(NS_OK), mFlags(NS_PARSER_FLAG_NONE), #ifdef ENABLE_CRC mComputedCRC32(0), mExpectedCRC32(0), #endif mLineNumber(1) { NS_INIT_REFCNT(); mBodyContext=new nsDTDContext(); #ifdef RICKG_DEBUG //DebugDumpContainmentRules2(*this,"c:/temp/DTDRules.new","New CNavDTD Containment Rules"); nsHTMLElement::DebugDumpContainment("c:/temp/contain.new","ElementTable Rules"); nsHTMLElement::DebugDumpMembership("c:/temp/membership.out"); nsHTMLElement::DebugDumpContainType("c:/temp/ctnrules.out"); #endif } /** * * @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; } NS_IF_RELEASE(mTokenizer); 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); if(aInstancePtrResult) { CNavDTD *theOtherDTD=(CNavDTD*)*aInstancePtrResult; if(theOtherDTD) { theOtherDTD->mDTDMode=mDTDMode; theOtherDTD->mParserCommand=mParserCommand; theOtherDTD->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. * @update gess 02/24/00 * @param * @return TRUE if this DTD can satisfy the request; FALSE otherwise. */ eAutoDetectResult CNavDTD::CanParse(CParserContext& aParserContext,nsString& aBuffer, PRInt32 aVersion) { eAutoDetectResult result=eUnknownDetect; if(eViewSource==aParserContext.mParserCommand) { if(PR_TRUE==aParserContext.mMimeType.EqualsWithConversion(kPlainTextContentType)) { result=ePrimaryDetect; } } else { if(PR_TRUE==aParserContext.mMimeType.EqualsWithConversion(kHTMLTextContentType)) { result=ePrimaryDetect; } else if(PR_TRUE==aParserContext.mMimeType.EqualsWithConversion(kPlainTextContentType)) { result=ePrimaryDetect; } else if(PR_TRUE==aParserContext.mMimeType.EqualsWithConversion(kTextCSSContentType)) { result=ePrimaryDetect; } else if(PR_TRUE==aParserContext.mMimeType.EqualsWithConversion(kApplicationJSContentType)) { result=ePrimaryDetect; } else if(PR_TRUE==aParserContext.mMimeType.EqualsWithConversion(kTextJSContentType)) { result=ePrimaryDetect; } else { //otherwise, look into the buffer to see if you recognize anything... PRBool theBufHasXML=PR_FALSE; if(BufferContainsHTML(aBuffer,theBufHasXML)){ result = eValidDetect ; if(0==aParserContext.mMimeType.Length()) { aParserContext.SetMimeType(NS_ConvertASCIItoUCS2(kHTMLTextContentType, sizeof(kHTMLTextContentType))); if(!theBufHasXML) { switch(aParserContext.mDTDMode) { case eDTDMode_strict: result=eValidDetect; break; default: result=ePrimaryDetect; break; } } else result=eValidDetect; } } } } return result; } /** * 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,nsIContentSink* aSink) { nsresult result=NS_OK; mFilename=aParserContext.mScanner->GetFilename(); mFlags = NS_PARSER_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; mBodyContext->SetNodeAllocator(&mNodeAllocator); if((!aParserContext.mPrevContext) && (aSink)) { STOP_TIMER(); MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::WillBuildModel(), this=%p\n", this)); mBodyContext->ResetCounters(); mDocType=aParserContext.mDocType; if(aSink && (!mSink)) { result=aSink->QueryInterface(kIHTMLContentSinkIID, (void **)&mSink); } if(result==NS_OK) { //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 result = aSink->WillBuildModel(); MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::WillBuildModel(), this=%p\n", this)); START_TIMER(); if(mSink) { PRBool enabled; mSink->GetPref(eHTMLTag_frameset,enabled); if(enabled) { mFlags |= NS_PARSER_FLAG_FRAMES_ENABLED; } mSink->GetPref(eHTMLTag_script,enabled); if(enabled) { mFlags |= NS_PARSER_FLAG_SCRIPT_ENABLED; } } mSkipTarget=eHTMLTag_unknown; #ifdef ENABLE_CRC mComputedCRC32=0; mExpectedCRC32=0; #endif } } 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) { nsITokenizer* oldTokenizer=mTokenizer; mTokenizer=aTokenizer; mParser=(nsParser*)aParser; if(mTokenizer) { mTokenAllocator=mTokenizer->GetTokenAllocator(); if(NS_FAILED(result)) return result; if(mSink) { if(!mBodyContext->GetCount()) { 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_PARSER_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=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)){ //Currently nsIHTMLContentSink does nothing with a call to WillProcessAToken. //mSink->WillProcessAToken(); #if 0 int n=aTokenizer->GetCount(); if(n>50) n=50; for(int i=0;iGetTokenAt(i); printf("\nToken[%i],%p",i,theToken); } printf("\n"); #endif if(mDTDState!=NS_ERROR_HTMLPARSER_STOPPARSING) { CToken* theToken=mTokenizer->PopToken(); if(theToken) { result=HandleToken(theToken,aParser); } else break; } else { 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; } /** * * @update gess5/18/98 * @param * @return */ nsresult CNavDTD::DidBuildModel(nsresult anErrorCode,PRBool aNotifySink,nsIParser* aParser,nsIContentSink* aSink){ nsresult result=NS_OK; if(aSink) { if((NS_OK==anErrorCode) && !(mFlags & (NS_PARSER_FLAG_HAD_BODY | NS_PARSER_FLAG_HAD_FRAMESET))) { mSkipTarget=eHTMLTag_unknown; //clear this in case we were searching earlier. CStartToken *theToken=NS_STATIC_CAST(CStartToken*,mTokenAllocator->CreateTokenOfType(eToken_start,eHTMLTag_body,NS_LITERAL_STRING("body"))); mTokenizer->PushTokenFront(theToken); //this token should get pushed on the context stack, don't recycle it result=BuildModel(aParser,mTokenizer,0,aSink); } if(aParser && (NS_OK==result)){ if(aNotifySink){ if((NS_OK==anErrorCode) && (mBodyContext->GetCount()>0)) { if(mSkipTarget) { CHTMLToken* theEndToken=nsnull; theEndToken=NS_STATIC_CAST(CHTMLToken*,mTokenAllocator->CreateTokenOfType(eToken_end,mSkipTarget)); if(theEndToken) { result=HandleToken(theEndToken,mParser); } } if(mDTDState==NS_ERROR_HTMLPARSER_MISPLACEDTABLECONTENT) { // Create an end table token to flush tokens off the misplaced list... CHTMLToken* theTableToken=NS_STATIC_CAST(CHTMLToken*,mTokenAllocator->CreateTokenOfType(eToken_end,eHTMLTag_table)); if(theTableToken) { result=HandleToken(theTableToken,mParser); } } if(result==NS_OK) { eHTMLTags theTarget; //now let's disable style handling to save time when closing remaining stack members... mFlags &= ~NS_PARSER_FLAG_ENABLE_RESIDUAL_STYLE; while(mBodyContext->GetCount() > 0) { theTarget = mBodyContext->Last(); CloseContainersTo(theTarget,PR_FALSE); } } } 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); } } STOP_TIMER(); MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::DidBuildModel(), this=%p\n", this)); #ifdef ENABLE_CRC //let's only grab this state once! if(!gShowCRC) { gShowCRC=1; //this only indicates we'll not initialize again. char* theEnvString = PR_GetEnv("RICKG_CRC"); if(theEnvString){ if(('1'==theEnvString[0]) || ('Y'==theEnvString[0]) || ('y'==theEnvString[0])){ gShowCRC=2; //this indicates that the CRC flag was found in the environment. } } } if(2==gShowCRC) { if(mComputedCRC32!=mExpectedCRC32) { if(mExpectedCRC32!=0) { printf("CRC Computed: %u Expected CRC: %u\n,",mComputedCRC32,mExpectedCRC32); result = aSink->DidBuildModel(2); } else { printf("Computed CRC: %u.\n",mComputedCRC32); result = aSink->DidBuildModel(3); } } else result = aSink->DidBuildModel(0); } else result=aSink->DidBuildModel(0); #endif MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::DidBuildModel(), this=%p\n", this)); START_TIMER(); //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. result=aSink->DidBuildModel(0); } //if asink return result; } /** * --- 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 nsAReadableString& name=attr->GetKey(); const nsAReadableString& value=attr->GetValue(); if((name.Equals(NS_LITERAL_STRING("type")) || name.Equals(NS_LITERAL_STRING("TYPE"))) && !(value.Equals(NS_LITERAL_STRING("hidden")) || value.Equals(NS_LITERAL_STRING("HIDDEN")))) { result=PR_TRUE; break; } }//for } else { result=PR_TRUE; } } } return result; } /** * 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(); PRBool execSkipContent=PR_FALSE; /* --------------------------------------------------------------------------------- To understand this little piece of code, you need to look below too. In essence, this code caches "skipped content" until we find a given skiptarget. Once we find the skiptarget, we take all skipped content up to that point and coallate it. Then we push those tokens back onto the tokenizer deque. --------------------------------------------------------------------------------- */ // printf("token: %p\n",aToken); if(mSkipTarget){ //handle a preexisting target... if((theTag==mSkipTarget) && (eToken_end==theType)){ mSkipTarget=eHTMLTag_unknown; //stop skipping. //mTokenizer->PushTokenFront(aToken); //push the end token... execSkipContent=PR_TRUE; IF_FREE(aToken, mTokenAllocator); theToken=(CHTMLToken*)mSkippedContent.PopFront(); theType=eToken_start; } else { mSkippedContent.Push(theToken); return result; } } else if(mFlags & NS_PARSER_FLAG_ALTERNATE_CONTENT) { if(theTag != mBodyContext->Last() || theType!=eToken_end) { // attribute source is a part of start token. if(theType!=eToken_attribute) { aToken->AppendSource(mScratch); } IF_FREE(aToken, mTokenAllocator); return result; } else { // If you're here then we have seen a /noscript. // After handling the text token intentionally // fall thro' such that /noscript gets handled. CTextToken theTextToken(mScratch); result=HandleStartToken(&theTextToken); if(NS_FAILED(result)) { return result; } mScratch.Truncate(); mScratch.SetCapacity(0); } } else if(mDTDState==NS_ERROR_HTMLPARSER_MISPLACEDTABLECONTENT) { // 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) { eHTMLTags theParentTag=mBodyContext->Last(); theTag=(eHTMLTags)theToken->GetTypeID(); if((FindTagInSet(theTag,gLegalElements,sizeof(gLegalElements)/sizeof(theTag))) || (gHTMLElements[theParentTag].CanContain(theTag)) && (theTag!=eHTMLTag_comment)) { // Added comment -> bug 40855 mDTDState=NS_OK; // reset the state since all the misplaced tokens are about to get handled. result=HandleSavedTokens(mBodyContext->mContextTopIndex); mBodyContext->mContextTopIndex=-1; if(NS_FAILED(result)) { return result; } //falling through intentionally,i.e., handle the token that is willing to be the child of the current parent. } else { mMisplacedContent.Push(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. --------------------------------------------------------------------------------- */ if(!execSkipContent) { switch(theTag) { case eHTMLTag_html: case eHTMLTag_noframes: case eHTMLTag_noscript: case eHTMLTag_script: case eHTMLTag_doctypeDecl: case eHTMLTag_instruction: break; case eHTMLTag_comment: case eHTMLTag_newline: case eHTMLTag_whitespace: if(mMisplacedContent.GetSize()<1) { // simply pass these through to token handler without further ado... // fix for bugs 17017,18308,23765,24275,69331 break; } default: if(!gHTMLElements[eHTMLTag_html].SectionContains(theTag,PR_FALSE)) { if(!(mFlags & (NS_PARSER_FLAG_HAD_BODY | NS_PARSER_FLAG_HAD_FRAMESET))) { //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 theExclusive=PR_FALSE; PRBool theChildBelongsInHead=gHTMLElements[eHTMLTag_head].IsChildOfHead(theTag,theExclusive); if(!theChildBelongsInHead) { //If you're here then we found a child of the body that was out of place. //We're going to move it to the body by storing it temporarily on the misplaced stack. //However, in quirks mode, a few tags request, ambiguosly, for a BODY. - Bugs 18928, 24204.- mMisplacedContent.Push(aToken); if(DoesRequireBody(aToken,mTokenizer)) { CToken* theBodyToken=NS_STATIC_CAST(CToken*,mTokenAllocator->CreateTokenOfType(eToken_start,eHTMLTag_body,NS_LITERAL_STRING("body"))); result=HandleToken(theBodyToken,aParser); } return result; } } //if } //if }//switch } //if if(theToken){ //Before dealing with the token normally, we need to deal with skip targets CStartToken* theStartToken=NS_STATIC_CAST(CStartToken*,aToken); if((!execSkipContent) && (theType!=eToken_end) && (eHTMLTag_unknown==mSkipTarget) && (gHTMLElements[theTag].mSkipTarget) && (!theStartToken->IsEmpty())) { // added empty token check for bug 44186 //create a new target mSkipTarget=gHTMLElements[theTag].mSkipTarget; mSkippedContent.Push(theToken); } else { mParser=(nsParser*)aParser; switch(theType) { case eToken_text: case eToken_start: case eToken_whitespace: case eToken_newline: result=HandleStartToken(theToken); break; case eToken_end: result=HandleEndToken(theToken); break; case eToken_cdatasection: case eToken_comment: case eToken_markupDecl: result=HandleCommentToken(theToken); break; case eToken_entity: result=HandleEntityToken(theToken); break; case eToken_attribute: result=HandleAttributeToken(theToken); break; case eToken_instruction: result=HandleProcessingInstructionToken(theToken); break; case eToken_doctypeDecl: result=HandleDocTypeDeclToken(theToken); break; default: break; }//switch if(NS_SUCCEEDED(result) || (NS_ERROR_HTMLPARSER_BLOCK==result)) { IF_FREE(theToken, mTokenAllocator); } else if(result==NS_ERROR_HTMLPARSER_STOPPARSING) { mDTDState=result; } else { return NS_OK; } } } }//if return result; } /** * This gets called after we've handled a given start tag. * It's a generic hook to let us to post processing. * @param aToken contains the tag in question * @param aChildTag is the tag itself. * @return status */ nsresult CNavDTD::DidHandleStartTag(nsIParserNode& aNode,eHTMLTags aChildTag){ nsresult result=NS_OK; #if 0 // XXX --- Ignore this: it's just rickg debug testing... nsAutoString theStr; aNode.GetSource(theStr); #endif switch(aChildTag){ case eHTMLTag_pre: case eHTMLTag_listing: { CToken* theNextToken=mTokenizer->PeekToken(); if(theNextToken) { eHTMLTokenTypes theType=eHTMLTokenTypes(theNextToken->GetTokenType()); if(eToken_newline==theType){ mLineNumber++; theNextToken=mTokenizer->PopToken(); //skip 1st newline inside PRE and LISTING IF_FREE(theNextToken, mTokenAllocator); // fix for Bug 29379 }//if }//if } break; case eHTMLTag_plaintext: case eHTMLTag_xmp: //grab the skipped content and dump it out as text... { STOP_TIMER() MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::DidHandleStartTag(), this=%p\n", this)); const nsString& theString=aNode.GetSkippedContent(); if(0CreateTokenOfType(eToken_text,eHTMLTag_text,theString)); nsCParserNode theNode(theToken,0,mTokenAllocator); result=mSink->AddLeaf(theNode); //when the node get's destructed, so does the new token } MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::DidHandleStartTag(), this=%p\n", this)); START_TIMER() } break; case eHTMLTag_counter: { PRInt32 theCount=mBodyContext->GetCount(); eHTMLTags theGrandParentTag=mBodyContext->TagAt(theCount-1); nsAutoString theNumber; mBodyContext->IncrementCounter(theGrandParentTag,aNode,theNumber); CTextToken theToken(theNumber); PRInt32 theLineNumber=0; nsCParserNode theNode(&theToken,theLineNumber,0 /*stack token*/); result=mSink->AddLeaf(theNode); } break; case eHTMLTag_meta: { //we should only enable user-defined entities in debug builds... PRInt32 theCount=aNode.GetAttributeCount(); const nsString* theNamePtr=0; const nsString* theValuePtr=0; if(theCount) { PRInt32 theIndex=0; for(theIndex=0;theIndexRegisterEntity(*theNamePtr,*theValuePtr); } } break; default: break; }//switch //handle tags by generating a close tag... //added this to fix bug 48351, which contains XHTML and uses empty tags. nsCParserNode* theNode=NS_STATIC_CAST(nsCParserNode*,&aNode); if(nsHTMLElement::IsContainer(aChildTag) && theNode && theNode->mToken) { //nullptr test fixes bug 56085 CStartToken *theToken=NS_STATIC_CAST(CStartToken*,theNode->mToken); if(theToken->IsEmpty()){ CToken *theEndToken=mTokenAllocator->CreateTokenOfType(eToken_end,aChildTag); if(theEndToken) { result=HandleEndToken(theEndToken); IF_FREE(theEndToken, mTokenAllocator); } } } return result; } /** * Determine whether the given tag is open anywhere * in our context stack. * * @update gess 4/2/98 * @param eHTMLTags tag to be searched for in stack * @return topmost index of tag on stack */ PRInt32 CNavDTD::LastOf(eHTMLTags aTagSet[],PRInt32 aCount) const { int theIndex=0; for(theIndex=mBodyContext->GetCount()-1;theIndex>=0;theIndex--){ if(FindTagInSet((*mBodyContext)[theIndex],aTagSet,aCount)) { return theIndex; } } return kNotFound; } /** * Call this to find the index of a given child, or (if not found) * the index of its nearest synonym. * * @update gess 3/25/98 * @param aTagStack -- list of open tags * @param aTag -- tag to test for containership * @return index of kNotFound */ static PRInt32 GetIndexOfChildOrSynonym(nsDTDContext& aContext,eHTMLTags aChildTag) { #if 1 PRInt32 theChildIndex=nsHTMLElement::GetIndexOfChildOrSynonym(aContext,aChildTag); #else PRInt32 theChildIndex=aContext.LastOf(aChildTag); if(kNotFound==theChildIndex) { TagList* theSynTags=gHTMLElements[aChildTag].GetSynonymousTags(); //get the list of tags that THIS tag can close if(theSynTags) { theChildIndex=LastOf(aContext,*theSynTags); } else{ PRInt32 theGroup=nsHTMLElement::GetSynonymousGroups(aChildTag); if(theGroup) { theChildIndex=aContext.GetCount(); while(-1<--theChildIndex) { eHTMLTags theTag=aContext[theChildIndex]; if(gHTMLElements[theTag].IsMemberOf(theGroup)) { break; } } } } } #endif return theChildIndex; } /** * This method is called to determine whether or not the child * tag is happy being OPENED in the context of the current * tag stack. This is only called if the current parent thinks * it wants to contain the given childtag. * * @param aChildTag -- tag enum of child to be opened * @param aTagStack -- ref to current tag stack in DTD. * @return PR_TRUE if child agrees to be opened here. */ static PRBool CanBeContained(eHTMLTags aChildTag,nsDTDContext& aContext) { /* # Interesting test cases: Result: * 1.