/* -*- 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): */ #include "nsDebug.h" #include "nsIDTDDebug.h" #include "CNavDTD.h" #include "nsHTMLTokens.h" #include "nsCRT.h" #include "nsParser.h" #include "nsIParser.h" #include "nsIHTMLContentSink.h" #include "nsScanner.h" #include "nsIDTDDebug.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" #ifdef XP_PC #include //this is here for debug reasons... #endif #include "prmem.h" //#define ENABLE_RESIDUALSTYLE //#define RICKG_DEBUG //#define ENABLE_CRC #ifdef RICKG_DEBUG #include #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 const char* kNullToken = "Error: Null token given"; static const char* kInvalidTagStackPos = "Error: invalid tag stack position"; static char* kVerificationDir = "c:/temp"; #ifdef ENABLE_CRC static char gShowCRC; #endif static eHTMLTags gFormElementTags[]= { eHTMLTag_button, eHTMLTag_fieldset, eHTMLTag_input, eHTMLTag_isindex, eHTMLTag_label, eHTMLTag_legend, eHTMLTag_option, eHTMLTag_optgroup, eHTMLTag_select, eHTMLTag_textarea}; #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 /*************************************************************** This the ITagHandler deque deallocator, needed by the CTagHandlerRegister ***************************************************************/ class CTagHandlerDeallocator: public nsDequeFunctor{ public: virtual void* operator()(void* aObject) { nsITagHandler* tagHandler = (nsITagHandler*)aObject; delete tagHandler; return 0; } }; /*************************************************************** This funtor will be called for each item in the TagHandler que to check for a Tag name, and setting the current TagHandler when it is reached ***************************************************************/ class CTagFinder: public nsDequeFunctor{ public: CTagFinder(){} void Initialize(const nsString &aTagName) {mTagName = aTagName;} virtual ~CTagFinder() { } virtual void* operator()(void* aObject) { nsString* theString = ((nsITagHandler*)aObject)->GetString(); if( theString->Equals(mTagName)){ return aObject; } return(0); } nsAutoString mTagName; }; /*************************************************************** This a an object that will keep track of TagHandlers in the DTD. Uses a factory pattern ***************************************************************/ class CTagHandlerRegister { public: CTagHandlerRegister(); ~CTagHandlerRegister(); void RegisterTagHandler(nsITagHandler *aTagHandler){ mTagHandlerDeque.Push(aTagHandler); } nsITagHandler* FindTagHandler(const nsString &aTagName){ nsITagHandler* foundHandler = nsnull; mTagFinder.Initialize(aTagName); mTagHandlerDeque.Begin(); foundHandler = (nsITagHandler*) mTagHandlerDeque.FirstThat(mTagFinder); return foundHandler; } nsDeque mTagHandlerDeque; CTagFinder mTagFinder; }; MOZ_DECL_CTOR_COUNTER(CTagHandlerRegister); CTagHandlerRegister::CTagHandlerRegister() : mTagHandlerDeque(new CTagHandlerDeallocator()) { MOZ_COUNT_CTOR(CTagHandlerRegister); } CTagHandlerRegister::~CTagHandlerRegister() { MOZ_COUNT_DTOR(CTagHandlerRegister); } /************************************************************************ The CTagHandlerRegister for a CNavDTD. This is where special taghanders for our tags can be managed and called from Note: This can also be attached to some object so it can be refcounted and destroyed if you want this to go away when not imbedded. ************************************************************************/ //CTagHandlerRegister gTagHandlerRegister; /************************************************************************ And now for the main class -- CNavDTD... ************************************************************************/ /** * 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), mSharedNodes(0), mScratch("") { NS_INIT_REFCNT(); mSink = 0; mParser=0; mDTDDebug=0; mLineNumber=1; mHasOpenBody=PR_FALSE; mHasOpenHead=0; mHasOpenForm=PR_FALSE; mHasOpenMap=PR_FALSE; mHeadContext=new nsDTDContext(); mBodyContext=new nsDTDContext(); mFormContext=0; mMapContext=0; mTempContext=0; mTokenizer=0; mComputedCRC32=0; mExpectedCRC32=0; mDTDState=NS_OK; mStyleHandlingEnabled=PR_TRUE; if(!gHTMLElements) { InitializeElementTable(); } // DebugDumpContainmentRules2(*this,"c:/temp/DTDRules.new","New CNavDTD Containment Rules"); #ifdef RICKG_DEBUG nsHTMLElement::DebugDumpContainment("c:/temp/rules.new","ElementTable Rules"); nsHTMLElement::DebugDumpMembership("c:/temp/table.out"); nsHTMLElement::DebugDumpContainType("c:/temp/ctnrules.out"); #endif #ifdef NS_DEBUG gNodeCount=0; #endif } /** * This method creates a new parser node. It tries to get one from * the recycle list before allocating a new one. * @update gess1/8/99 * @param * @return valid node* */ nsCParserNode* CNavDTD::CreateNode(void) { nsCParserNode* result=0; if(0mUseCount)) { if(aNode->mToken) { if(!aNode->mToken->mUseCount) { mTokenRecycler->RecycleToken(aNode->mToken); } } CToken* theToken=0; while((theToken=(CToken*)aNode->PopAttributeToken())){ if(!theToken->mUseCount) { mTokenRecycler->RecycleToken(theToken); } } mSharedNodes.Push(aNode); } } /** * * @update gess1/8/99 * @param * @return */ const nsIID& CNavDTD::GetMostDerivedIID(void)const { return kClassIID; } /** * Default destructor * * @update gess 4/9/98 * @param * @return */ CNavDTD::~CNavDTD(){ delete mHeadContext; delete mBodyContext; if(mTokenizer) delete (nsHTMLTokenizer*)mTokenizer; if(mTempContext) delete mTempContext; nsCParserNode* theNode=0; #ifdef NS_DEBUG #if 1 PRInt32 count=gNodeCount-mSharedNodes.GetSize(); if(count) { printf("%i of %i nodes leaked!\n",count,gNodeCount); } #endif #endif #if 1 while((theNode=(nsCParserNode*)mSharedNodes.Pop())){ delete theNode; } #endif #ifdef NS_DEBUG gNodeCount=0; #endif NS_IF_RELEASE(mSink); NS_IF_RELEASE(mDTDDebug); } /** * Call this method if you want the DTD to construct a fresh * instance of itself. * @update gess7/23/98 * @param * @return */ nsresult CNavDTD::CreateNewInstance(nsIDTD** aInstancePtrResult){ return NS_NewNavHTMLDTD(aInstancePtrResult); } /** * Called by the parser to initiate dtd verification of the * internal context stack. * @update gess 7/23/98 * @param * @return */ PRBool CNavDTD::Verify(nsString& aURLRef,nsIParser* aParser){ PRBool result=PR_TRUE; /* * Disable some DTD debugging code in the parser that * breaks on some compilers because of some broken * streams code in prstrm.cpp. */ #if !defined(MOZ_DISABLE_DTD_DEBUG) if(!mDTDDebug){ nsresult rval = NS_NewDTDDebug(&mDTDDebug); if (NS_OK != rval) { fputs("Cannot create parser debugger.\n", stdout); result=-PR_FALSE; } else mDTDDebug->SetVerificationDirectory(kVerificationDir); } #endif if(mDTDDebug) { // mDTDDebug->Verify(this,aParser,mBodyContext->GetCount(),mBodyContext->mStack,aURLRef); } 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 gess6/24/98 * @param * @return TRUE if this DTD can satisfy the request; FALSE otherwise. */ eAutoDetectResult CNavDTD::CanParse(nsString& aContentType, nsString& aCommand, nsString& aBuffer, PRInt32 aVersion) { eAutoDetectResult result=eUnknownDetect; if(!aCommand.Equals(kViewSourceCommand)) { if(PR_TRUE==aContentType.Equals(kHTMLTextContentType)) { 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==aContentType.Length()) { aContentType=kHTMLTextContentType; result = (theBufHasXML) ? eValidDetect : ePrimaryDetect; } } } } return result; } /** * * @update gess5/18/98 * @param * @return */ nsresult CNavDTD::WillBuildModel(nsString& aFilename, PRBool aNotifySink,nsString& aSourceType,eParseMode aParseMode, nsString& aCommand,nsIContentSink* aSink){ nsresult result=NS_OK; mFilename=aFilename; mHasOpenBody=PR_FALSE; mHadBody=PR_FALSE; mHadFrameset=PR_FALSE; mLineNumber=1; mHasOpenScript=PR_FALSE; mParseMode=aParseMode; if((aNotifySink) && (aSink)) { STOP_TIMER(); MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::WillBuildModel(), this=%p\n", this)); mTokenRecycler=0; mStyleHandlingEnabled=PR_TRUE; if(aSink && (!mSink)) { result=aSink->QueryInterface(kIHTMLContentSinkIID, (void **)&mSink); } if(result==NS_OK) { result = aSink->WillBuildModel(); MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::WillBuildModel(), this=%p\n", this)); START_TIMER(); mSkipTarget=eHTMLTag_unknown; mComputedCRC32=0; mExpectedCRC32=0; } } 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) { nsresult result=NS_OK; if(aTokenizer) { nsITokenizer* oldTokenizer=mTokenizer; mTokenizer=aTokenizer; mParser=(nsParser*)aParser; if(mTokenizer) { mTokenRecycler=(CTokenRecycler*)mTokenizer->GetTokenRecycler(); if(mSink) { if(!mBodyContext->GetCount()) { //if the content model is empty, then begin by opening ... CStartToken *theToken=(CStartToken*)mTokenRecycler->CreateTokenOfType(eToken_start,eHTMLTag_html); HandleStartToken(theToken); //this token should get pushed on the context stack, don't recycle it. } while(NS_SUCCEEDED(result)){ if(mDTDState!=NS_ERROR_HTMLPARSER_STOPPARSING) { CToken* theToken=mTokenizer->PopToken(); if(theToken) { result=HandleToken(theToken,aParser); } else break; } else { result=mDTDState; break; } }//while mTokenizer=oldTokenizer; } } } 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) && (!mHadBody) && (!mHadFrameset)) { CStartToken *theToken=(CStartToken*)mTokenRecycler->CreateTokenOfType(eToken_start,eHTMLTag_body); mTokenizer->PushTokenFront(theToken); //this token should get pushed on the context stack, don't recycle it mTokenizer->PrependTokens(mMisplacedContent); //push misplaced content 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=(CHTMLToken*)mTokenRecycler->CreateTokenOfType(eToken_end,mSkipTarget); if(theEndToken) { result=HandleToken(theEndToken,mParser); } } if(result==NS_OK) { eHTMLTags theTarget; //now let's disable style handling to save time when closing remaining stack members... mStyleHandlingEnabled=PR_FALSE; while(mBodyContext->GetCount() > 0) { theTarget = mBodyContext->Last(); CloseContainersTo(theTarget,PR_FALSE); } } } 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); #else result=aSink->DidBuildModel(0); #endif MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::DidBuildModel(), this=%p\n", this)); START_TIMER(); if(mDTDDebug) { mDTDDebug->DumpVectorRecord(); } } } } 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 5/21/98 * @param aToken * @param aParser * @return */ nsresult CNavDTD::HandleToken(CToken* aToken,nsIParser* aParser){ nsresult result=NS_OK; if(aToken) { CHTMLToken* theToken= (CHTMLToken*)(aToken); eHTMLTokenTypes theType=eHTMLTokenTypes(theToken->GetTokenType()); eHTMLTags theTag=(eHTMLTags)theToken->GetTypeID(); PRBool execSkipContent=PR_FALSE; theToken->mUseCount=0; //assume every token coming into this system needs recycling. /* --------------------------------------------------------------------------------- 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; mTokenRecycler->RecycleToken(aToken); theToken=(CHTMLToken*)mSkippedContent.PopFront(); theType=eToken_start; } else { mSkippedContent.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) { static eHTMLTags passThru[]= { eHTMLTag_html,eHTMLTag_comment,eHTMLTag_newline, eHTMLTag_whitespace,eHTMLTag_script, eHTMLTag_markupDecl,eHTMLTag_userdefined }; if(!FindTagInSet(theTag,passThru,sizeof(passThru)/sizeof(eHTMLTag_unknown))){ if(!gHTMLElements[eHTMLTag_html].SectionContains(theTag,PR_FALSE)) { if((!mHadBody) && (!mHadFrameset)){ if(mHasOpenHead) { //just fall through and handle current token if(!gHTMLElements[eHTMLTag_head].IsChildOfHead(theTag)){ mMisplacedContent.Push(aToken); aToken->mUseCount++; return result; } } else { if(gHTMLElements[eHTMLTag_body].SectionContains(theTag,PR_TRUE)){ mTokenizer->PushTokenFront(aToken); //put this token back... mTokenizer->PrependTokens(mMisplacedContent); //push misplaced content theToken=(CHTMLToken*)mTokenRecycler->CreateTokenOfType(eToken_start,theTag=eHTMLTag_body); theType=eToken_start; //now open a body... } } } } //if } //if } //if if(theToken){ //Before dealing with the token normally, we need to deal with skip targets if((!execSkipContent) && (theType!=eToken_end) && (eHTMLTag_unknown==mSkipTarget) && (gHTMLElements[theTag].mSkipTarget)){ //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_comment: result=HandleCommentToken(theToken); break; case eToken_entity: result=HandleEntityToken(theToken); break; case eToken_attribute: result=HandleAttributeToken(theToken); break; case eToken_style: result=HandleStyleToken(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(0>=theToken->mUseCount) mTokenRecycler->RecycleToken(theToken); } else if(result==NS_ERROR_HTMLPARSER_STOPPARSING) mDTDState=result; else return NS_OK; /*************************************************************/ // CAUTION: Here we are forgetting to push the ATTRIBUTE Tokens. // So, before you uncomment this part please make sure // that the attribute tokens are also accounted for. //else if(NS_ERROR_HTMLPARSER_MISPLACED!=result) // mTokenizer->PushTokenFront(theToken); //else result=NS_OK; /***************************************************************/ #if 0 if (mDTDDebug) { mDTDDebug->Verify(this, mParser, mBodyContext->GetCount(), mBodyContext->mStack, mFilename); } #endif } } }//if return result; } /** * This method causes all tokens to be dispatched to the given tag handler. * * @update gess 3/25/98 * @param aHandler -- object to receive subsequent tokens... * @return error code (usually 0) */ nsresult CNavDTD::CaptureTokenPump(nsITagHandler* aHandler) { nsresult result=NS_OK; return result; } /** * This method releases the token-pump capture obtained in CaptureTokenPump() * * @update gess 3/25/98 * @param aHandler -- object that received tokens... * @return error code (usually 0) */ nsresult CNavDTD::ReleaseTokenPump(nsITagHandler* aHandler){ nsresult result=NS_OK; 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(nsCParserNode& aNode,eHTMLTags aChildTag){ nsresult result=NS_OK; switch(aChildTag){ case eHTMLTag_pre: case eHTMLTag_listing: { CToken* theNextToken=mTokenizer->PeekToken(); if(theNextToken) { eHTMLTokenTypes theType=eHTMLTokenTypes(theNextToken->GetTokenType()); if(eToken_newline==theType){ mLineNumber++; mTokenizer->PopToken(); //skip 1st newline inside PRE and LISTING }//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& theText=aNode.GetSkippedContent(); if(0

  • ....
  • inner
  • closes outer
  • * 2.
    allow nested
    * 3. is badly misplaced. To be backwards // compatible we should not attempt to close the tags above it, for // the contents inside the table might get thrown out of the table. // The safest thing to do is to discard this tag. return result; } CloseContainersTo(theIndex,theParentTag,PR_TRUE); }//if else break; }//if else { CreateContextStackFor(aChildTag); theIndex=mBodyContext->GetCount(); } }//if break; case eIgnore: default: break; }//switch } while(!(theParentContains && theChildAgrees)); if(theChildIsContainer){ result=OpenContainer(aNode,aChildTag,PR_TRUE); } else { //we're writing a leaf... result=AddLeaf(aNode); } return result; } #ifdef RICKG_DEBUG void WriteTokenToLog(CToken* aToken) { static nsFileSpec fileSpec("c:\\temp\\tokenlog.html"); static nsOutputFileStream outputStream(fileSpec); aToken->DebugDumpSource(outputStream); //write token without close bracket... } #endif /** * This gets called before we've handled a given start tag. * It's a generic hook to let us do pre processing. * @param aToken contains the tag in question * @param aChildTag is the tag itself. * @param aNode is the node (tag) with associated attributes. * @return TRUE if tag processing should continue; FALSE if the tag has been handled. */ nsresult CNavDTD::WillHandleStartTag(CToken* aToken,eHTMLTags aTag,nsCParserNode& aNode){ nsresult result=NS_OK; PRInt32 theAttrCount = aNode.GetAttributeCount(); //first let's see if there's some skipped content to deal with... if(gHTMLElements[aTag].mSkipTarget) { result=CollectSkippedContent(aNode,theAttrCount); } STOP_TIMER() MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::WillHandleStartTag(), this=%p\n", this)); if(mParser) { CObserverService& theService=mParser->GetObserverService(); CParserContext* pc=mParser->PeekContext(); void* theDocID=(pc)? pc->mKey:0; result=theService.Notify(aTag,aNode,(PRUint32)theDocID,kHTMLTextContentType,mParser); } MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::WillHandleStartTag(), this=%p\n", this)); START_TIMER() if(NS_SUCCEEDED(result)) { #ifdef ENABLE_CRC STOP_TIMER() if(eHTMLTag_meta==aTag) { PRInt32 theCount=aNode.GetAttributeCount(); if(1 0) { CToken* theAttrToken=nsnull; nsCParserNode* theAttrNode = (nsCParserNode*)&aNode; if(theAttrNode) { while(aCount){ theAttrToken=theAttrNode->PopAttributeToken(); if(theAttrToken) { aDeque.Push(theAttrToken); theAttrToken->mUseCount=0; } aCount--; }//while }//if }//if } /** * This method gets called when a start token has been encountered that the parent * wants to omit. * * @update gess 3/25/98 * @param aToken -- next (start) token to be handled * @param aChildTag -- id of the child in question * @param aParent -- id of the parent in question * @param aNode -- CParserNode representing this start token * @return PR_TRUE if all went well; PR_FALSE if error occured */ nsresult CNavDTD::HandleOmittedTag(CToken* aToken,eHTMLTags aChildTag,eHTMLTags aParent,nsIParserNode* aNode) { NS_PRECONDITION(mBodyContext != nsnull,"need a context to work with"); nsresult result=NS_OK; //The trick here is to see if the parent can contain the child, but prefers not to. //Only if the parent CANNOT contain the child should we look to see if it's potentially a child //of another section. If it is, the cache it for later. // 1. Get the root node for the child. See if the ultimate node is the BODY, FRAMESET, HEAD or HTML PRInt32 theTagCount = mBodyContext->GetCount(); CToken* theToken = aToken; if(aToken) { PRInt32 attrCount = aToken->GetAttributeCount(); if((gHTMLElements[aParent].HasSpecialProperty(kBadContentWatch)) && (!nsHTMLElement::IsWhitespaceTag(aChildTag))) { eHTMLTags theTag=eHTMLTag_unknown; PRInt32 theIndex=kNotFound; while(theTagCount > 0) { theTag = mBodyContext->TagAt(--theTagCount); if(!gHTMLElements[theTag].HasSpecialProperty(kBadContentWatch)) { if(!gHTMLElements[theTag].CanContain(aChildTag)) break; theIndex = theTagCount; break; } } PRBool done=PR_FALSE; if(theIndex>kNotFound) { while(!done){ mMisplacedContent.Push(theToken); theToken->mUseCount++; // If the token is attributed then save those attributes too. if(attrCount > 0) PushMisplacedAttributes(*aNode,mMisplacedContent,attrCount); theToken=mTokenizer->PeekToken(); if(theToken) { theToken->mUseCount=0; theTag=(eHTMLTags)theToken->GetTypeID(); if(!nsHTMLElement::IsWhitespaceTag(theTag) && theTag!=eHTMLTag_unknown) { if((gHTMLElements[theTag].HasSpecialProperty(kBadContentWatch)) || (gHTMLElements[aParent].CanContain(theTag))) { done=PR_TRUE; } } if(!done) theToken=mTokenizer->PopToken(); } else done=PR_TRUE; }//while if(result==NS_OK) { result=HandleSavedTokens(theIndex); } }//if }//if if((aChildTag!=aParent) && (gHTMLElements[aParent].HasSpecialProperty(kSaveMisplaced))) { mMisplacedContent.Push(aToken); aToken->mUseCount++; // If the token is attributed then save those attributes too. if(attrCount > 0) PushMisplacedAttributes(*aNode,mMisplacedContent,attrCount); } } return result; } /** * This method gets called when a start token has been * encountered in the parse process. If the current container * can contain this tag, then add it. Otherwise, you have * two choices: 1) create an implicit container for this tag * to be stored in * 2) close the top container, and add this to * whatever container ends up on top. * * @update gess 1/04/99 * @param aToken -- next (start) token to be handled * @param aNode -- CParserNode representing this start token * @return PR_TRUE if all went well; PR_FALSE if error occured */ nsresult CNavDTD::HandleStartToken(CToken* aToken) { NS_PRECONDITION(0!=aToken,kNullToken); #ifdef RICKG_DEBUG WriteTokenToLog(aToken); #endif //Begin by gathering up attributes... nsCParserNode* theNode=CreateNode(); theNode->Init(aToken,mLineNumber,mTokenRecycler); eHTMLTags theChildTag=(eHTMLTags)aToken->GetTypeID(); PRInt16 attrCount=aToken->GetAttributeCount(); eHTMLTags theParent=mBodyContext->Last(); nsresult result=(0==attrCount) ? NS_OK : CollectAttributes(*theNode,theChildTag,attrCount); if(NS_OK==result) { result=WillHandleStartTag(aToken,theChildTag,*theNode); if(NS_OK==result) { PRBool isTokenHandled =PR_FALSE; PRBool theHeadIsParent=PR_FALSE; if(nsHTMLElement::IsSectionTag(theChildTag)){ switch(theChildTag){ case eHTMLTag_body: if(mHasOpenBody) { result=OpenContainer(theNode,theChildTag,PR_FALSE); isTokenHandled=PR_TRUE; } break; case eHTMLTag_head: if(mHadBody || mHadFrameset) { result=HandleOmittedTag(aToken,theChildTag,theParent,theNode); isTokenHandled=PR_TRUE; } break; default: break; } } mLineNumber += aToken->mNewlineCount; theHeadIsParent=nsHTMLElement::IsChildOfHead(theChildTag); switch(theChildTag) { case eHTMLTag_newline: mLineNumber++; break; case eHTMLTag_area: if(!mHasOpenMap) isTokenHandled=PR_TRUE; STOP_TIMER(); MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::HandleStartToken(), this=%p\n", this)); if (mHasOpenMap && mSink) result=mSink->AddLeaf(*theNode); MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::HandleStartToken(), this=%p\n", this)); START_TIMER(); break; case eHTMLTag_image: aToken->SetTypeID(theChildTag=eHTMLTag_img); break; case eHTMLTag_userdefined: isTokenHandled=PR_TRUE; break; case eHTMLTag_script: theHeadIsParent=(!mHasOpenBody); mHasOpenScript=PR_TRUE; default: break; }//switch if(!isTokenHandled) { if(theHeadIsParent || (mHasOpenHead && ((eHTMLTag_newline==theChildTag) || (eHTMLTag_whitespace==theChildTag)))) { result=AddHeadLeaf(theNode); } else result=HandleDefaultStartToken(aToken,theChildTag,theNode); } //now do any post processing necessary on the tag... if(NS_OK==result) DidHandleStartTag(*theNode,theChildTag); }//if } //if RecycleNode(theNode); return result; } /** * Call this to see if you have a closeable peer on the stack that * is ABOVE one of its root tags. * * @update gess 4/11/99 * @param aRootTagList -- list of root tags for aTag * @param aTag -- tag to test for containership * @return PR_TRUE if given tag can contain other tags */ static PRBool HasCloseablePeerAboveRoot(TagList& aRootTagList,nsDTDContext& aContext,eHTMLTags aTag,PRBool anEndTag) { PRInt32 theRootIndex=GetTopmostIndexOf(aContext,aRootTagList); TagList* theCloseTags=(anEndTag) ? gHTMLElements[aTag].GetAutoCloseEndTags() : gHTMLElements[aTag].GetAutoCloseStartTags(); PRInt32 theChildIndex=-1; if(theCloseTags) { theChildIndex=GetTopmostIndexOf(aContext,*theCloseTags); } else { if((anEndTag) || (!gHTMLElements[aTag].CanContainSelf())) theChildIndex=aContext.GetTopmostIndexOf(aTag); } // I changed this to theRootIndex<=theChildIndex so to handle this case: //
    ... allow nested
    * 4. ... */ //Note: This method is going away. First we need to get the elementtable to do closures right, and // therefore we must get residual style handling to work. PRBool result=PR_TRUE; if(aContext.GetCount()){ TagList* theRootTags=gHTMLElements[aChildTag].GetRootTags(); TagList* theSpecialParents=gHTMLElements[aChildTag].GetSpecialParents(); if(theRootTags) { PRInt32 theRootIndex=GetTopmostIndexOf(aContext,*theRootTags); PRInt32 theSPIndex=(theSpecialParents) ? GetTopmostIndexOf(aContext,*theSpecialParents) : kNotFound; PRInt32 theChildIndex=GetIndexOfChildOrSynonym(aContext,aChildTag); PRInt32 theBaseIndex=(theRootIndex>theSPIndex) ? theRootIndex : theSPIndex; if((theBaseIndex==theChildIndex) && (gHTMLElements[aChildTag].CanContainSelf())) result=PR_TRUE; else result=PRBool(theBaseIndex>theChildIndex); } } return result; } enum eProcessRule {eIgnore,eTest}; /** * This method gets called when a start token has been * encountered in the parse process. If the current container * can contain this tag, then add it. Otherwise, you have * two choices: 1) create an implicit container for this tag * to be stored in * 2) close the top container, and add this to * whatever container ends up on top. * * @update gess 3/25/98 * @param aToken -- next (start) token to be handled * @param aNode -- CParserNode representing this start token * @return PR_TRUE if all went well; PR_FALSE if error occured */ nsresult CNavDTD::HandleDefaultStartToken(CToken* aToken,eHTMLTags aChildTag,nsIParserNode *aNode) { NS_PRECONDITION(0!=aToken,kNullToken); nsresult result=NS_OK; PRBool theChildAgrees=PR_TRUE; PRInt32 theIndex=mBodyContext->GetCount(); PRBool theChildIsContainer=nsHTMLElement::IsContainer(aChildTag); PRBool theParentContains=-1; do { eHTMLTags theParentTag=mBodyContext->TagAt(--theIndex); theParentContains=CanContain(theParentTag,aChildTag); //precompute containment, and pass it to CanOmit()... if(CanOmit(theParentTag,aChildTag,theParentContains)) { result=HandleOmittedTag(aToken,aChildTag,theParentTag,aNode); return result; } eProcessRule theRule=eTest; switch(theRule){ case eTest: theChildAgrees=PR_TRUE; if(theParentContains) { eHTMLTags theAncestor=gHTMLElements[aChildTag].mExcludingAncestor; if(eHTMLTag_unknown!=theAncestor){ theChildAgrees=!HasOpenContainer(theAncestor); } if(theChildAgrees){ theAncestor=gHTMLElements[aChildTag].mRequiredAncestor; if(eHTMLTag_unknown!=theAncestor){ theChildAgrees=HasOpenContainer(theAncestor); } } if(theChildAgrees) { //This code is here to allow DT/DD (or similar) to close arbitrary stuff between //this instance and a prior one on the stack. I had to add this because //(for now) DT is inline, and fontstyle's can contain them (until Res-style code works) if(gHTMLElements[aChildTag].HasSpecialProperty(kMustCloseSelf)){ theChildAgrees=CanBeContained(aChildTag,*mBodyContext); } } } if(!(theParentContains && theChildAgrees)) { if (!CanPropagate(theParentTag,aChildTag,theParentContains)) { if(theChildIsContainer || (!theParentContains)){ if(!gHTMLElements[aChildTag].CanAutoCloseTag(theParentTag)) { // Closing the tags above might cause non-compatible results. // Ex.
    Text
    . // In the example above