/* -*- 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.0 (the "NPL"); you may not use this file except in * compliance with the NPL. You may obtain a copy of the NPL at * http://www.mozilla.org/NPL/ * * Software distributed under the NPL is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL * for the specific language governing rights and limitations under the * NPL. * * The Initial Developer of this code under the NPL is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All Rights * Reserved. */ #include "nsDebug.h" #include "nsIDTDDebug.h" #include "CNavDTD.h" #include "nsHTMLTokens.h" #include "nsCRT.h" #include "nsParser.h" #include "nsIHTMLContentSink.h" #include "nsScanner.h" #include "nsParserTypes.h" #include "nsVoidArray.h" #include "nsTokenHandler.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" #ifdef XP_PC #include //this is here for debug reasons... #endif #include "prmem.h" 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* kNullURL = "Error: Null URL given"; //static const char* kNullFilename= "Error: Null filename given"; static const char* kNullToken = "Error: Null token given"; static const char* kInvalidTagStackPos = "Error: invalid tag stack position"; static const char* kHTMLTextContentType = "text/html"; static char* kVerificationDir = "c:/temp"; static nsAutoString gEmpty; static eHTMLTags gFormElementTags[]= { eHTMLTag_button, eHTMLTag_fieldset, eHTMLTag_input, eHTMLTag_isindex, eHTMLTag_label, eHTMLTag_legend, eHTMLTag_option, eHTMLTag_select, eHTMLTag_textarea}; static eHTMLTags gHeadingTags[]={ eHTMLTag_h1, eHTMLTag_h2, eHTMLTag_h3, eHTMLTag_h4, eHTMLTag_h5, eHTMLTag_h6}; static eHTMLTags gStyleTags[]={ eHTMLTag_a, eHTMLTag_acronym, eHTMLTag_b, eHTMLTag_bdo, eHTMLTag_big, eHTMLTag_blink, eHTMLTag_center, eHTMLTag_cite, eHTMLTag_code, eHTMLTag_del, eHTMLTag_dfn, eHTMLTag_em, eHTMLTag_font, eHTMLTag_i, eHTMLTag_ins, eHTMLTag_kbd, eHTMLTag_nobr, eHTMLTag_q, eHTMLTag_s, eHTMLTag_samp, eHTMLTag_small, eHTMLTag_span, eHTMLTag_strike, eHTMLTag_strong, eHTMLTag_sub, eHTMLTag_sup, eHTMLTag_tt, eHTMLTag_u, eHTMLTag_var}; static eHTMLTags gNonContainers[]={ eHTMLTag_area, eHTMLTag_base, eHTMLTag_basefont, eHTMLTag_br, eHTMLTag_col, eHTMLTag_embed, eHTMLTag_frame, eHTMLTag_hr, eHTMLTag_whitespace, eHTMLTag_input, eHTMLTag_link, eHTMLTag_isindex, eHTMLTag_meta, eHTMLTag_param, eHTMLTag_plaintext, eHTMLTag_style, eHTMLTag_spacer, eHTMLTag_wbr, eHTMLTag_newline, eHTMLTag_text, eHTMLTag_img, eHTMLTag_unknown, eHTMLTag_xmp}; static eHTMLTags gTableTags[]={ eHTMLTag_caption, eHTMLTag_col, eHTMLTag_colgroup, eHTMLTag_tbody, eHTMLTag_tfoot, eHTMLTag_tr, eHTMLTag_thead, eHTMLTag_td}; static eHTMLTags gWhitespaceTags[]={ eHTMLTag_newline, eHTMLTag_whitespace}; /************************************************************** Now define the token deallocator class... **************************************************************/ class CNavTokenDeallocator: public nsDequeFunctor{ public: virtual void* operator()(void* anObject) { CToken* aToken = (CToken*)anObject; delete aToken; return 0; } }; static CNavTokenDeallocator gTokenKiller; /************************************************************** Now define the tokenrecycler class... **************************************************************/ /************************************************************************ CTokenRecycler class implementation. This class is used to recycle tokens. By using this simple class, we cut WAY down on the number of tokens that get created during the run of the system. ************************************************************************/ class nsCTokenRecycler : public nsITokenRecycler { public: // enum {eCacheMaxSize=100}; nsCTokenRecycler(); virtual ~nsCTokenRecycler(); virtual void RecycleToken(CToken* aToken); virtual CToken* CreateTokenOfType(eHTMLTokenTypes aType,eHTMLTags aTag, const nsString& aString); protected: nsDeque* mTokenCache[eToken_last-1]; // PRInt32 mTotals[eToken_last-1]; }; /** * * @update gess7/25/98 * @param */ nsCTokenRecycler::nsCTokenRecycler() : nsITokenRecycler() { int i=0; for(i=0;iGetTokenType(); mTokenCache[theType-1]->Push(aToken); } } /** * * @update gess8/4/98 * @param * @return */ CToken* nsCTokenRecycler::CreateTokenOfType(eHTMLTokenTypes aType,eHTMLTags aTag, const nsString& aString) { CToken* result=(CToken*)mTokenCache[aType-1]->Pop(); if(result) { result->Reinitialize(aTag,aString); } else { // mTotals[aType-1]++; switch(aType){ case eToken_start: result=new CStartToken(aTag); break; case eToken_end: result=new CEndToken(aTag); break; case eToken_comment: result=new CCommentToken(); break; case eToken_attribute: result=new CAttributeToken(); break; case eToken_entity: result=new CEntityToken(); break; case eToken_whitespace: result=new CWhitespaceToken(); break; case eToken_newline: result=new CNewlineToken(); break; case eToken_text: result=new CTextToken(aString); break; case eToken_script: result=new CScriptToken(); break; case eToken_style: result=new CStyleToken(); break; case eToken_skippedcontent: result=new CSkippedContentToken(aString); break; case eToken_instruction:result=new CInstructionToken(); break; default: break; } } return result; } nsCTokenRecycler gTokenRecycler; /************************************************************************ 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; } /** * This method is defined in nsIParser. It is used to * cause the COM-like construction of an nsParser. * * @update gess 4/8/98 * @param nsIParser** ptr to newly instantiated parser * @return NS_xxx error result */ NS_HTMLPARS nsresult NS_NewNavHTMLDTD(nsIDTD** aInstancePtrResult) { CNavDTD* it = new CNavDTD(); if (it == 0) { return NS_ERROR_OUT_OF_MEMORY; } return it->QueryInterface(kClassIID, (void **) aInstancePtrResult); } NS_IMPL_ADDREF(CNavDTD) NS_IMPL_RELEASE(CNavDTD) /** * * * @update gess 6/9/98 * @param * @return */ PRInt32 NavDispatchTokenHandler(CToken* aToken,nsIDTD* aDTD) { PRInt32 result=0; CHTMLToken* theToken= (CHTMLToken*)(aToken); eHTMLTokenTypes theType= (eHTMLTokenTypes)theToken->GetTokenType(); CNavDTD* theDTD=(CNavDTD*)aDTD; if(aDTD) { switch(theType) { case eToken_start: result=theDTD->HandleStartToken(aToken); break; case eToken_end: result=theDTD->HandleEndToken(aToken); break; case eToken_comment: result=theDTD->HandleCommentToken(aToken); break; case eToken_entity: result=theDTD->HandleEntityToken(aToken); break; case eToken_whitespace: result=theDTD->HandleStartToken(aToken); break; case eToken_newline: result=theDTD->HandleStartToken(aToken); break; case eToken_text: result=theDTD->HandleStartToken(aToken); break; case eToken_attribute: result=theDTD->HandleAttributeToken(aToken); break; case eToken_style: result=theDTD->HandleStyleToken(aToken); break; case eToken_skippedcontent: result=theDTD->HandleSkippedContentToken(aToken); break; case eToken_instruction: result=theDTD->HandleProcessingInstructionToken(aToken); break; default: result=0; }//switch }//if return result; } /** * init the set of default token handlers... * * @update gess 3/25/98 * @param * @return */ void CNavDTD::InitializeDefaultTokenHandlers() { AddTokenHandler(new CTokenHandler(NavDispatchTokenHandler,eToken_start)); AddTokenHandler(new CTokenHandler(NavDispatchTokenHandler,eToken_end)); AddTokenHandler(new CTokenHandler(NavDispatchTokenHandler,eToken_comment)); AddTokenHandler(new CTokenHandler(NavDispatchTokenHandler,eToken_entity)); AddTokenHandler(new CTokenHandler(NavDispatchTokenHandler,eToken_whitespace)); AddTokenHandler(new CTokenHandler(NavDispatchTokenHandler,eToken_newline)); AddTokenHandler(new CTokenHandler(NavDispatchTokenHandler,eToken_text)); AddTokenHandler(new CTokenHandler(NavDispatchTokenHandler,eToken_attribute)); // AddTokenHandler(new CTokenHandler(NavDispatchTokenHandler,eToken_script)); AddTokenHandler(new CTokenHandler(NavDispatchTokenHandler,eToken_style)); AddTokenHandler(new CTokenHandler(NavDispatchTokenHandler,eToken_skippedcontent)); AddTokenHandler(new CTokenHandler(NavDispatchTokenHandler,eToken_instruction)); } /** * Default constructor * * @update gess 4/9/98 * @param * @return */ CNavDTD::CNavDTD() : nsIDTD(), mTokenDeque(gTokenKiller) { NS_INIT_REFCNT(); mParser=0; mSink = nsnull; mDTDDebug=0; mLineNumber=1; mParseMode=eParseMode_navigator; nsCRT::zero(mTokenHandlers,sizeof(mTokenHandlers)); mHasOpenForm=PR_FALSE; mHasOpenMap=PR_FALSE; InitializeDefaultTokenHandlers(); mHeadContext=new nsDTDContext(); mBodyContext=new nsDTDContext(); mFormContext=0; mMapContext=0; } /** * Default destructor * * @update gess 4/9/98 * @param * @return */ CNavDTD::~CNavDTD(){ DeleteTokenHandlers(); delete mHeadContext; delete mBodyContext; 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); } /** * * @update gess8/4/98 * @param * @return */ nsITokenRecycler* CNavDTD::GetTokenRecycler(void){ // return 0; return &gTokenRecycler; } /** * 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){ PRBool result=PR_TRUE; 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); } if(mDTDDebug) { mDTDDebug->Verify(this,mParser, mBodyContext->mElements.mCount, mBodyContext->mElements.mTags,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. */ PRBool CNavDTD::CanParse(nsString& aContentType, PRInt32 aVersion){ PRBool result=aContentType.Equals(kHTMLTextContentType); return result; } /** * * @update gess7/7/98 * @param * @return */ eAutoDetectResult CNavDTD::AutoDetectContentType(nsString& aBuffer,nsString& aType){ eAutoDetectResult result=eUnknownDetect; if(PR_TRUE==aType.Equals(kHTMLTextContentType)) result=eValidDetect; return result; } /** * * @update gess5/18/98 * @param * @return */ nsresult CNavDTD::WillBuildModel(nsString& aFilename,PRBool aNotifySink){ nsresult result=NS_OK; mFilename=aFilename; if((aNotifySink) && (mSink)) { mLineNumber=1; result = mSink->WillBuildModel(); } return result; } /** * * @update gess5/18/98 * @param * @return */ nsresult CNavDTD::DidBuildModel(PRInt32 anErrorCode,PRBool aNotifySink){ nsresult result= NS_OK; if((kNoError==anErrorCode) && (mBodyContext->mElements.mCount>0)) { result = CloseContainersTo(0,eHTMLTag_unknown,PR_FALSE); } if((aNotifySink) && (mSink)) { result = mSink->DidBuildModel(1); } 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 aType * @param aToken * @param aParser * @return */ nsresult CNavDTD::HandleToken(CToken* aToken){ nsresult result=NS_OK; if(aToken) { CHTMLToken* theToken= (CHTMLToken*)(aToken); eHTMLTokenTypes theType=eHTMLTokenTypes(theToken->GetTokenType()); CITokenHandler* theHandler=GetTokenHandler(theType); if(theHandler) { result=(*theHandler)(theToken,this); if (mDTDDebug) mDTDDebug->Verify(this, mParser, mBodyContext->mElements.mCount, mBodyContext->mElements.mTags, mFilename); } }//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 */ PRInt32 CNavDTD::DidHandleStartTag(CToken* aToken,eHTMLTags aChildTag){ PRInt32 result=kNoError; CToken* theNextToken=mParser->PeekToken(); if(theNextToken) { eHTMLTokenTypes theType=eHTMLTokenTypes(theNextToken->GetTokenType()); if(eToken_newline==theType){ switch(aChildTag){ case eHTMLTag_pre: case eHTMLTag_listing: //we skip the first newline token inside PRE and LISTING mParser->PopToken(); break; default: break; }//switch }//if }//if return result; } void CompareResults(PRBool canContain,eHTMLTags aParentTag,eHTMLTags aChildTag){ /* PRBool theNewResult=CanContainEx(aParentTag,aChildTag); if(canContain!=theNewResult){ printf("%s(child:%i,parent:%i)\n","can contain!=can containex",aChildTag,aParentTag); } */ } /** * 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; eHTMLTags theParentTag=mBodyContext->mElements.Last(); if(RequiresAutomaticClosure(theParentTag,aChildTag)){ result=CloseContainersTo(aChildTag,PR_TRUE); } PRBool theCanContainResult=CanContain(theParentTag,aChildTag); CompareResults(theCanContainResult,theParentTag,aChildTag); if(PR_FALSE==theCanContainResult){ if(CanPropagate(theParentTag,aChildTag)) result=CreateContextStackFor(aChildTag); else result=kCantPropagate; if(NS_OK!=result) { //if you're here, then the new topmost container can't contain aToken. //You must determine what container hierarchy you need to hold aToken, //and create that on the parsestack. result=ReduceContextStackFor(aChildTag); PRBool theCanContainResult=CanContain(mBodyContext->mElements.Last(),aChildTag); CompareResults(theCanContainResult,mBodyContext->mElements.Last(),aChildTag); if(PR_FALSE==theCanContainResult) { //we unwound too far; now we have to recreate a valid context stack. result=CreateContextStackFor(aChildTag); } } } if(IsContainer(aChildTag)){ if(PR_TRUE==mBodyContext->mElements.mBits[mBodyContext->mElements.mCount-1]) { CloseTransientStyles(aChildTag); } result=OpenContainer(aNode,PR_TRUE); } else { if(PR_FALSE==mBodyContext->mElements.mBits[mBodyContext->mElements.mCount-1]) { OpenTransientStyles(aChildTag); } result=AddLeaf(aNode); } //now do any post processing necessary on the tag... if(NS_OK==result) DidHandleStartTag(aToken,aChildTag); 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 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::HandleStartToken(CToken* aToken) { NS_PRECONDITION(0!=aToken,kNullToken); //Begin by gathering up attributes... nsCParserNode attrNode((CHTMLToken*)aToken,mLineNumber); PRInt16 attrCount=aToken->GetAttributeCount(); nsresult result=(0==attrCount) ? NS_OK : CollectAttributes(attrNode,attrCount); eHTMLTags theParent=mBodyContext->mElements.Last(); eHTMLTags theChildTag=(eHTMLTags)aToken->GetTypeID(); if(NS_OK==result) { //now check to see if this token should be omitted... if(PR_FALSE==CanOmit(theParent,theChildTag)) { switch(theChildTag) { case eHTMLTag_title: { result=OpenHead(attrNode); //open the head... if(NS_OK==result) { PRInt32 theCount; result=CollectSkippedContent(attrNode,theCount); mSink->SetTitle(attrNode.GetSkippedContent()); result=CloseHead(attrNode); //close the head... } } break; case eHTMLTag_link: case eHTMLTag_base: case eHTMLTag_meta: { result=OpenHead(attrNode); if(NS_OK==result) result=AddLeaf(attrNode); result=CloseHead(attrNode); } break; case eHTMLTag_style: { result=OpenHead(attrNode); if(NS_OK==result) { PRInt32 theCount; CollectSkippedContent(attrNode,theCount); if(NS_OK==result) { result=AddLeaf(attrNode); if(NS_OK==result) result=CloseHead(attrNode); } } } break; case eHTMLTag_area: if (mHasOpenMap) { result = mSink->AddLeaf(attrNode); } break; default: result=HandleDefaultStartToken(aToken,theChildTag,attrNode); break; } //switch } //if } //if if(eHTMLTag_newline==theChildTag) mLineNumber++; return result; } /** * This method gets called when an end token has been * encountered in the parse process. If the end tag matches * the start tag on the stack, then simply close it. Otherwise, * we have a erroneous state condition. This can be because we * have a close tag with no prior open tag (user error) or because * we screwed something up in the parse process. I'm not sure * yet how to tell the difference. * * @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::HandleEndToken(CToken* aToken) { NS_PRECONDITION(0!=aToken,kNullToken); nsresult result=NS_OK; CEndToken* et = (CEndToken*)(aToken); eHTMLTags tokenTagType=(eHTMLTags)et->GetTypeID(); // Here's the hacky part: // Because we're trying to be backward compatible with Nav4/5, // we have to handle explicit styles the way it does. That means // that we keep an internal style stack.When an EndToken occurs, // we should see if it is an explicit style tag. If so, we can // close the explicit style tag (goofy, huh?) //now check to see if this token should be omitted, or //if it's gated from closing by the presence of another tag. if(PR_TRUE==CanOmitEndTag(mBodyContext->mElements.Last(),tokenTagType)) { UpdateStyleStackForCloseTag(tokenTagType,tokenTagType); return result; } nsCParserNode theNode((CHTMLToken*)aToken,mLineNumber); switch(tokenTagType) { case eHTMLTag_style: case eHTMLTag_link: case eHTMLTag_meta: case eHTMLTag_textarea: case eHTMLTag_title: case eHTMLTag_head: case eHTMLTag_script: break; case eHTMLTag_map: case eHTMLTag_form: { nsCParserNode aNode((CHTMLToken*)aToken,mLineNumber); result=CloseContainer(aNode,tokenTagType,PR_FALSE); } break; case eHTMLTag_td: case eHTMLTag_th: result=CloseContainersTo(tokenTagType,PR_TRUE); // Empty the transient style stack (we just closed any extra // ones off so it's safe to do it now) because they don't carry // forward across table cell boundaries. mBodyContext->mStyles->mCount=0; break; default: if(IsContainer(tokenTagType)){ result=CloseContainersTo(tokenTagType,PR_TRUE); } // break; } 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); CEntityToken* et = (CEntityToken*)(aToken); nsresult result=NS_OK; if(PR_FALSE==CanOmit(mBodyContext->mElements.Last(),eHTMLTag_entity)) { nsCParserNode aNode((CHTMLToken*)aToken,mLineNumber); result=AddLeaf(aNode); } 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); return NS_OK; } /** * This method gets called when a skippedcontent token has * been encountered in the parse process. After verifying * that the topmost container can contain text, we call * AddLeaf to store this token in the top container. * * @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::HandleSkippedContentToken(CToken* aToken) { NS_PRECONDITION(0!=aToken,kNullToken); nsresult result=NS_OK; if(HasOpenContainer(eHTMLTag_body)) { nsCParserNode aNode((CHTMLToken*)aToken,mLineNumber); result=AddLeaf(aNode); } 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!"); return NS_OK; } /** * This method gets called when a script 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::HandleScriptToken(nsCParserNode& aNode) { nsresult result=NS_OK; PRInt32 pos=GetTopmostIndexOf(eHTMLTag_body); PRInt32 attrCount=aNode.GetAttributeCount(PR_TRUE); if (kNotFound == pos) { // We're in the HEAD, but don't bother to open it. if(NS_OK==result) { CollectSkippedContent(aNode,attrCount); result=AddLeaf(aNode); }//if }//if else { // We're in the BODY CollectSkippedContent(aNode,attrCount); result=AddLeaf(aNode); } return result; } /** * This method gets called when a style 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::HandleStyleToken(CToken* aToken){ NS_PRECONDITION(0!=aToken,kNullToken); // CStyleToken* st = (CStyleToken*)(aToken); 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); // CStyleToken* st = (CStyleToken*)(aToken); return NS_OK; } /** * 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) */ PRInt32 CNavDTD::CollectAttributes(nsCParserNode& aNode,PRInt32 aCount){ int attr=0; for(attr=0;attrPeekToken(); if(theToken) { eHTMLTokenTypes theType=eHTMLTokenTypes(theToken->GetTokenType()); if(eToken_attribute==theType){ mParser->PopToken(); //pop it for real... aNode.AddAttribute(theToken); } } else return kInterrupted; } return kNoError; } /** * Causes the next skipped-content token (if any) to * be consumed by this node. * @update gess5/11/98 * @param node to consume skipped-content * @param holds the number of skipped content elements encountered * @return Error condition. */ PRInt32 CNavDTD::CollectSkippedContent(nsCParserNode& aNode,PRInt32& aCount) { PRInt32 result=kNoError; eHTMLTokenTypes theType; CToken* theToken; aCount=0; do{ theToken=mParser->PeekToken(); if(theToken) { theType=eHTMLTokenTypes(theToken->GetTokenType()); if(eToken_skippedcontent==theType) { mParser->PopToken(); aNode.SetSkippedContent(theToken); aCount++; } } } while(theToken && (eToken_skippedcontent==theType)); return result; } /** * Finds a tag handler for the given tag type, given in string. * * @update gess 4/2/98 * @param aString contains name of tag to be handled * @return valid tag handler (if found) or null */ void CNavDTD::DeleteTokenHandlers(void) { for(int i=eToken_unknown;i0) && (aTypeGetTokenType(); if(typeGetParseMode(); // mParseMode=eParseMode_noquirks; } /** * This method gets called in order to set the content * sink for this parser to dump nodes to. * * @update gess 3/25/98 * @param nsIContentSink interface for node receiver * @return */ nsIContentSink* CNavDTD::SetContentSink(nsIContentSink* aSink) { nsIContentSink* old=mSink; mSink=(nsIHTMLContentSink*)aSink; return old; } /** * This method is called to determine whether or not a tag * can contain an explict style tag (font, italic, bold, etc.) * Most can -- but some, like option, cannot. Therefore we * don't bother to open transient styles within these elements. * * @update gess 4/8/98 * @param aParent -- tag enum of parent container * @param aChild -- tag enum of child container * @return PR_TRUE if parent can contain child */ PRBool CNavDTD::CanContainStyles(eHTMLTags aParent) const { PRBool result=PR_TRUE; switch(aParent) { case eHTMLTag_option: result=PR_FALSE; break; default: break; } return result; } /*********************************************************************************** The following tables determine the set of elements each tag can contain... ***********************************************************************************/ static eHTMLTags gTagSet1[]={ eHTMLTag_a, eHTMLTag_abbr, eHTMLTag_acronym, eHTMLTag_address, eHTMLTag_applet, eHTMLTag_blink, eHTMLTag_b, eHTMLTag_basefont, eHTMLTag_bdo, eHTMLTag_big, eHTMLTag_blockquote,eHTMLTag_br, eHTMLTag_button, eHTMLTag_center, eHTMLTag_cite, eHTMLTag_code, eHTMLTag_del, eHTMLTag_dfn, eHTMLTag_dir, eHTMLTag_div, eHTMLTag_dl, eHTMLTag_dt, eHTMLTag_em, eHTMLTag_embed, eHTMLTag_fieldset, eHTMLTag_font, eHTMLTag_form, eHTMLTag_h1, eHTMLTag_h2, eHTMLTag_h3, eHTMLTag_h4, eHTMLTag_h5, eHTMLTag_h6, eHTMLTag_hr, eHTMLTag_i, eHTMLTag_iframe, eHTMLTag_img, eHTMLTag_input, eHTMLTag_ins, eHTMLTag_isindex, eHTMLTag_kbd, eHTMLTag_label, eHTMLTag_layer, eHTMLTag_li, eHTMLTag_map, eHTMLTag_menu, eHTMLTag_newline, eHTMLTag_nobr, eHTMLTag_noframes, eHTMLTag_noscript, eHTMLTag_object, eHTMLTag_ol, eHTMLTag_p, eHTMLTag_pre, eHTMLTag_q, eHTMLTag_s, eHTMLTag_strike, eHTMLTag_samp, eHTMLTag_script, eHTMLTag_select, eHTMLTag_small, eHTMLTag_spacer, eHTMLTag_span, eHTMLTag_strong, eHTMLTag_sub, eHTMLTag_sup, eHTMLTag_table, eHTMLTag_text, eHTMLTag_textarea, eHTMLTag_tt, eHTMLTag_u, eHTMLTag_ul, eHTMLTag_userdefined, eHTMLTag_var, eHTMLTag_wbr, eHTMLTag_whitespace}; static eHTMLTags gTagSet2[]={ eHTMLTag_a, eHTMLTag_abbr, eHTMLTag_acronym, eHTMLTag_applet, eHTMLTag_blink, eHTMLTag_b, eHTMLTag_basefont, eHTMLTag_bdo, eHTMLTag_big, eHTMLTag_br, eHTMLTag_button, eHTMLTag_cite, eHTMLTag_code, eHTMLTag_del, eHTMLTag_dfn, eHTMLTag_div, eHTMLTag_em, eHTMLTag_font, eHTMLTag_hr, eHTMLTag_embed, eHTMLTag_i, eHTMLTag_iframe, eHTMLTag_img, eHTMLTag_input, eHTMLTag_ins, eHTMLTag_kbd, eHTMLTag_label, eHTMLTag_layer, eHTMLTag_map, eHTMLTag_newline, eHTMLTag_nobr, eHTMLTag_object, eHTMLTag_p, eHTMLTag_q, eHTMLTag_s, eHTMLTag_strike, eHTMLTag_samp, eHTMLTag_script, eHTMLTag_select, eHTMLTag_small, eHTMLTag_spacer, eHTMLTag_span, eHTMLTag_strong, eHTMLTag_sub, eHTMLTag_sup, eHTMLTag_text, eHTMLTag_textarea, eHTMLTag_table,// XXX kipp was here eHTMLTag_tt, eHTMLTag_u, eHTMLTag_userdefined, eHTMLTag_var, eHTMLTag_wbr, eHTMLTag_whitespace}; static eHTMLTags gTagSet3[]={ eHTMLTag_a, eHTMLTag_abbr, eHTMLTag_acronym, eHTMLTag_applet, eHTMLTag_blink, eHTMLTag_b, eHTMLTag_bdo, eHTMLTag_big, eHTMLTag_br, eHTMLTag_blockquote, eHTMLTag_body, eHTMLTag_caption, eHTMLTag_center, eHTMLTag_cite, eHTMLTag_code, eHTMLTag_dd, eHTMLTag_del, eHTMLTag_dfn, eHTMLTag_div, eHTMLTag_dt, eHTMLTag_em, eHTMLTag_fieldset, eHTMLTag_embed, eHTMLTag_font, eHTMLTag_form, eHTMLTag_h1, eHTMLTag_h2, eHTMLTag_h3, eHTMLTag_h4, eHTMLTag_h5, eHTMLTag_h6, eHTMLTag_i, eHTMLTag_iframe, eHTMLTag_ins, eHTMLTag_kbd, eHTMLTag_label, eHTMLTag_legend, eHTMLTag_li, eHTMLTag_map, eHTMLTag_newline, eHTMLTag_noframes, eHTMLTag_noscript, eHTMLTag_object, eHTMLTag_p, eHTMLTag_pre, eHTMLTag_q, eHTMLTag_s, eHTMLTag_strike, eHTMLTag_samp, eHTMLTag_small, eHTMLTag_spacer, eHTMLTag_span, eHTMLTag_strong, eHTMLTag_sub, eHTMLTag_sup, eHTMLTag_td, eHTMLTag_text, eHTMLTag_th, eHTMLTag_tt, eHTMLTag_u, eHTMLTag_userdefined, eHTMLTag_var, eHTMLTag_wbr, eHTMLTag_whitespace}; /*********************************************************************************** The preceeding tables determine the set of elements each tag can contain... ***********************************************************************************/ /** * This method quickly scans the given set of tags, * looking for the given tag. * @update gess8/27/98 * @param aTag -- tag to be search for in set * @param aTagSet -- set of tags to be searched * @return */ inline PRBool FindTagInSet(PRInt32 aTag,const eHTMLTags aTagSet[],PRInt32 aCount) { PRInt32 index; for(index=0;index which must have a *