/* -*- 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" #include "nsTagHandler.h" #include "nsViewSourceHTML.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 const char* kViewSourceCommand= "view-source"; 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_bgsound, eHTMLTag_br, eHTMLTag_col, eHTMLTag_embed, eHTMLTag_frame, eHTMLTag_img, eHTMLTag_keygen, eHTMLTag_hr, eHTMLTag_input, eHTMLTag_link, eHTMLTag_isindex, eHTMLTag_meta, eHTMLTag_param, eHTMLTag_plaintext, eHTMLTag_sound, eHTMLTag_style, eHTMLTag_spacer, eHTMLTag_wbr, eHTMLTag_newline, eHTMLTag_text, eHTMLTag_whitespace, 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}; static eHTMLTags gBlockParentTags []={ eHTMLTag_applet, eHTMLTag_basefont, eHTMLTag_blockquote, eHTMLTag_body, eHTMLTag_center, eHTMLTag_dd, eHTMLTag_div, eHTMLTag_embed, eHTMLTag_fieldset, eHTMLTag_form, eHTMLTag_iframe, eHTMLTag_layer, eHTMLTag_li, eHTMLTag_listing, eHTMLTag_multicol, eHTMLTag_noembed, eHTMLTag_noframes, eHTMLTag_nolayer, eHTMLTag_noscript, eHTMLTag_object, eHTMLTag_plaintext, eHTMLTag_pre, eHTMLTag_td, eHTMLTag_th, eHTMLTag_ul, eHTMLTag_xmp, }; static eHTMLTags gBlockChildTags []={ eHTMLTag_a, eHTMLTag_abbr, eHTMLTag_acronym, eHTMLTag_address, eHTMLTag_applet, eHTMLTag_b, eHTMLTag_basefont, eHTMLTag_bdo, eHTMLTag_bgsound, eHTMLTag_big, eHTMLTag_blink, eHTMLTag_blockquote, eHTMLTag_br, eHTMLTag_button, eHTMLTag_center, eHTMLTag_cite, eHTMLTag_code, eHTMLTag_del, eHTMLTag_dfn, eHTMLTag_dir, eHTMLTag_div, eHTMLTag_dl, 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_ilayer, eHTMLTag_img, eHTMLTag_ins, eHTMLTag_isindex, eHTMLTag_input, eHTMLTag_kbd, eHTMLTag_label, eHTMLTag_layer, eHTMLTag_listing, eHTMLTag_map, /*eHTMLTag_marque,*/ eHTMLTag_menu, eHTMLTag_multicol, eHTMLTag_nobr, eHTMLTag_noembed, eHTMLTag_noframes, eHTMLTag_nolayer, eHTMLTag_noscript, eHTMLTag_object, eHTMLTag_ol, eHTMLTag_p, eHTMLTag_plaintext, eHTMLTag_pre, eHTMLTag_q, eHTMLTag_s, eHTMLTag_samp, eHTMLTag_script, eHTMLTag_select, eHTMLTag_small, eHTMLTag_sound, eHTMLTag_spacer, eHTMLTag_span, eHTMLTag_strike, eHTMLTag_strong, eHTMLTag_sub, eHTMLTag_sup, eHTMLTag_table, eHTMLTag_textarea, eHTMLTag_tt, eHTMLTag_u, eHTMLTag_ul, eHTMLTag_userdefined, eHTMLTag_var, eHTMLTag_wbr, eHTMLTag_xmp, //a few special cases... eHTMLTag_newline, eHTMLTag_whitespace, }; static eHTMLTags gInlineParentTags []={ eHTMLTag_a, eHTMLTag_abbr, eHTMLTag_address, eHTMLTag_b, eHTMLTag_bdo, eHTMLTag_big, eHTMLTag_blink, eHTMLTag_button, eHTMLTag_caption, eHTMLTag_cite, eHTMLTag_code, eHTMLTag_dfn, eHTMLTag_dt, eHTMLTag_em, eHTMLTag_font, eHTMLTag_h1, eHTMLTag_h2, eHTMLTag_h3, eHTMLTag_h4, eHTMLTag_h5, eHTMLTag_h6, eHTMLTag_i, eHTMLTag_ilayer, eHTMLTag_kbd, eHTMLTag_label, eHTMLTag_legend, /*eHTMLTag_marquee,*/ eHTMLTag_nobr, eHTMLTag_ol, eHTMLTag_p, eHTMLTag_q, eHTMLTag_s, eHTMLTag_samp, eHTMLTag_small, eHTMLTag_span, eHTMLTag_strike, eHTMLTag_strong, eHTMLTag_sub, eHTMLTag_sup, eHTMLTag_td, eHTMLTag_th, eHTMLTag_tt, eHTMLTag_u, eHTMLTag_ul, eHTMLTag_var }; static eHTMLTags gInlineChildTags []={ eHTMLTag_a, eHTMLTag_abbr, eHTMLTag_acronym, eHTMLTag_applet, eHTMLTag_b, eHTMLTag_bdo, eHTMLTag_bgsound, eHTMLTag_big, eHTMLTag_blink, eHTMLTag_br, eHTMLTag_button, eHTMLTag_cite, eHTMLTag_code, eHTMLTag_dfn, eHTMLTag_em, eHTMLTag_embed, eHTMLTag_font, eHTMLTag_form, //Added this for backward compatibility... eHTMLTag_i, eHTMLTag_iframe, eHTMLTag_ilayer, eHTMLTag_img, eHTMLTag_input, eHTMLTag_kbd, eHTMLTag_label, eHTMLTag_layer, //Added this for backward compatibility... eHTMLTag_li, eHTMLTag_map, eHTMLTag_nobr, eHTMLTag_object, eHTMLTag_p, eHTMLTag_pre, eHTMLTag_q, eHTMLTag_s, eHTMLTag_samp, eHTMLTag_script, eHTMLTag_select, eHTMLTag_small, eHTMLTag_sound, eHTMLTag_spacer, eHTMLTag_span, eHTMLTag_strike, eHTMLTag_strong, eHTMLTag_sub, eHTMLTag_sup, eHTMLTag_table, //Added this for backward compatibility... eHTMLTag_textarea,eHTMLTag_tt, eHTMLTag_u, eHTMLTag_userdefined, eHTMLTag_var, eHTMLTag_wbr, eHTMLTag_text, eHTMLTag_newline, eHTMLTag_whitespace, }; //This is a convenience set, which combines block AND inline child lists... static eHTMLTags gBlockAndInlineChildTags []={ eHTMLTag_a, eHTMLTag_abbr, eHTMLTag_acronym, eHTMLTag_address, eHTMLTag_applet, eHTMLTag_b, eHTMLTag_basefont, eHTMLTag_bdo, eHTMLTag_bgsound, eHTMLTag_big, eHTMLTag_blink, eHTMLTag_blockquote, eHTMLTag_br, eHTMLTag_button, eHTMLTag_center, eHTMLTag_cite, eHTMLTag_code, eHTMLTag_dfn, eHTMLTag_del, eHTMLTag_dir, eHTMLTag_div, eHTMLTag_dl, 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_ilayer, eHTMLTag_img, eHTMLTag_ins, eHTMLTag_isindex, eHTMLTag_input, eHTMLTag_kbd, eHTMLTag_label, eHTMLTag_layer, eHTMLTag_li, eHTMLTag_listing, eHTMLTag_map, /*eHTMLTag_marque,*/ eHTMLTag_menu, eHTMLTag_multicol, eHTMLTag_nobr, eHTMLTag_noembed, eHTMLTag_noframes, eHTMLTag_nolayer, eHTMLTag_noscript, eHTMLTag_object, eHTMLTag_ol, eHTMLTag_p, eHTMLTag_plaintext, eHTMLTag_pre, eHTMLTag_q, eHTMLTag_s, eHTMLTag_samp, eHTMLTag_script, eHTMLTag_select, eHTMLTag_small, eHTMLTag_sound, eHTMLTag_spacer, eHTMLTag_span, eHTMLTag_strike, eHTMLTag_strong, eHTMLTag_sub, eHTMLTag_sup, eHTMLTag_table, eHTMLTag_textarea, eHTMLTag_tt, eHTMLTag_u, eHTMLTag_ul, eHTMLTag_userdefined, eHTMLTag_var, eHTMLTag_wbr, eHTMLTag_xmp, //a few special cases... eHTMLTag_text, eHTMLTag_newline, eHTMLTag_whitespace, }; static eHTMLTags gPREChildTags []={ eHTMLTag_a, eHTMLTag_abbr, eHTMLTag_acronym, eHTMLTag_b, eHTMLTag_bdo, eHTMLTag_bgsound, eHTMLTag_br, eHTMLTag_button, eHTMLTag_cite, eHTMLTag_code, eHTMLTag_dfn, eHTMLTag_em, eHTMLTag_embed, eHTMLTag_i, eHTMLTag_ilayer, eHTMLTag_input, eHTMLTag_kbd, eHTMLTag_label, eHTMLTag_li, eHTMLTag_map, eHTMLTag_q, eHTMLTag_s, eHTMLTag_samp, eHTMLTag_script, eHTMLTag_select, eHTMLTag_sound, eHTMLTag_spacer, eHTMLTag_span, eHTMLTag_strike, eHTMLTag_strong, eHTMLTag_textarea,eHTMLTag_tt, eHTMLTag_u, eHTMLTag_var, eHTMLTag_wbr, eHTMLTag_text, eHTMLTag_newline, eHTMLTag_whitespace, }; static eHTMLTags gHeadChildTags []={ eHTMLTag_base, eHTMLTag_bgsound, eHTMLTag_isindex, eHTMLTag_link, eHTMLTag_meta, /*eHTMLTag_nextid,*/ eHTMLTag_script, eHTMLTag_sound, eHTMLTag_style, eHTMLTag_title }; static eHTMLTags gFormChildTags []={ eHTMLTag_button, eHTMLTag_input, eHTMLTag_keygen, eHTMLTag_label, eHTMLTag_select, eHTMLTag_textarea }; static CTokenRecycler gTokenRecycler; /*************************************************************** 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() : mDeallocator(), mTagHandlerDeque(mDeallocator) { } 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; } CTagHandlerDeallocator mDeallocator; nsDeque mTagHandlerDeque; CTagFinder mTagFinder; }; /************************************************************************ 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; } /** * 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)); } static CTokenDeallocator gTokenKiller; /** * Default constructor * * @update gess 4/9/98 * @param * @return */ CNavDTD::CNavDTD() : nsIDTD(), mTokenDeque(gTokenKiller) { NS_INIT_REFCNT(); mParser=0; mSink = 0; mDTDDebug=0; mLineNumber=1; mParseMode=eParseMode_navigator; nsCRT::zero(mTokenHandlers,sizeof(mTokenHandlers)); mHasOpenForm=PR_FALSE; mHasOpenMap=PR_FALSE; mAllowUnknownTags=PR_FALSE; InitializeDefaultTokenHandlers(); mHeadContext=new nsDTDContext(); mBodyContext=new nsDTDContext(); mFormContext=0; mMapContext=0; // DebugDumpContainmentRules(*this,"c:/temp/DTDRules1.out","New CNavDTD Containment Rules"); } /** * 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, nsString& aCommand, PRInt32 aVersion){ PRBool result=PR_FALSE; if(!aCommand.Equals(kViewSourceCommand)) { 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; else { //otherwise, look into the buffer to see if you recognize anything... if(BufferContainsHTML(aBuffer)){ result=eValidDetect; if(0==aType.Length()) aType=kHTMLTextContentType; } } 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 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 */ PRInt32 CNavDTD::DidHandleStartTag(CToken* aToken,eHTMLTags aChildTag){ PRInt32 result=kNoError; CToken* theNextToken=mParser->PeekToken(); switch(aChildTag){ case eHTMLTag_pre: case eHTMLTag_listing: { 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 } break; case eHTMLTag_plaintext: case eHTMLTag_xmp: //grab the skipped content and dump it out as text... if(theNextToken){ eHTMLTokenTypes theType=eHTMLTokenTypes(theNextToken->GetTokenType()); if(eToken_skippedcontent==theType){ nsString& theText=((CAttributeToken*)theNextToken)->GetKey(); CViewSourceHTML::WriteText(theText,*mSink,PR_TRUE); } } break; default: break; }//switch 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(); PRInt32 theAttrCount; 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) { result=CollectSkippedContent(attrNode,theAttrCount); mSink->SetTitle(attrNode.GetSkippedContent()); result=CloseHead(attrNode); //close the head... } } break; case eHTMLTag_link: case eHTMLTag_base: case eHTMLTag_meta: result=AddHeadLeaf(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); nsCParserNode aNode((CHTMLToken*)aToken,mLineNumber); nsresult result=mSink->AddComment(aNode); return result; } /** * 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); nsCParserNode aNode((CHTMLToken*)aToken,mLineNumber); nsresult result=mSink->AddProcessingInstruction(aNode); 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) */ 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 preceeding tables determine the set of elements each tag can contain... ***********************************************************************************/ /** * This method is called to determine whether or not a tag * of one type can contain a tag of another type. * * @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::CanContain(PRInt32 aParent,PRInt32 aChild) const { PRBool result=PR_FALSE; if(!IsContainer(aParent)) return result; /*************************************************************************** * Handle form elements here. Why? Because as long as a form is open, * almost any form element is allowed. (Except