/* -*- 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. */ /** * MODULE NOTES: * @update gess 4/8/98 * * */ /** * TRANSIENT STYLE-HANDLING NOTES: * @update gess 6/15/98 * * ...add comments here about transient style stack. * */ #include "nsIParserDebug.h" #include "CNavDTD.h" #include "nsHTMLTokens.h" #include "nsCRT.h" #include "nsParser.h" #include "nsHTMLContentSink.h" #include "nsScanner.h" #include "nsParserTypes.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" #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* kNullTokenizer = "Error: Unable to construct tokenizer"; static const char* kNullToken = "Error: Null token given"; static const char* kInvalidTagStackPos = "Error: invalid tag stack position"; static nsAutoString gEmpty; static char formElementTags[]= { eHTMLTag_button, eHTMLTag_fieldset, eHTMLTag_input, eHTMLTag_isindex, eHTMLTag_label, eHTMLTag_legend, eHTMLTag_option, eHTMLTag_select, eHTMLTag_textarea,0}; static char gHeadingTags[]={ eHTMLTag_h1, eHTMLTag_h2, eHTMLTag_h3, eHTMLTag_h4, eHTMLTag_h5, eHTMLTag_h6, 0}; static char gStyleTags[]={ eHTMLTag_a, eHTMLTag_bold, eHTMLTag_big, eHTMLTag_blink, eHTMLTag_cite, eHTMLTag_em, eHTMLTag_font, eHTMLTag_italic, eHTMLTag_kbd, eHTMLTag_small, eHTMLTag_spell, eHTMLTag_strike, eHTMLTag_strong, eHTMLTag_sub, eHTMLTag_sup, eHTMLTag_tt, eHTMLTag_u, eHTMLTag_var, 0}; static char gWhitespaceTags[]={ eHTMLTag_newline, eHTMLTag_whitespace, 0}; /** * 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; } ((nsISupports*) *aInstancePtr)->AddRef(); 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; 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)); } class CNavTokenDeallocator: public nsDequeFunctor{ public: virtual void operator()(void* anObject) { CToken* aToken = (CToken*)anObject; delete aToken; } }; static CNavTokenDeallocator gTokenKiller; /** * Default constructor * * @update gess 4/9/98 * @param * @return */ CNavDTD::CNavDTD() : nsIDTD(), mTokenDeque(gTokenKiller) { NS_INIT_REFCNT(); mParser=0; mFilename=0; mParserDebug=0; nsCRT::zero(mLeafBits,sizeof(mLeafBits)); nsCRT::zero(mContextStack,sizeof(mContextStack)); nsCRT::zero(mStyleStack,sizeof(mStyleStack)); nsCRT::zero(mTokenHandlers,sizeof(mTokenHandlers)); mContextStackPos=0; mStyleStackPos=0; mHasOpenForm=PR_FALSE; mHasOpenMap=PR_FALSE; InitializeDefaultTokenHandlers(); } /** * Default destructor * * @update gess 4/9/98 * @param * @return */ CNavDTD::~CNavDTD(){ DeleteTokenHandlers(); if (mFilename) PL_strfree(mFilename); if (mParserDebug) NS_RELEASE(mParserDebug); // NS_RELEASE(mSink); } /** * * @update gess5/18/98 * @param * @return */ PRInt32 CNavDTD::WillBuildModel(const char* aFilename, nsIParserDebug* aParserDebug){ PRInt32 result=0; if (mFilename) { PL_strfree(mFilename); mFilename=0; } if(aFilename) { mFilename = PL_strdup(aFilename); } mParserDebug = aParserDebug; NS_IF_ADDREF(mParserDebug); if(mSink) mSink->WillBuildModel(); return result; } /** * * @update gess5/18/98 * @param * @return */ PRInt32 CNavDTD::DidBuildModel(PRInt32 anErrorCode){ PRInt32 result=0; if((kNoError==anErrorCode) && (mContextStackPos>0)) { CloseContainersTo(0,eHTMLTag_unknown,PR_FALSE); } if(mSink) { mSink->DidBuildModel(1); } 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 */ PRInt32 CNavDTD::HandleToken(CToken* aToken){ PRInt32 result=0; if(aToken) { CHTMLToken* theToken= (CHTMLToken*)(aToken); eHTMLTokenTypes theType=eHTMLTokenTypes(theToken->GetTokenType()); CTokenHandler* aHandler=GetTokenHandler(theType); if(aHandler) { result=(*aHandler)(theToken,this); if (mParserDebug) mParserDebug->Verify(this, mParser, mContextStackPos, mContextStack, mFilename); } }//if 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 */ PRInt32 CNavDTD::HandleDefaultStartToken(CToken* aToken,eHTMLTags aChildTag,nsCParserNode& aNode) { NS_PRECONDITION(0!=aToken,kNullToken); eHTMLTags parentTag=(eHTMLTags)GetTopNode(); PRInt32 result=kNoError; PRBool contains=CanContain(parentTag,aChildTag); if(PR_FALSE==contains){ result=CreateContextStackFor(aChildTag); if(kNoError!=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); if(PR_FALSE==CanContain(GetTopNode(),aChildTag)) { //we unwound too far; now we have to recreate a valid context stack. result=CreateContextStackFor(aChildTag); } } } if(IsContainer(aChildTag)){ if(PR_TRUE==mLeafBits[mContextStackPos-1]) { CloseTransientStyles(aChildTag); } result=OpenContainer(aNode,PR_TRUE); } else { if(PR_FALSE==mLeafBits[mContextStackPos-1]) { OpenTransientStyles(aChildTag); } result=AddLeaf(aNode); } 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 */ PRInt32 CNavDTD::HandleStartToken(CToken* aToken) { NS_PRECONDITION(0!=aToken,kNullToken); CStartToken* st= (CStartToken*)(aToken); eHTMLTags tokenTagType=(eHTMLTags)st->GetTypeID(); //Begin by gathering up attributes... nsCParserNode attrNode((CHTMLToken*)aToken); PRInt16 attrCount=aToken->GetAttributeCount(); PRInt32 theCount; PRInt32 result=(0==attrCount) ? kNoError : mParser->CollectAttributes(attrNode,attrCount); if(kNoError==result) { //now check to see if this token should be omitted... if(PR_FALSE==CanOmit(GetTopNode(),tokenTagType)) { switch(tokenTagType) { case eHTMLTag_html: result=OpenHTML(attrNode); break; case eHTMLTag_title: { nsCParserNode theNode(st); result=OpenHead(theNode); //open the head... if(kNoError==result) { result=mParser->CollectSkippedContent(attrNode,theCount); mSink->SetTitle(attrNode.GetSkippedContent()); result=CloseHead(theNode); //close the head... } } break; case eHTMLTag_textarea: { mParser->CollectSkippedContent(attrNode,theCount); result=AddLeaf(attrNode); } break; case eHTMLTag_form: result = OpenForm(attrNode); break; case eHTMLTag_meta: case eHTMLTag_link: { nsCParserNode theNode((CHTMLToken*)aToken); result=OpenHead(theNode); if(kNoError==result) result=AddLeaf(theNode); if(kNoError==result) result=CloseHead(theNode); } break; case eHTMLTag_style: { nsCParserNode theNode((CHTMLToken*)aToken); result=OpenHead(theNode); if(kNoError==result) { mParser->CollectSkippedContent(attrNode,theCount); if(kNoError==result) { result=AddLeaf(attrNode); if(kNoError==result) result=CloseHead(theNode); } } } break; case eHTMLTag_script: result=HandleScriptToken(st); break; case eHTMLTag_head: break; //ignore head tags... case eHTMLTag_base: result=OpenHead(attrNode); if(kNoError==result) { result=AddLeaf(attrNode); if(kNoError==result) result=CloseHead(attrNode); } break; case eHTMLTag_nobr: result=PR_TRUE; case eHTMLTag_map: result=PR_TRUE; default: result=HandleDefaultStartToken(aToken,tokenTagType,attrNode); break; } //switch } //if } //if 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 */ PRInt32 CNavDTD::HandleEndToken(CToken* aToken) { NS_PRECONDITION(0!=aToken,kNullToken); PRInt32 result=kNoError; 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 AND explicit style tag (goofy, huh?) /* if(0!=strchr(gStyleTags,tokenTagType)){ eHTMLTags topTag=(eHTMLTags)GetTopNode(); if(0!=strchr(gStyleTags,topTag)){ tokenTagType=topTag; } } */ //now check to see if this token should be omitted... if(PR_TRUE==CanOmitEndTag(GetTopNode(),tokenTagType)) { UpdateStyleStackForCloseTag(tokenTagType,tokenTagType); return result; } nsCParserNode theNode((CHTMLToken*)aToken); 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: result=CloseContainer(theNode,tokenTagType,PR_TRUE); break; case eHTMLTag_form: { nsCParserNode aNode((CHTMLToken*)aToken); result=CloseForm(aNode); } 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 */ PRInt32 CNavDTD::HandleEntityToken(CToken* aToken) { NS_PRECONDITION(0!=aToken,kNullToken); CEntityToken* et = (CEntityToken*)(aToken); PRInt32 result=kNoError; eHTMLTags tokenTagType=(eHTMLTags)et->GetTypeID(); if(PR_FALSE==CanOmit(GetTopNode(),tokenTagType)) { nsCParserNode aNode((CHTMLToken*)aToken); 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 */ PRInt32 CNavDTD::HandleCommentToken(CToken* aToken) { NS_PRECONDITION(0!=aToken,kNullToken); return kNoError; } /** * 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 */ PRInt32 CNavDTD::HandleSkippedContentToken(CToken* aToken) { NS_PRECONDITION(0!=aToken,kNullToken); PRInt32 result=kNoError; if(HasOpenContainer(eHTMLTag_body)) { nsCParserNode aNode((CHTMLToken*)aToken); 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 */ PRInt32 CNavDTD::HandleAttributeToken(CToken* aToken) { NS_PRECONDITION(0!=aToken,kNullToken); NS_ERROR("attribute encountered -- this shouldn't happen!"); CAttributeToken* at = (CAttributeToken*)(aToken); PRInt32 result=kNoError; return result; } /** * 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 */ PRInt32 CNavDTD::HandleScriptToken(CToken* aToken) { NS_PRECONDITION(0!=aToken,kNullToken); nsCParserNode theNode((CHTMLToken*)aToken); PRInt32 theCount=0; PRInt32 result=mParser->CollectSkippedContent(theNode,theCount); 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 */ PRInt32 CNavDTD::HandleStyleToken(CToken* aToken){ NS_PRECONDITION(0!=aToken,kNullToken); CStyleToken* st = (CStyleToken*)(aToken); PRInt32 result=kNoError; 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) { int i=0; for(i=eToken_unknown;i0) && (aTypeGetTokenType(); if(type=0;i--){ if(mContextStack[i]==aTag) return i; } return kNotFound; } /********************************************* Here comes code that handles the interface to our content sink. *********************************************/ /** * It is with great trepidation that I offer this method (privately of course). * The gets called whenever a container gets opened. This methods job is to * take a look at the (transient) style stack, and open any style containers that * are there. Of course, we shouldn't bother to open styles that are incompatible * with our parent container. * * @update gess6/4/98 * @param tag of the container just opened * @return 0 (for now) */ PRInt32 CNavDTD::OpenTransientStyles(eHTMLTags aTag){ PRInt32 result=0; if(0==strchr(gWhitespaceTags,aTag)){ PRInt32 pos=0; eHTMLTags parentTag=(eHTMLTags)GetTopNode(); if(CanContainStyles(parentTag)) { for(pos=0;posclose any style containers * that are there. Of course, we shouldn't bother to open styles that are incompatible * with our parent container. * SEE THE TOP OF THIS FILE for more information about how the transient style stack works. * * @update gess6/4/98 * @param tag of the container just opened * @return 0 (for now) */ PRInt32 CNavDTD::CloseTransientStyles(eHTMLTags aTag){ PRInt32 result=0; if((mStyleStackPos>0) && (mLeafBits[mContextStackPos-1])) { if(0==strchr(gWhitespaceTags,aTag)){ result=CloseContainersTo(mStyleStack[0],PR_FALSE); mLeafBits[mContextStackPos-1]=PR_FALSE; }//if }//if return result; } /** * This method does two things: 1st, help construct * our own internal model of the content-stack; and * 2nd, pass this message on to the sink. * * @update gess4/22/98 * @param aNode -- next node to be added to model * @return TRUE if ok, FALSE if error */ PRInt32 CNavDTD::OpenHTML(const nsIParserNode& aNode){ NS_PRECONDITION(mContextStackPos >= 0, kInvalidTagStackPos); PRInt32 result=mSink->OpenHTML(aNode); mContextStack[mContextStackPos++]=(eHTMLTags)aNode.GetNodeType(); return result; } /** * This method does two things: 1st, help construct * our own internal model of the content-stack; and * 2nd, pass this message on to the sink. * * @update gess4/6/98 * @param aNode -- next node to be removed from our model * @return TRUE if ok, FALSE if error */ PRInt32 CNavDTD::CloseHTML(const nsIParserNode& aNode){ NS_PRECONDITION(mContextStackPos > 0, kInvalidTagStackPos); PRInt32 result=mSink->CloseHTML(aNode); mContextStack[--mContextStackPos]=eHTMLTag_unknown; return result; } /** * This method does two things: 1st, help construct * our own internal model of the content-stack; and * 2nd, pass this message on to the sink. * @update gess4/6/98 * @param aNode -- next node to be added to model * @return TRUE if ok, FALSE if error */ PRInt32 CNavDTD::OpenHead(const nsIParserNode& aNode){ mContextStack[mContextStackPos++]=eHTMLTag_head; PRInt32 result=mSink->OpenHead(aNode); return result; } /** * This method does two things: 1st, help construct * our own internal model of the content-stack; and * 2nd, pass this message on to the sink. * @update gess4/6/98 * @param aNode -- next node to be removed from our model * @return TRUE if ok, FALSE if error */ PRInt32 CNavDTD::CloseHead(const nsIParserNode& aNode){ PRInt32 result=mSink->CloseHead(aNode); mContextStack[--mContextStackPos]=eHTMLTag_unknown; return result; } /** * This method does two things: 1st, help construct * our own internal model of the content-stack; and * 2nd, pass this message on to the sink. * @update gess4/6/98 * @param aNode -- next node to be added to model * @return TRUE if ok, FALSE if error */ PRInt32 CNavDTD::OpenBody(const nsIParserNode& aNode){ NS_PRECONDITION(mContextStackPos >= 0, kInvalidTagStackPos); PRInt32 result=kNoError; eHTMLTags topTag=(eHTMLTags)CNavDTD::GetTopNode(); if(eHTMLTag_html!=topTag) { //ok, there are two cases: // 1. Nobody opened the html container // 2. Someone left the head (or other) open PRInt32 pos=GetTopmostIndexOf(eHTMLTag_html); if(kNotFound!=pos) { //if you're here, it means html is open, //but some other tag(s) are in the way. //So close other tag(s). result=CloseContainersTo(pos+1,eHTMLTag_body,PR_TRUE); } else { //if you're here, it means that there is //no HTML tag in document. Let's open it. result=CloseContainersTo(0,eHTMLTag_html,PR_TRUE); //close current stack containers. CHTMLToken token(gEmpty); nsCParserNode htmlNode(&token); token.SetTypeID(eHTMLTag_html); //open the html container... result=OpenHTML(htmlNode); } } if(kNoError==result) { result=mSink->OpenBody(aNode); mContextStack[mContextStackPos++]=(eHTMLTags)aNode.GetNodeType(); } return result; } /** * This method does two things: 1st, help close * our own internal model of the content-stack; and * 2nd, pass this message on to the sink. * @update gess4/6/98 * @param aNode -- next node to be removed from our model * @return TRUE if ok, FALSE if error */ PRInt32 CNavDTD::CloseBody(const nsIParserNode& aNode){ NS_PRECONDITION(mContextStackPos >= 0, kInvalidTagStackPos); PRInt32 result=mSink->CloseBody(aNode); mContextStack[--mContextStackPos]=eHTMLTag_unknown; return result; } /** * This method does two things: 1st, help construct * our own internal model of the content-stack; and * 2nd, pass this message on to the sink. * @update gess4/6/98 * @param aNode -- next node to be added to model * @return TRUE if ok, FALSE if error */ PRInt32 CNavDTD::OpenForm(const nsIParserNode& aNode){ if(mHasOpenForm) CloseForm(aNode); PRInt32 result=mSink->OpenForm(aNode); if(kNoError==result) mHasOpenForm=PR_TRUE; return result; } /** * This method does two things: 1st, help construct * our own internal model of the content-stack; and * 2nd, pass this message on to the sink. * @update gess4/6/98 * @param aNode -- next node to be removed from our model * @return TRUE if ok, FALSE if error */ PRInt32 CNavDTD::CloseForm(const nsIParserNode& aNode){ PRInt32 result=kNoError; if(mHasOpenForm) { mHasOpenForm=PR_FALSE; result=mSink->CloseForm(aNode); } return result; } /** * This method does two things: 1st, help construct * our own internal model of the content-stack; and * 2nd, pass this message on to the sink. * @update gess4/6/98 * @param aNode -- next node to be added to model * @return TRUE if ok, FALSE if error */ PRInt32 CNavDTD::OpenMap(const nsIParserNode& aNode){ if(mHasOpenMap) CloseMap(aNode); //NOTE: We need to change to method so that it opens a MAP, // instead of a FORM. This was copy/paste coding at its best. PRInt32 result=mSink->OpenForm(aNode); if(kNoError==result) mHasOpenMap=PR_TRUE; return result; } /** * This method does two things: 1st, help construct * our own internal model of the content-stack; and * 2nd, pass this message on to the sink. * @update gess4/6/98 * @param aNode -- next node to be removed from our model * @return TRUE if ok, FALSE if error */ PRInt32 CNavDTD::CloseMap(const nsIParserNode& aNode){ PRInt32 result=kNoError; if(mHasOpenMap) { mHasOpenMap=PR_FALSE; //NOTE: We need to change to method so that it closes a MAP, // instead of a FORM. This was copy/paste coding at its best. result=mSink->CloseForm(aNode); } return result; } /** * This method does two things: 1st, help construct * our own internal model of the content-stack; and * 2nd, pass this message on to the sink. * @update gess4/6/98 * @param aNode -- next node to be added to model * @return TRUE if ok, FALSE if error */ PRInt32 CNavDTD::OpenFrameset(const nsIParserNode& aNode){ NS_PRECONDITION(mContextStackPos >= 0, kInvalidTagStackPos); PRInt32 result=mSink->OpenFrameset(aNode); mContextStack[mContextStackPos++]=(eHTMLTags)aNode.GetNodeType(); return result; } /** * This method does two things: 1st, help construct * our own internal model of the content-stack; and * 2nd, pass this message on to the sink. * @update gess4/6/98 * @param aNode -- next node to be removed from our model * @return TRUE if ok, FALSE if error */ PRInt32 CNavDTD::CloseFrameset(const nsIParserNode& aNode){ NS_PRECONDITION(mContextStackPos > 0, kInvalidTagStackPos); PRInt32 result=mSink->CloseFrameset(aNode); mContextStack[--mContextStackPos]=eHTMLTag_unknown; return result; } /** * This method does two things: 1st, help construct * our own internal model of the content-stack; and * 2nd, pass this message on to the sink. * @update gess4/6/98 * @param aNode -- next node to be added to model * @return TRUE if ok, FALSE if error */ PRInt32 CNavDTD::OpenContainer(const nsIParserNode& aNode,PRBool aUpdateStyleStack){ NS_PRECONDITION(mContextStackPos > 0, kInvalidTagStackPos); PRInt32 result=kNoError; eHTMLTags nodeType=(eHTMLTags)aNode.GetNodeType(); // CloseTransientStyles(nodeType); switch(nodeType) { case eHTMLTag_html: result=OpenHTML(aNode); break; case eHTMLTag_body: result=OpenBody(aNode); break; case eHTMLTag_style: case eHTMLTag_textarea: case eHTMLTag_head: case eHTMLTag_title: break; case eHTMLTag_form: result=OpenForm(aNode); break; default: result=mSink->OpenContainer(aNode); mContextStack[mContextStackPos++]=nodeType; break; } if((kNoError==result) && (PR_TRUE==aUpdateStyleStack)){ UpdateStyleStackForOpenTag(nodeType,nodeType); } return result; } /** * This method does two things: 1st, help construct * our own internal model of the content-stack; and * 2nd, pass this message on to the sink. * @update gess4/6/98 * @param aNode -- next node to be removed from our model * @return TRUE if ok, FALSE if error */ PRInt32 CNavDTD::CloseContainer(const nsIParserNode& aNode,eHTMLTags aTag,PRBool aUpdateStyles){ NS_PRECONDITION(mContextStackPos > 0, kInvalidTagStackPos); PRInt32 result=kNoError; //was false eHTMLTags nodeType=(eHTMLTags)aNode.GetNodeType(); //XXX Hack! We know this is wrong, but it works //for the general case until we get it right. switch(nodeType) { case eHTMLTag_html: result=CloseHTML(aNode); break; case eHTMLTag_style: case eHTMLTag_textarea: break; case eHTMLTag_head: //result=CloseHead(aNode); break; case eHTMLTag_body: result=CloseBody(aNode); break; case eHTMLTag_form: result=CloseForm(aNode); break; case eHTMLTag_title: default: result=mSink->CloseContainer(aNode); mContextStack[--mContextStackPos]=eHTMLTag_unknown; break; } mLeafBits[mContextStackPos]=PR_FALSE; if((kNoError==result) && (PR_TRUE==aUpdateStyles)){ UpdateStyleStackForCloseTag(nodeType,aTag); } return result; } /** * This method does two things: 1st, help construct * our own internal model of the content-stack; and * 2nd, pass this message on to the sink. * @update gess4/6/98 * @param * @return TRUE if ok, FALSE if error */ PRInt32 CNavDTD::CloseContainersTo(PRInt32 anIndex,eHTMLTags aTag,PRBool aUpdateStyles){ NS_PRECONDITION(mContextStackPos > 0, kInvalidTagStackPos); PRInt32 result=kNoError; CEndToken aToken(gEmpty); nsCParserNode theNode(&aToken); if((anIndex=0)) { while(mContextStackPos>anIndex) { eHTMLTags theTag=mContextStack[mContextStackPos-1]; aToken.SetTypeID(theTag); result=CloseContainer(theNode,aTag,aUpdateStyles); } } return result; } /** * This method does two things: 1st, help construct * our own internal model of the content-stack; and * 2nd, pass this message on to the sink. * @update gess4/6/98 * @param * @return TRUE if ok, FALSE if error */ PRInt32 CNavDTD::CloseContainersTo(eHTMLTags aTag,PRBool aUpdateStyles){ NS_PRECONDITION(mContextStackPos > 0, kInvalidTagStackPos); PRInt32 pos=GetTopmostIndexOf(aTag); if(kNotFound!=pos) { //the tag is indeed open, so close it. return CloseContainersTo(pos,aTag,aUpdateStyles); } eHTMLTags theTopTag=(eHTMLTags)GetTopNode(); if(IsCompatibleStyleTag(aTag,theTopTag)) { //if you're here, it's because we're trying to close one style tag, //but a different one is actually open. Because this is NAV4x //compatibililty mode, we must close the one that's really open. aTag=theTopTag; pos=GetTopmostIndexOf(aTag); if(kNotFound!=pos) { //the tag is indeed open, so close it. return CloseContainersTo(pos,aTag,aUpdateStyles); } } PRInt32 result=kNoError; eHTMLTags theParentTag=GetDefaultParentTagFor(aTag); pos=GetTopmostIndexOf(theParentTag); if(kNotFound!=pos) { //the parent container is open, so close it instead result=CloseContainersTo(pos+1,aTag,aUpdateStyles); } return result; } /** * This method causes the topmost container on the stack * to be closed. * @update gess4/6/98 * @see CloseContainer() * @param * @return TRUE if ok, FALSE if error */ PRInt32 CNavDTD::CloseTopmostContainer(){ NS_PRECONDITION(mContextStackPos > 0, kInvalidTagStackPos); CEndToken aToken(gEmpty); eHTMLTags theTag=(eHTMLTags)mContextStack[mContextStackPos-1]; aToken.SetTypeID(theTag); nsCParserNode theNode(&aToken); return CloseContainer(theNode,theTag,PR_TRUE); } /** * This method does two things: 1st, help construct * our own internal model of the content-stack; and * 2nd, pass this message on to the sink. * @update gess4/6/98 * @param aNode -- next node to be added to model * @return TRUE if ok, FALSE if error */ PRInt32 CNavDTD::AddLeaf(const nsIParserNode& aNode){ PRInt32 result=mSink->AddLeaf(aNode); return result; } /** * This method gets called to create a valid context stack * for the given child. We compare the current stack to the * default needs of the child, and push new guys onto the * stack until the child can be properly placed. * * @update gess 4/8/98 * @param aChildTag is the child for whom we need to * create a new context vector * @return true if we succeeded, otherwise false */ PRInt32 CNavDTD::CreateContextStackFor(eHTMLTags aChildTag){ nsAutoString theVector; PRInt32 result=kNoError; PRInt32 pos=0; PRInt32 cnt=0; eHTMLTags theTop=GetTopNode(); if(PR_TRUE==ForwardPropagate(theVector,theTop,aChildTag)){ //add code here to build up context stack based on forward propagated context vector... pos=0; cnt=theVector.Length()-1; if(mContextStack[mContextStackPos-1]==theVector[cnt]) result=kNoError; else result=kContextMismatch; } else { PRBool tempResult; if(eHTMLTag_unknown!=theTop) { tempResult=BackwardPropagate(theVector,theTop,aChildTag); if(eHTMLTag_html!=theTop) BackwardPropagate(theVector,eHTMLTag_html,theTop); } else tempResult=BackwardPropagate(theVector,eHTMLTag_html,aChildTag); if(PR_TRUE==tempResult) { //propagation worked, so pop unwanted containers, push new ones, then exit... pos=0; cnt=theVector.Length(); result=kNoError; while(pos0) { switch (aTag) { case eHTMLTag_a: case eHTMLTag_bold: case eHTMLTag_big: case eHTMLTag_blink: case eHTMLTag_cite: case eHTMLTag_em: case eHTMLTag_font: case eHTMLTag_italic: case eHTMLTag_kbd: case eHTMLTag_small: case eHTMLTag_spell: case eHTMLTag_strike: case eHTMLTag_strong: case eHTMLTag_sub: case eHTMLTag_sup: case eHTMLTag_tt: case eHTMLTag_u: case eHTMLTag_var: if(aTag==anActualTag) mStyleStack[--mStyleStackPos]=eHTMLTag_unknown; break; case eHTMLTag_h1: case eHTMLTag_h2: case eHTMLTag_h3: case eHTMLTag_h4: case eHTMLTag_h5: case eHTMLTag_h6: break; default: break; }//switch }//if return result; } //update... /******************************************************************* These methods used to be hidden in the tokenizer-delegate. That file merged with the DTD, since the separation wasn't really buying us anything. *******************************************************************/ /** * This method is called just after a "<" has been consumed * and we know we're at the start of some kind of tagged * element. We don't know yet if it's a tag or a comment. * * @update gess 5/12/98 * @param aChar is the last char read * @param aScanner is represents our input source * @param aToken is the out arg holding our new token * @return error code (may return kInterrupted). */ PRInt32 CNavDTD::ConsumeTag(PRUnichar aChar,CScanner& aScanner,CToken*& aToken) { nsAutoString empty(""); PRInt32 result=aScanner.GetChar(aChar); if(kNoError==result) { switch(aChar) { case kForwardSlash: PRUnichar ch; result=aScanner.Peek(ch); if(kNoError==result) { if(nsString::IsAlpha(ch)) aToken=new CEndToken(empty); else aToken=new CCommentToken(empty); //Special case: is treated as a comment }//if break; case kExclamation: aToken=new CCommentToken(empty); break; default: if(nsString::IsAlpha(aChar)) return ConsumeStartTag(aChar,aScanner,aToken); else if(kEOF!=aChar) { nsAutoString temp("<"); return ConsumeText(temp,aScanner,aToken); } } //switch if((0!=aToken) && (kNoError==result)) { result= aToken->Consume(aChar,aScanner); //tell new token to finish consuming text... if(result) { delete aToken; aToken=0; } } //if } //if return result; } /** * This method is called just after we've consumed a start * tag, and we now have to consume its attributes. * * @update gess 3/25/98 * @param aChar: last char read * @param aScanner: see nsScanner.h * @return */ PRInt32 CNavDTD::ConsumeAttributes(PRUnichar aChar,CScanner& aScanner,CStartToken* aToken) { PRBool done=PR_FALSE; PRInt32 result=kNoError; nsAutoString as(""); PRInt16 theAttrCount=0; while((!done) && (result==kNoError)) { CAttributeToken* theToken= new CAttributeToken(as); if(theToken){ result=theToken->Consume(aChar,aScanner); //tell new token to finish consuming text... //Much as I hate to do this, here's some special case code. //This handles the case of empty-tags in XML. Our last //attribute token will come through with a text value of "" //and a textkey of "/". We should destroy it, and tell the //start token it was empty. nsString& key=theToken->GetKey(); nsString& text=theToken->GetText(); if((key[0]==kForwardSlash) && (0==text.Length())){ //tada! our special case! Treat it like an empty start tag... aToken->SetEmpty(PR_TRUE); delete theToken; } else if(kNoError==result){ theAttrCount++; mTokenDeque.Push(theToken); }//if else delete theToken; //we can't keep it... }//if if(kNoError==result){ result=aScanner.Peek(aChar); if(aChar==kGreaterThan) { //you just ate the '>' aScanner.GetChar(aChar); //skip the '>' done=PR_TRUE; }//if }//if }//while aToken->SetAttributeCount(theAttrCount); return result; } /** * This is a special case method. It's job is to consume * all of the given tag up to an including the end tag. * * @param aChar: last char read * @param aScanner: see nsScanner.h * @param anErrorCode: arg that will hold error condition * @return new token or null */ PRInt32 CNavDTD::ConsumeContentToEndTag(const nsString& aString,PRUnichar aChar,CScanner& aScanner,CToken*& aToken){ //In the case that we just read the given tag, we should go and //consume all the input until we find a matching end tag. nsAutoString endTag(""); aToken=new CSkippedContentToken(endTag); return aToken->Consume(aChar,aScanner); //tell new token to finish consuming text... } /** * This method is called just after a "<" has been consumed * and we know we're at the start of a tag. * * @update gess 3/25/98 * @param aChar: last char read * @param aScanner: see nsScanner.h * @param anErrorCode: arg that will hold error condition * @return new token or null */ PRInt32 CNavDTD::ConsumeStartTag(PRUnichar aChar,CScanner& aScanner,CToken*& aToken) { PRInt32 theDequeSize=mTokenDeque.GetSize(); PRInt32 result=kNoError; aToken=new CStartToken(nsAutoString("")); if(aToken) { result= aToken->Consume(aChar,aScanner); //tell new token to finish consuming text... if(kNoError==result) { if(((CStartToken*)aToken)->IsAttributed()) { result=ConsumeAttributes(aChar,aScanner,(CStartToken*)aToken); } //now that that's over with, we have one more problem to solve. //In the case that we just read a