/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: NPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Netscape Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/NPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * rickg@netscape.com * * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the NPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the NPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /************************************************************************ * MODULE NOTES: * @update gess 04.08.2000 * * - CElement::mAutoClose should only be set for tags whose end tag * is optional. * * ************************************************************************/ #ifndef _COTHERELEMENTS_ #define _COTHERELEMENTS_ #include "nsDTDUtils.h" /************************************************************************ This union is a bitfield which describes the group membership ************************************************************************/ struct CGroupBits { PRUint32 mHead: 1; PRUint32 mHeadMisc: 1; //script, style, meta, link, object PRUint32 mHeadContent: 1; //title, base PRUint32 mFontStyle : 1; PRUint32 mPhrase: 1; PRUint32 mSpecial: 1; PRUint32 mFormControl: 1; PRUint32 mHeading: 1; PRUint32 mBlock: 1; PRUint32 mFrame:1; PRUint32 mList: 1; PRUint32 mPreformatted: 1; PRUint32 mTable: 1; PRUint32 mSelf: 1; PRUint32 mLeaf: 1; PRUint32 mWhiteSpace: 1; PRUint32 mComment: 1; PRUint32 mTextContainer: 1; PRUint32 mTopLevel: 1; PRUint32 mDTDInternal: 1; PRUint32 mFlowEntity: 1; PRUint32 mBlockEntity: 1; PRUint32 mInlineEntity: 1; }; union CGroupMembers { PRUint32 mAllBits; CGroupBits mBits; }; inline PRBool ContainsGroup(CGroupMembers& aGroupSet,CGroupMembers& aGroup) { PRBool result=PR_FALSE; if(aGroup.mAllBits) { result=(aGroupSet.mAllBits & aGroup.mAllBits) ? PR_TRUE : PR_FALSE; } return result; } inline PRBool ListContainsTag(const eHTMLTags* aTagList,eHTMLTags aTag) { if(aTagList) { const eHTMLTags *theNextTag=aTagList; while(eHTMLTag_unknown!=*theNextTag) { if(aTag==*theNextTag) { return PR_TRUE; } ++theNextTag; } } return PR_FALSE; } /********************************************************** Begin with the baseclass for all elements... **********************************************************/ class CElement { public: //break this struct out separately so that lame compilers don't gack. struct CFlags { PRUint32 mOmitEndTag:1; PRUint32 mIsContainer:1; PRUint32 mIsSinkContainer:1; PRUint32 mDeprecated:1; PRUint32 mOmitWS:1; }; union { PRUint32 mAllBits; CFlags mProperties; }; CElement(eHTMLTags aTag=eHTMLTag_unknown) { mAllBits=0; mTag=aTag; mGroup.mAllBits=0; mContainsGroups.mAllBits=0; mAutoClose=mIncludeKids=mExcludeKids=0; mDelegate=eHTMLTag_unknown; } CElement( eHTMLTags aTag,CGroupMembers& aGroup) { mAllBits=0; mTag=aTag; mGroup=aGroup; mContainsGroups.mAllBits=0; mAutoClose=mIncludeKids=mExcludeKids=0; mDelegate=eHTMLTag_unknown; } static CGroupMembers& GetEmptyGroup(void) { static CGroupMembers theGroup={0}; return theGroup; } static void Initialize(CElement& anElement,eHTMLTags aTag){ anElement.mProperties.mIsContainer=0; anElement.mProperties.mIsSinkContainer=0; anElement.mTag=aTag; anElement.mGroup.mAllBits=0;; anElement.mContainsGroups.mAllBits=0; } static void InitializeLeaf(CElement& anElement,eHTMLTags aTag,CGroupMembers& aGroup,CGroupMembers& aContainsGroups) { anElement.mProperties.mIsContainer=PR_FALSE; anElement.mProperties.mIsSinkContainer=PR_FALSE; anElement.mTag=aTag; anElement.mGroup.mAllBits=aGroup.mAllBits; anElement.mContainsGroups.mAllBits=aContainsGroups.mAllBits; } static void Initialize(CElement& anElement,eHTMLTags aTag,CGroupMembers& aGroup,CGroupMembers& aContainsGroups) { anElement.mProperties.mIsContainer=PR_TRUE; anElement.mProperties.mIsSinkContainer=PR_TRUE; anElement.mTag=aTag; anElement.mGroup.mAllBits=aGroup.mAllBits; anElement.mContainsGroups.mAllBits=aContainsGroups.mAllBits; } inline CElement* GetDelegate(void); inline CElement* GetDefaultContainerFor(CElement* anElement); virtual PRBool CanContain(CElement* anElement,nsDTDContext* aContext); virtual PRInt32 FindAutoCloseIndexForStartTag(CElement* anElement,PRInt32 aParentIndex,nsDTDContext* aContext); virtual PRBool CanBeClosedByEndTag(CElement* anElement,nsDTDContext* aContext); //This tells us whether this tag can potentially close other blocks. //That DOES NOT mean that this tag is necessarily a block itself (condsider TBODY,TR,TD...) virtual PRBool IsBlockCloser(void) { PRBool result=IsBlockElement(eHTMLTag_body); if(!result) { if(IsInlineElement(eHTMLTag_body) || mGroup.mBits.mHead || mGroup.mBits.mHeadMisc || mGroup.mBits.mFormControl || mGroup.mBits.mFrame || mGroup.mBits.mLeaf || mGroup.mBits.mComment || mGroup.mBits.mTextContainer || mGroup.mBits.mWhiteSpace) result=PR_FALSE; else result=PR_TRUE; } return result; } //this tells us whether this tag is a block tag within the given parent virtual PRBool IsBlockElement(eHTMLTags aParentID); //this tells us whether this tag is an inline tag within the given parent //NOTE: aParentID is currently ignored, but shouldn't be. virtual PRBool IsInlineElement(eHTMLTags aParentID); //this tells us whether the tag is a container as defined by HTML //NOTE: aParentID is currently ignored, but shouldn't be. virtual PRBool IsContainer(void) {return mProperties.mIsContainer; } //this tells us whether the tag should be opened as a container in the sink (script doesn't, for example). virtual PRBool IsSinkContainer(void) { return mProperties.mIsSinkContainer; } virtual eHTMLTags GetSkipTarget(void) {return eHTMLTag_unknown;} virtual nsresult WillHandleStartToken( CElement* anElement, nsIParserNode* aNode, eHTMLTags aTag, nsDTDContext* aContext, nsIHTMLContentSink* aSink); virtual nsresult HandleStartToken( nsCParserNode* aNode, eHTMLTags aTag, nsDTDContext* aContext, nsIHTMLContentSink* aSink); virtual nsresult HandleEndToken(nsCParserNode* aNode,eHTMLTags aTag,nsDTDContext* aContext,nsIHTMLContentSink* aSink); virtual nsresult HandleMisplacedStartToken(nsCParserNode* aNode,eHTMLTags aTag,nsDTDContext* aContext,nsIHTMLContentSink* aSink) { nsresult result=NS_OK; return result; } virtual PRInt32 FindAutoCloseTargetForEndTag(nsCParserNode* aNode,eHTMLTags aTag,nsDTDContext* aContext,nsIHTMLContentSink* aSink, PRInt32& anIndex) { PRInt32 result=-1; if(mTag!=aTag) { if(HasOptionalEndTag(mTag) && (0TagAt(--anIndex); CElement *theGrandParent=GetElement(theGrandParentTag); if(theGrandParent) { result=theGrandParent->FindAutoCloseTargetForEndTag(aNode,aTag,aContext,aSink,anIndex); //give the parent a chance... } } } else result=anIndex; return result; } virtual nsresult HandleMisplacedEndToken(nsIParserNode* aNode,eHTMLTags aTag,nsDTDContext* aContext,nsIHTMLContentSink* aSink) { nsresult result=NS_OK; return result; } nsresult AutoGenerateStructure(eHTMLTags *aTagList,nsDTDContext* aContext,nsIHTMLContentSink* aSink) { nsresult result=NS_OK; CStartToken theToken(*aTagList); nsCParserNode theNode(&theToken, 0 /*stack token*/); result=OpenContainer(&theNode,*aTagList,aContext,aSink); if(eHTMLTag_unknown!=*(aTagList+1)) { AutoGenerateStructure(++aTagList,aContext,aSink); } CEndToken theEndToken(*aTagList--); nsCParserNode theEndNode(&theEndToken, 0 /*stack token*/); result=CloseContainer(&theEndNode,*aTagList,aContext,aSink); return result; } /********************************************************** Call this for each element as it get's opened on the stack **********************************************************/ virtual nsresult NotifyOpen(nsIParserNode* aNode,eHTMLTags aTag,nsDTDContext* aContext,nsIHTMLContentSink* aSink) { return NS_OK; } /********************************************************** Call this for each element as it get's closed **********************************************************/ virtual nsresult NotifyClose(nsIParserNode* aNode,eHTMLTags aTag,nsDTDContext* aContext,nsIHTMLContentSink* aSink) { return NS_OK; } /********************************************************** this gets called after each tag is opened in the given context **********************************************************/ virtual nsresult OpenContainer(nsIParserNode *aNode,eHTMLTags aTag,nsDTDContext *aContext,nsIHTMLContentSink *aSink) { return aSink->OpenContainer(*aNode); } /********************************************************** this gets called after each tag is opened in the given context **********************************************************/ virtual nsresult OpenContext(nsCParserNode *aNode,eHTMLTags aTag,nsDTDContext *aContext,nsIHTMLContentSink *aSink) { aContext->Push(aNode, 0, PR_FALSE); CElement *theElement = (aTag == mTag) ? this : GetElement(aTag); theElement->NotifyOpen(aNode, aTag, aContext,aSink); return NS_OK; } /********************************************************** this gets called after each tag is opened in the given context **********************************************************/ virtual nsresult OpenContainerInContext(nsCParserNode *aNode,eHTMLTags aTag,nsDTDContext *aContext,nsIHTMLContentSink *aSink) { OpenContext(aNode,aTag,aContext,aSink); return OpenContainer(aNode,aTag,aContext,aSink); } /********************************************************** this gets called to close a given tag in the sink **********************************************************/ virtual nsresult CloseContainer(nsIParserNode *aNode,eHTMLTags aTag,nsDTDContext *aContext,nsIHTMLContentSink *aSink) { return aSink->CloseContainer(aTag); } /********************************************************** this gets called to close a tag in the given context **********************************************************/ virtual nsresult CloseContext(nsCParserNode *aNode,eHTMLTags aTag,nsDTDContext *aContext,nsIHTMLContentSink *aSink) { nsresult result=NS_OK; nsEntryStack *theStack=0; nsCParserNode *theNode=aContext->Pop(theStack); CElement *theElement=(aTag==mTag) ? this : GetElement(aTag); result=theElement->NotifyClose(theNode,aTag,aContext,aSink); IF_FREE(aNode, aContext->mNodeAllocator); return result; } /********************************************************** this gets called to close a tag in the sink and in the context **********************************************************/ virtual nsresult CloseContainerInContext(nsCParserNode *aNode,eHTMLTags aTag,nsDTDContext *aContext,nsIHTMLContentSink *aSink) { nsresult result=NS_OK; if(mTag!=aTag) { CElement *theElement=GetElement(aTag); return theElement->CloseContainerInContext(aNode,aTag,aContext,aSink); } result=CloseContainer(aNode,aTag,aContext,aSink); CloseContext(aNode,aTag,aContext,aSink); return result; } CElement* GetElement(eHTMLTags aTag); eHTMLTags mTag; eHTMLTags mDelegate; CGroupMembers mGroup; CGroupMembers mContainsGroups; const eHTMLTags *mIncludeKids; const eHTMLTags *mExcludeKids; const eHTMLTags *mAutoClose; //other start tags that close this container }; /********************************************************** This defines the Special element group **********************************************************/ class CLeafElement: public CElement { public: static CGroupMembers& GetGroup(void) { static CGroupMembers theGroup={0}; theGroup.mBits.mLeaf=1; return theGroup; } static CGroupMembers& GetContainedGroups(void) { static CGroupMembers theGroups={0}; return theGroups; } static void Initialize(CElement& anElement,eHTMLTags aTag){ CElement::InitializeLeaf(anElement,aTag,GetGroup(),GetContainedGroups()); } CLeafElement(eHTMLTags aTag) : CElement(aTag) { mProperties.mIsContainer=0; } }; /********************************************************** This defines elements that are deprecated **********************************************************/ class CDeprecatedElement: public CElement { public: static void Initialize(CElement& anElement,eHTMLTags aTag){ CElement::Initialize(anElement,aTag); anElement.mProperties.mDeprecated=1; } CDeprecatedElement(eHTMLTags aTag) : CElement(aTag) { CDeprecatedElement::Initialize(*this,aTag); } }; /********************************************************** This defines elements that are for use only by the DTD **********************************************************/ class CInlineElement: public CElement { public: static CGroupMembers& GetGroup(void) { static CGroupMembers theGroup={0}; theGroup.mBits.mInlineEntity=1; return theGroup; } static CGroupMembers& GetContainedGroups(void) { static CGroupMembers theGroup={0}; static PRBool initialized=PR_FALSE; if(!initialized) { initialized=PR_TRUE; theGroup.mBits.mFormControl=1; theGroup.mBits.mFontStyle =1; theGroup.mBits.mPhrase=1; theGroup.mBits.mSpecial=1; theGroup.mBits.mList=0; //intentionally remove list from inline group theGroup.mBits.mPreformatted=0; theGroup.mBits.mSelf=1; theGroup.mBits.mLeaf=1; theGroup.mBits.mWhiteSpace=1; theGroup.mBits.mComment=1; theGroup.mBits.mInlineEntity=1; } return theGroup; } static void Initialize(CElement& anElement,eHTMLTags aTag){ CElement::Initialize(anElement,aTag,GetGroup(),GetContainedGroups()); } CInlineElement(eHTMLTags aTag) : CElement(aTag) { CInlineElement::Initialize(*this,aTag); } }; /********************************************************** This defines the Block element group **********************************************************/ class CBlockElement : public CElement { public: static CGroupMembers& GetGroup(void) { static CGroupMembers theBlockGroup={0}; theBlockGroup.mBits.mBlock=1; return theBlockGroup; } /********************************************************** by default,members of the block group contain inline children **********************************************************/ static CGroupMembers& GetContainedGroups(PRBool aCanContainSelf = PR_TRUE) { static CGroupMembers theGroups=CInlineElement::GetContainedGroups(); theGroups.mBits.mSelf=aCanContainSelf; return theGroups; } /********************************************************** call this if you want a group that contains only block elements... **********************************************************/ static CGroupMembers& GetBlockGroupMembers(void) { static CGroupMembers theGroups={0}; theGroups.mBits.mBlock=1; theGroups.mBits.mSelf=1; return theGroups; } static void Initialize(CElement& anElement,eHTMLTags aTag){ CElement::Initialize(anElement,aTag,GetGroup(),GetContainedGroups()); } CBlockElement(eHTMLTags aTag) : CElement(aTag) { CBlockElement::Initialize(*this,aTag); } }; /************************************************************ This defines flowEntity elements that contain block+inline ************************************************************/ class CFlowElement: public CInlineElement { public: static CGroupMembers& GetGroup(void) { static CGroupMembers theGroup={0}; theGroup.mBits.mFlowEntity=1; return theGroup; } static CGroupMembers& GetContainedGroups(void) { static CGroupMembers theGroup={0}; theGroup=CInlineElement::GetContainedGroups(); theGroup.mBits.mBlock=1; theGroup.mBits.mBlockEntity=1; return theGroup; } static void Initialize(CElement& anElement,eHTMLTags aTag){ CElement::Initialize(anElement,aTag,GetGroup(),GetContainedGroups()); } CFlowElement(eHTMLTags aTag) : CInlineElement(aTag) { CFlowElement::Initialize(*this,aTag); } }; /********************************************************** This defines the Phrase element group **********************************************************/ class CPhraseElement: public CElement { public: static CGroupMembers& GetGroup(void) { static CGroupMembers thePhraseGroup={0}; thePhraseGroup.mBits.mPhrase=1; return thePhraseGroup; } static CGroupMembers& GetContainedGroups(void) { static CGroupMembers theGroups=CInlineElement::GetContainedGroups(); return theGroups; } static void Initialize(CElement& anElement,eHTMLTags aTag){ CElement::Initialize(anElement,aTag,GetGroup(),GetContainedGroups()); } CPhraseElement(eHTMLTags aTag) : CElement(aTag) { CPhraseElement::Initialize(*this,aTag); } }; /********************************************************** This defines the formcontrol element group **********************************************************/ class CFormControlElement: public CElement { public: static CGroupMembers& GetGroup(void) { static CGroupMembers theGroup={0}; theGroup.mBits.mFormControl=1; return theGroup; } static CGroupMembers& GetContainedGroups(void) { static CGroupMembers theGroup={0}; theGroup.mBits.mFormControl=1; theGroup.mBits.mLeaf=1; theGroup.mBits.mWhiteSpace=1; return theGroup; } static void Initialize(CElement& anElement,eHTMLTags aTag){ CElement::Initialize(anElement,aTag,GetGroup(),GetContainedGroups()); } CFormControlElement(eHTMLTags aTag) : CElement(aTag) { CFormControlElement::Initialize(*this,aTag); } }; /********************************************************** This defines the form element itself **********************************************************/ class CFormElement: public CBlockElement { public: static void Initialize(CElement& anElement,eHTMLTags aTag){ CElement::Initialize(anElement,aTag,CBlockElement::GetGroup(),CBlockElement::GetBlockGroupMembers()); } CFormElement() : CBlockElement(eHTMLTag_form) { CFormElement::Initialize(*this,eHTMLTag_form); mContainsGroups.mBits.mSelf=0; mContainsGroups.mBits.mFormControl=1; } virtual PRBool CanContain(CElement* anElement,nsDTDContext* aContext) { PRBool result=CElement::CanContain(anElement,aContext); if((!result) && (aContext->mFlags.mTransitional)) { //If we're in transitional mode, then also allow inline elements... CGroupMembers& theFlowGroup=CFlowElement::GetContainedGroups(); result=ContainsGroup(theFlowGroup,anElement->mGroup); } return result; } /********************************************************** this gets called after each tag is opened in the given context **********************************************************/ virtual nsresult OpenContainer(nsIParserNode *aNode,eHTMLTags aTag,nsDTDContext *aContext,nsIHTMLContentSink *aSink) { nsresult result=aSink->OpenForm(*aNode); return result; } }; /********************************************************** This defines the fontstyle element group **********************************************************/ class CFontStyleElement: public CElement { public: static CGroupMembers& GetGroup(void) { static CGroupMembers theGroup={0}; theGroup.mBits.mFontStyle=1; return theGroup; } static CGroupMembers& GetContainedGroups(void) { static CGroupMembers theGroups=CInlineElement::GetContainedGroups(); return theGroups; } static void Initialize(CElement& anElement,eHTMLTags aTag){ CElement::Initialize(anElement,aTag,GetGroup(),GetContainedGroups()); } CFontStyleElement(eHTMLTags aTag) : CElement(aTag) { CFontStyleElement::Initialize(*this,aTag); } }; /********************************************************** This defines the special-inline element group **********************************************************/ class CSpecialElement : public CElement { public: static CGroupMembers& GetGroup(void) { static CGroupMembers theGroup={0}; theGroup.mBits.mSpecial=1; return theGroup; } static CGroupMembers& GetContainedGroups(void) { static CGroupMembers theGroups=CInlineElement::GetContainedGroups(); return theGroups; } static void Initialize(CElement& anElement,eHTMLTags aTag){ CElement::Initialize(anElement,aTag,GetGroup(),GetContainedGroups()); } CSpecialElement(eHTMLTags aTag) : CElement(aTag) { CSpecialElement::Initialize(*this,aTag); } }; /********************************************************** This defines the Table block itself, not it's children. **********************************************************/ class CTableElement: public CElement { public: static CGroupMembers& GetGroup(void) { static CGroupMembers theTableGroup={0}; theTableGroup.mBits.mTable=1; return theTableGroup; } static CGroupMembers& GetContainedGroups(void) { static CGroupMembers theGroups={0}; theGroups.mBits.mTable=1; return theGroups; } static void Initialize(CElement& anElement,eHTMLTags aTag){ CElement::Initialize(anElement,aTag,GetGroup(),GetContainedGroups()); } CTableElement(eHTMLTags aTag=eHTMLTag_table) : CElement(aTag) { CElement::Initialize(*this,aTag,CBlockElement::GetGroup(),CTableElement::GetContainedGroups()); } PRBool CanContain(CElement* anElement,nsDTDContext* aContext) { PRBool result=PR_FALSE; switch(anElement->mTag) { case eHTMLTag_caption: result=(aContext->mTableStates && aContext->mTableStates->CanOpenCaption()); break; case eHTMLTag_colgroup: result=(aContext->mTableStates && aContext->mTableStates->CanOpenCols()); break; case eHTMLTag_thead: //nothing to do for these empty tags... result=(aContext->mTableStates && aContext->mTableStates->CanOpenTHead()); break; case eHTMLTag_tfoot: result=(aContext->mTableStates && aContext->mTableStates->CanOpenTFoot()); break; case eHTMLTag_tr: case eHTMLTag_th: result=(aContext->mTableStates && aContext->mTableStates->CanOpenTBody()); break; default: result=CElement::CanContain(anElement,aContext); break; } return result; } /********************************************************** Table needs to be notified so it can manage table states. **********************************************************/ virtual nsresult NotifyOpen(nsIParserNode* aNode,eHTMLTags aTag,nsDTDContext* aContext,nsIHTMLContentSink* aSink) { aContext->mTableStates=new CTableState(aContext->mTableStates); //create and prepend a new state return NS_OK; } /********************************************************** Table needs to be notified so it can manage table states. **********************************************************/ virtual nsresult NotifyClose(nsIParserNode* aNode,eHTMLTags aTag,nsDTDContext* aContext,nsIHTMLContentSink* aSink) { nsresult result=NS_OK; if(aContext->mTableStates) { if(!aContext->mTableStates->mHasTBody) { //so let's open a tbody, a TR and a TD for good measure... eHTMLTags theTags[]={eHTMLTag_tbody,eHTMLTag_tr,eHTMLTag_td,eHTMLTag_unknown}; AutoGenerateStructure(theTags,aContext,aSink); } //pop the current state and restore it's predecessor, if any... CTableState *theState=aContext->mTableStates; aContext->mTableStates=theState->mPrevious; delete theState; } return result; } /********************************************************** Table handles the opening of it's own children **********************************************************/ virtual nsresult HandleStartToken(nsCParserNode* aNode,eHTMLTags aTag,nsDTDContext* aContext,nsIHTMLContentSink* aSink) { nsresult result=NS_OK; switch(aTag) { case eHTMLTag_caption: if(aContext->mTableStates && aContext->mTableStates->CanOpenCaption()) { result=OpenContainerInContext(aNode,aTag,aContext,aSink); //force the title onto the stack } break; case eHTMLTag_col: result=aSink->AddLeaf(*aNode); break; case eHTMLTag_colgroup: if(aContext->mTableStates && aContext->mTableStates->CanOpenCols()) { result=OpenContainerInContext(aNode,aTag,aContext,aSink); //force the title onto the stack } break; case eHTMLTag_thead: //nothing to do for these empty tags... if(aContext->mTableStates && aContext->mTableStates->CanOpenTHead()) { aContext->mTableStates->mHasTHead=PR_TRUE; result=OpenContainerInContext(aNode,aTag,aContext,aSink); //force the title onto the stack } break; case eHTMLTag_tbody: aContext->mTableStates->mHasTBody=PR_TRUE; result=OpenContainerInContext(aNode,aTag,aContext,aSink); //force the title onto the stack break; case eHTMLTag_tfoot: if(aContext->mTableStates && aContext->mTableStates->CanOpenTFoot()) { aContext->mTableStates->mHasTFoot=PR_TRUE; result=OpenContainerInContext(aNode,aTag,aContext,aSink); //force the title onto the stack } break; case eHTMLTag_tr: case eHTMLTag_th: if(aContext->mTableStates) { if(aContext->mTableStates->CanOpenTBody()) { CToken* theToken=(CStartToken*)aContext->mTokenAllocator->CreateTokenOfType(eToken_start,eHTMLTag_tbody); nsCParserNode* theNode=aContext->mNodeAllocator->CreateNode(theToken, 0); result=HandleStartToken(theNode,eHTMLTag_tbody,aContext,aSink); } if(NS_SUCCEEDED(result)) { CElement *theElement=GetElement(eHTMLTag_tbody); if(theElement) { result=theElement->HandleStartToken(aNode,aTag,aContext,aSink); } } } break; default: break; } return result; } /********************************************************** Table handles the closing of it's own children **********************************************************/ virtual nsresult HandleEndToken(nsCParserNode* aNode,eHTMLTags aTag,nsDTDContext* aContext,nsIHTMLContentSink* aSink) { nsresult result=NS_OK; if(aContext->HasOpenContainer(aTag)) { switch(aTag) { case eHTMLTag_caption: case eHTMLTag_col: case eHTMLTag_colgroup: case eHTMLTag_tr: case eHTMLTag_thead: case eHTMLTag_tfoot: case eHTMLTag_tbody: result=CloseContainerInContext(aNode,aTag,aContext,aSink); //force the title onto the stack break; default: break; } //switch } //if return result; } /********************************************************** If you're here, then children below you have optional end tags, can't deal with the given tag, and want you to handle it. **********************************************************/ virtual PRInt32 FindAutoCloseTargetForEndTag(nsIParserNode* aNode,eHTMLTags aTag,nsDTDContext* aContext,nsIHTMLContentSink* aSink, PRInt32& anIndex) { // XXXldb This method is completely unused because the |aNode| // parameter is declared as |nsIParserNode| rather than // |nsCParserNode| so it doesn't override the member function of // CElement. NS_NOTREACHED("This isn't used. Should it be?"); PRInt32 result=kNotFound; switch(aTag) { case eHTMLTag_table: case eHTMLTag_caption: case eHTMLTag_col: case eHTMLTag_colgroup: case eHTMLTag_thead: case eHTMLTag_tfoot: case eHTMLTag_tbody: case eHTMLTag_tr: case eHTMLTag_td: { PRInt32 theTablePos=aContext->LastOf(eHTMLTag_table); PRInt32 theTagPos=aContext->LastOf(aTag); if((kNotFound!=theTagPos) && (theTablePos<=theTagPos)) { result=theTagPos; } } break; default: break; } //switch return result; } }; /********************************************************** This defines the Table block itself, not it's children. **********************************************************/ class CTableRowElement: public CElement { public: static void Initialize(CElement& anElement,eHTMLTags aTag){ CElement::Initialize(anElement,aTag,CTableElement::GetGroup(),CElement::GetEmptyGroup()); static eHTMLTags kTRKids[]={eHTMLTag_td,eHTMLTag_th,eHTMLTag_unknown}; anElement.mIncludeKids=kTRKids; } CTableRowElement(eHTMLTags aTag=eHTMLTag_tr) : CElement(aTag) { CTableRowElement::Initialize(*this,aTag); mContainsGroups.mBits.mSelf=0; } virtual nsresult HandleEndTokenForChild(CElement *aChild,nsIParserNode* aNode,eHTMLTags aTag,nsDTDContext* aContext,nsIHTMLContentSink* aSink) { nsresult result=NS_OK; return result; } }; /********************************************************** This defines the List element group (ol,ul,dir,menu) **********************************************************/ class CListElement: public CElement { public: static CGroupMembers& GetGroup(void) { static CGroupMembers theListGroup={0}; theListGroup.mBits.mList=1; return theListGroup; } static CGroupMembers& GetContainedGroups(void) { static CGroupMembers theGroups=CInlineElement::GetContainedGroups(); return theGroups; } static void Initialize(CElement& anElement,eHTMLTags aTag){ CElement::Initialize(anElement,aTag,GetGroup(),GetContainedGroups()); } CListElement(eHTMLTags aTag) : CElement(aTag) { CListElement::Initialize(*this,aTag); } }; /********************************************************** This defines the LI element... An interesting problem here is that LI normally contains Block+inline, unless it's inside a MENU or DIR, in which case it contains only inline. **********************************************************/ class CLIElement: public CElement { public: CLIElement(eHTMLTags aTag=eHTMLTag_li) : CElement(aTag) { CFlowElement::Initialize(*this,aTag); mGroup.mAllBits=0; mGroup.mBits.mList=1; } }; /********************************************************** This defines the counter element, and is for debug use. Usage: if you leave off the name key/value pair, we'll use the name of the element instead. **********************************************************/ class CCounterElement: public CInlineElement { public: CCounterElement(eHTMLTags aTag=eHTMLTag_counter) : CInlineElement(aTag) { CInlineElement::Initialize(*this,aTag); mProperties.mIsSinkContainer=PR_FALSE; } /********************************************************** handles the opening of it's own children **********************************************************/ virtual nsresult HandleStartToken( nsCParserNode* aNode, eHTMLTags aTag, nsDTDContext* aContext, nsIHTMLContentSink* aSink) { return CElement::HandleStartToken(aNode,aTag,aContext,aSink); } /********************************************************** this gets called after each tag is opened in the given context **********************************************************/ virtual nsresult OpenContext(nsCParserNode *aNode,eHTMLTags aTag,nsDTDContext *aContext,nsIHTMLContentSink *aSink) { CElement::OpenContext(aNode,aTag,aContext,aSink); nsresult result=NS_OK; PRInt32 theCount=aContext->GetCount(); nsCParserNode *theNode = (nsCParserNode*)aNode; #ifdef DEBUG eHTMLTags theGrandParentTag=aContext->TagAt(theCount-2); nsAutoString theNumber; aContext->IncrementCounter(theGrandParentTag,*theNode,theNumber); CTextToken theToken(theNumber); nsCParserNode theNewNode(&theToken, 0 /*stack token*/); *theNode = theNewNode; #endif result=aSink->AddLeaf(*theNode); return result; } /********************************************************** handles the opening of it's own children **********************************************************/ virtual nsresult HandleEndToken(nsCParserNode* aNode,eHTMLTags aTag,nsDTDContext* aContext,nsIHTMLContentSink* aSink) { return CElement::HandleEndToken(aNode,aTag,aContext,aSink); } }; /********************************************************** This defines the heading element group (h1..h6) **********************************************************/ class CHeadingElement: public CElement { public: static CGroupMembers& GetGroup(void) { static CGroupMembers theGroup={0}; theGroup.mBits.mHeading=1; return theGroup; } static CGroupMembers& GetContainedGroups(void) { static CGroupMembers theGroups=CInlineElement::GetContainedGroups(); return theGroups; } static void Initialize(CElement& anElement,eHTMLTags aTag){ CElement::Initialize(anElement,aTag,GetGroup(),GetContainedGroups()); } CHeadingElement(eHTMLTags aTag) : CElement(aTag) { CHeadingElement::Initialize(*this,aTag); } }; /********************************************************** This defines the tags that relate to frames **********************************************************/ class CFrameElement: public CElement { public: static CGroupMembers& GetGroup(void) { static CGroupMembers theGroup={0}; theGroup.mBits.mFrame=1; return theGroup; } static void Initialize(CElement& anElement,eHTMLTags aTag){ anElement.mProperties.mIsContainer=1; anElement.mProperties.mIsSinkContainer=1; anElement.mTag=aTag; anElement.mGroup.mAllBits=0; anElement.mGroup.mBits.mFrame=1; anElement.mContainsGroups.mAllBits=0; anElement.mContainsGroups.mBits.mFrame=1; anElement.mContainsGroups.mBits.mSelf=1; } CFrameElement(eHTMLTags aTag) : CElement(aTag) { CFrameElement::Initialize(*this,aTag); } }; /********************************************************** This defines elements that are for use only by the DTD **********************************************************/ class CDTDInternalElement: public CElement { public: static void Initialize(CElement& anElement,eHTMLTags aTag){ anElement.mProperties.mIsContainer=1; anElement.mTag=aTag; anElement.mContainsGroups.mAllBits=0; anElement.mGroup.mBits.mDTDInternal=1; } CDTDInternalElement(eHTMLTags aTag) : CElement(aTag) { CDTDInternalElement::Initialize(*this,aTag); } }; /********************************************************** Here comes the head element **********************************************************/ class CHeadElement: public CElement { public: static CGroupMembers& GetGroup(void) { static CGroupMembers theHeadGroup={0}; theHeadGroup.mBits.mTopLevel=1; return theHeadGroup; } static CGroupMembers& GetContentGroup(void) { static CGroupMembers theGroup={0}; theGroup.mBits.mHeadContent=1; return theGroup; } static CGroupMembers& GetMiscGroup(void) { static CGroupMembers theGroup={0}; theGroup.mBits.mHeadMisc=1; return theGroup; } static CGroupMembers& GetContainedGroups(void) { static CGroupMembers theGroupsContainedByHead={0}; theGroupsContainedByHead.mBits.mHeadMisc=1; theGroupsContainedByHead.mBits.mHeadContent=1; return theGroupsContainedByHead; } static void Initialize(CElement& anElement,eHTMLTags aTag){ CElement::Initialize(anElement,aTag,GetGroup(),GetContainedGroups()); static eHTMLTags kHeadKids[]={eHTMLTag_isindex,eHTMLTag_unknown}; anElement.mIncludeKids=kHeadKids; } virtual nsresult OpenContext(nsIParserNode* aNode,eHTMLTags aTag,nsDTDContext* aContext,nsIHTMLContentSink* aSink) { // XXXldb This method is completely unused because the |aNode| // parameter is declared as |nsIParserNode| rather than // |nsCParserNode| so it doesn't override the member function of // CElement. NS_NOTREACHED("This isn't used. Should it be?"); NS_ASSERTION(aContext!=nsnull,"cannot make a decision without a context"); nsresult result=NS_OK; if(aSink && aContext) { if(aContext->mFlags.mHasOpenHead==PR_FALSE) { result=aSink->OpenHead(*aNode); aContext->mFlags.mHasOpenHead=PR_TRUE; } } return result; } virtual nsresult CloseContext(nsIParserNode* aNode,eHTMLTags aTag,nsDTDContext* aContext,nsIHTMLContentSink* aSink) { // XXXldb This method is completely unused because the |aNode| // parameter is declared as |nsIParserNode| rather than // |nsCParserNode| so it doesn't override the member function of // CElement. NS_NOTREACHED("This isn't used. Should it be?"); NS_ASSERTION(aContext!=nsnull,"cannot make a decision without a context"); nsresult result=NS_OK; if(aSink && aContext) { if(aContext->mFlags.mHasOpenHead==PR_TRUE) { result = aSink->CloseHead(); aContext->mFlags.mHasOpenHead=PR_FALSE; } } return result; } CHeadElement(eHTMLTags aTag) : CElement(aTag) { CHeadElement::Initialize(*this,aTag); } }; /********************************************************** This class is for use with title, script, style **********************************************************/ class CTextContainer : public CElement { public: static CGroupMembers& GetGroup(void) { static CGroupMembers theGroup={0}; theGroup.mBits.mTextContainer=1; return theGroup; } static CGroupMembers& GetContainedGroups(void) { static CGroupMembers theContainedGroups={0}; theContainedGroups.mBits.mLeaf=1; return theContainedGroups; } static void Initialize(CElement& anElement,eHTMLTags aTag){ CElement::Initialize(anElement,aTag,GetGroup(),GetContainedGroups()); } CTextContainer(eHTMLTags aTag) : CElement(aTag) { CTextContainer::Initialize(*this,aTag); } virtual ~CTextContainer() { } /********************************************************** Call this for each element as it get's closed **********************************************************/ virtual nsresult NotifyClose(nsIParserNode* aNode,eHTMLTags aTag,nsDTDContext* aContext,nsIHTMLContentSink* aSink) { nsresult result=NS_OK; if(aNode) { #if 0 CStartToken theToken(aTag); nsCParserNode theNode(&theToken); theNode.SetSkippedContent(mText); result=aSink->AddLeaf(theNode); #endif nsCParserNode *theNode=(nsCParserNode*)aNode; //theNode->SetSkippedContent(mText); XXX why do we need this? result=aSink->AddLeaf(*theNode); } mText.Truncate(0); return result; } /********************************************************** Textcontainer handles the opening of it's own children **********************************************************/ virtual nsresult HandleStartToken(nsIParserNode* aNode,eHTMLTags aTag,nsDTDContext* aContext,nsIHTMLContentSink* aSink) { // XXXldb This method is completely unused because the |aNode| // parameter is declared as |nsIParserNode| rather than // |nsCParserNode| so it doesn't override the member function of // CElement. NS_NOTREACHED("This isn't used. Should it be?"); nsresult result=NS_OK; switch(aTag) { case eHTMLTag_text: case eHTMLTag_whitespace: mText.Append(aNode->GetText()); break; default: break; } return result; } virtual nsresult HandleEndToken(nsIParserNode* aNode,eHTMLTags aTag,nsDTDContext* aContext,nsIHTMLContentSink* aSink) { // XXXldb This method is completely unused because the |aNode| // parameter is declared as |nsIParserNode| rather than // |nsCParserNode| so it doesn't override the member function of // CElement. NS_NOTREACHED("This isn't used. Should it be?"); nsresult result=NS_OK; return result; } nsString mText; }; /********************************************************** This class is for the title element **********************************************************/ class CTitleElement : public CTextContainer { public: static void Initialize(CElement& anElement,eHTMLTags aTag){ CTextContainer::Initialize(anElement,aTag); } CTitleElement() : CTextContainer(eHTMLTag_title) { mGroup.mBits.mHeadMisc=1; } /********************************************************** Call this for each element as it get's closed **********************************************************/ virtual nsresult NotifyClose(nsCParserNode* aNode,eHTMLTags aTag,nsDTDContext* aContext,nsIHTMLContentSink* aSink) { // XXXldb This method is completely unused because the |aNode| // parameter is declared as |nsCParserNode| rather than // |nsIParserNode| so it doesn't override the member function of // CTextContainer. NS_NOTREACHED("This isn't used. Should it be?"); nsresult result=NS_OK; CElement* theHead=GetElement(eHTMLTag_head); if(theHead) { result=theHead->OpenContext(aNode,aTag,aContext,aSink); if(NS_SUCCEEDED(result)) { result=aSink->SetTitle(mText); mText.Truncate(0); if(NS_SUCCEEDED(result)) { result=theHead->CloseContext(aNode,aTag,aContext,aSink); } } } return result; } /********************************************************** Title handles the opening of it's own children **********************************************************/ virtual nsresult HandleStartToken(nsIParserNode* aNode,eHTMLTags aTag,nsDTDContext* aContext,nsIHTMLContentSink* aSink) { nsresult result=NS_OK; switch(aTag) { case eHTMLTag_text: if(aNode && aNode->GetTokenType()==eToken_entity) { nsAutoString tmp; aNode->TranslateToUnicodeStr(tmp); mText.Append(tmp); break; } case eHTMLTag_whitespace: mText.Append(aNode->GetText()); break; default: break; } return result; } }; /********************************************************** This class is for the title element **********************************************************/ class CTextAreaElement: public CTextContainer { public: CTextAreaElement() : CTextContainer(eHTMLTag_textarea) { mGroup.mBits.mHeadMisc=1; mGroup=CFormControlElement::GetGroup(); mProperties.mIsSinkContainer=0; } virtual nsresult HandleStartToken(nsIParserNode* aNode,eHTMLTags aTag,nsDTDContext* aContext,nsIHTMLContentSink* aSink) { nsresult result=NS_OK; switch(aTag) { case eHTMLTag_text: if(aNode && aNode->GetTokenType()==eToken_entity) { nsAutoString tmp; aNode->TranslateToUnicodeStr(tmp); mText.Append(tmp); break; } case eHTMLTag_whitespace: case eHTMLTag_newline: mText.Append(aNode->GetText()); break; default: break; } return result; } }; /********************************************************** This class is for use with style **********************************************************/ class CStyleElement: public CTextContainer { public: static void Initialize(CElement& anElement,eHTMLTags aTag){ CTextContainer::Initialize(anElement,aTag); } CStyleElement() : CTextContainer(eHTMLTag_style) { mGroup.mBits.mHeadMisc=1; } /********************************************************** Call this for each element as it get's closed **********************************************************/ virtual nsresult NotifyClose(nsCParserNode* aNode,eHTMLTags aTag,nsDTDContext* aContext,nsIHTMLContentSink* aSink) { // XXXldb This method is completely unused because the |aNode| // parameter is declared as |nsCParserNode| rather than // |nsIParserNode| so it doesn't override the member function of // CTextContainer. NS_NOTREACHED("This isn't used. Should it be?"); nsresult result=NS_OK; CElement* theHead=GetElement(eHTMLTag_head); if(theHead) { result=theHead->OpenContext(aNode,aTag,aContext,aSink); if(NS_SUCCEEDED(result)) { result=CTextContainer::NotifyClose(aNode,aTag,aContext,aSink); mText.Truncate(0); if(NS_SUCCEEDED(result)) { result=theHead->CloseContext(aNode,aTag,aContext,aSink); } } } return result; } }; /********************************************************** This class is for use with script **********************************************************/ class CScriptElement: public CTextContainer { public: static void Initialize(CElement& anElement,eHTMLTags aTag){ CTextContainer::Initialize(anElement,aTag); anElement.mProperties.mIsSinkContainer=PR_FALSE; } CScriptElement() : CTextContainer(eHTMLTag_script) { mGroup.mBits.mHeadMisc=1; mGroup.mBits.mInlineEntity=1; mGroup.mBits.mSpecial=1; mProperties.mIsSinkContainer=PR_FALSE; } /********************************************************** this gets called after each tag is opened in the given context **********************************************************/ virtual nsresult OpenContainerInContext(nsCParserNode *aNode,eHTMLTags aTag,nsDTDContext *aContext,nsIHTMLContentSink *aSink) { OpenContext(aNode,aTag,aContext,aSink); return NS_OK; } /********************************************************** this gets called to close a tag in the given context **********************************************************/ virtual nsresult CloseContext(nsIParserNode *aNode,eHTMLTags aTag,nsDTDContext *aContext,nsIHTMLContentSink *aSink) { // XXXldb This method is completely unused because the |aNode| // parameter is declared as |nsIParserNode| rather than // |nsCParserNode| so it doesn't override the member function of // CElement. NS_NOTREACHED("This isn't used. Should it be?"); nsEntryStack* theStack=0; nsIParserNode *theNode=aContext->Pop(theStack); CElement *theElement=(aTag==mTag) ? this : GetElement(aTag); theElement->NotifyClose(theNode,aTag,aContext,aSink); return NS_OK; } /********************************************************** Call this for each element as it get's closed **********************************************************/ virtual nsresult NotifyClose(nsCParserNode* aNode,eHTMLTags aTag,nsDTDContext* aContext,nsIHTMLContentSink* aSink) { // XXXldb This method is completely unused because the |aNode| // parameter is declared as |nsCParserNode| rather than // |nsIParserNode| so it doesn't override the member function of // CTextContainer. NS_NOTREACHED("This isn't used. Should it be?"); nsresult result=NS_OK; if(aContext->HasOpenContainer(eHTMLTag_body)) { //add the script to the body result=CTextContainer::NotifyClose(aNode,aTag,aContext,aSink); } else { //add it to the head... CElement* theHead=GetElement(eHTMLTag_head); if(theHead) { result=theHead->OpenContext(aNode,aTag,aContext,aSink); if(NS_SUCCEEDED(result)) { result=CTextContainer::NotifyClose(aNode,aTag,aContext,aSink); if(NS_SUCCEEDED(result)) { result=theHead->CloseContext(aNode,aTag,aContext,aSink); } } } } mText.Truncate(0); return result; } }; /********************************************************** This defines the preformatted element group, (PRE). **********************************************************/ class CPreformattedElement: public CBlockElement { public: static void Initialize(CElement& anElement,eHTMLTags aTag){ CBlockElement::Initialize(anElement,aTag); } CPreformattedElement(eHTMLTags aTag) : CBlockElement(aTag) { mGroup=GetGroup(); mContainsGroups=GetContainedGroups(); mProperties.mIsContainer=1; } /********************************************************** Pre handles the opening of it's own children **********************************************************/ virtual nsresult HandleStartToken(nsCParserNode* aNode,eHTMLTags aTag,nsDTDContext* aContext,nsIHTMLContentSink* aSink) { nsresult result=CElement::HandleStartToken(aNode,aTag,aContext,aSink); return result; } /********************************************************** Pre handles the closing of it's own children **********************************************************/ virtual nsresult HandleEndToken(nsCParserNode* aNode,eHTMLTags aTag,nsDTDContext* aContext,nsIHTMLContentSink* aSink) { nsresult result=CElement::HandleEndToken(aNode,aTag,aContext,aSink); return result; } }; /********************************************************** This is used for both applet and object elements **********************************************************/ class CAppletElement: public CSpecialElement { public: static CGroupMembers& GetGroup(void) { static CGroupMembers theGroup={0}; theGroup.mBits.mSpecial=1; theGroup.mBits.mBlock=1; return theGroup; } static CGroupMembers& GetContainedGroups(void) { return CFlowElement::GetContainedGroups(); } static void Initialize(CElement& anElement,eHTMLTags aTag){ CElement::Initialize(anElement,aTag,GetGroup(),GetContainedGroups()); static eHTMLTags kSpecialKids[]={eHTMLTag_param,eHTMLTag_unknown}; anElement.mIncludeKids=kSpecialKids; anElement.mProperties.mIsContainer=1; } CAppletElement(eHTMLTags aTag) : CSpecialElement(aTag) { Initialize(*this,aTag); } /********************************************************** handles the opening of it's own children **********************************************************/ virtual nsresult HandleStartToken(nsCParserNode* aNode,eHTMLTags aTag,nsDTDContext* aContext,nsIHTMLContentSink* aSink) { nsresult result=NS_OK; nsIParserNode *theNode=aContext->PeekNode(); if(theNode) { PRBool theContentsHaveArrived=theNode->GetGenericState(); switch(aTag) { case eHTMLTag_param: if(!theContentsHaveArrived) { result=CElement::HandleStartToken(aNode,aTag,aContext,aSink); } break; case eHTMLTag_newline: case eHTMLTag_whitespace: result=CElement::HandleStartToken(aNode,aTag,aContext,aSink); break; default: theNode->SetGenericState(PR_TRUE); result=CElement::HandleStartToken(aNode,aTag,aContext,aSink); break; } //switch } return result; } }; /********************************************************** This defines the fieldset element... **********************************************************/ class CFieldsetElement: public CBlockElement { public: static CGroupMembers& GetGroup(void) { static CGroupMembers theGroup={0}; theGroup.mBits.mBlock=1; return theGroup; } static CGroupMembers& GetContainedGroups(void) { return CFlowElement::GetContainedGroups(); } static void Initialize(CElement& anElement,eHTMLTags aTag){ CElement::Initialize(anElement,aTag,GetGroup(),GetContainedGroups()); } CFieldsetElement() : CBlockElement(eHTMLTag_fieldset) { mGroup=GetGroup(); mContainsGroups=GetContainedGroups(); mProperties.mIsContainer=1; } /********************************************************** fieldset handles the opening of it's own children **********************************************************/ virtual nsresult HandleStartToken(nsCParserNode* aNode,eHTMLTags aTag,nsDTDContext* aContext,nsIHTMLContentSink* aSink) { nsresult result=NS_OK; nsIParserNode *theNode=aContext->PeekNode(); if(theNode) { PRBool theLegendExists=theNode->GetGenericState(); switch(aTag) { case eHTMLTag_legend: if(!theLegendExists) { theNode->SetGenericState(PR_TRUE); result=OpenContainerInContext(aNode,aTag,aContext,aSink); //force the title onto the stack } break; default: if(theLegendExists) { result=CElement::HandleStartToken(aNode,aTag,aContext,aSink); //force the title onto the stack } break; } //switch } return result; } }; /********************************************************** This is for FRAMESET, etc. **********************************************************/ class CTopLevelElement: public CElement { public: static CGroupMembers& GetGroup(void) { static CGroupMembers theGroup={0}; theGroup.mBits.mTopLevel=1; return theGroup; } static CGroupMembers& GetContainedGroups(void) { static CGroupMembers theGroup=CFlowElement::GetContainedGroups(); return theGroup; } static void Initialize(CElement& anElement,eHTMLTags aTag){ CElement::Initialize(anElement,aTag,GetGroup(),GetContainedGroups()); } CTopLevelElement(eHTMLTags aTag) : CElement(aTag) { CTopLevelElement::Initialize(*this,aTag); } /********************************************************** Toplevel handles the opening of it's own children **********************************************************/ virtual nsresult HandleStartToken( nsCParserNode* aNode, eHTMLTags aTag, nsDTDContext* aContext, nsIHTMLContentSink* aSink) { nsresult result=NS_OK; switch(aTag) { case eHTMLTag_unknown: default: result=CElement::HandleStartToken(aNode,aTag,aContext,aSink); break; }//switch return result; } /********************************************************** TopLevel handles the opening of it's own children **********************************************************/ virtual nsresult HandleEndToken(nsCParserNode* aNode,eHTMLTags aTag,nsDTDContext* aContext,nsIHTMLContentSink* aSink) { nsresult result=NS_OK; switch(aTag) { case eHTMLTag_html: if(aContext->HasOpenContainer(aTag)) { result=aSink->CloseHTML(); CloseContext(aNode,aTag,aContext,aSink); } break; case eHTMLTag_body: if(aContext->HasOpenContainer(aTag)) { result=aSink->CloseBody(); CloseContext(aNode,aTag,aContext,aSink); } break; case eHTMLTag_frameset: if(aContext->HasOpenContainer(aTag)) { result=aSink->OpenFrameset(*aNode); CloseContext(aNode,aTag,aContext,aSink); } break; default: result=CElement::HandleEndToken(aNode,aTag,aContext,aSink); break; }//switch return result; } }; /********************************************************** This is for HTML only... **********************************************************/ class CHTMLElement: public CTopLevelElement{ public: static CGroupMembers& GetGroup(void) { static CGroupMembers theBlockGroup={0}; theBlockGroup.mBits.mTopLevel=1; return theBlockGroup; } static CGroupMembers& GetContainedGroups(void) { static CGroupMembers theGroups={0}; theGroups.mBits.mTopLevel=1; return theGroups; } CHTMLElement(eHTMLTags aTag) : CTopLevelElement(aTag) { CElement::Initialize(*this,aTag,CHTMLElement::GetGroup(),CHTMLElement::GetContainedGroups()); } /********************************************************** HTML handles the opening of it's own children **********************************************************/ nsresult HandleDoctypeDecl( nsIParserNode* aNode, eHTMLTags aTag, nsDTDContext* aContext, nsIHTMLContentSink* aSink) { nsCParserNode *theNode=(nsCParserNode*)aNode; nsresult result=NS_OK; if(theNode) { nsAutoString theStr(theNode->mToken->GetStringValue()); PRInt32 theLen=theStr.Length(); //PRInt32 thePos=theStr.RFindChar(kGreaterThan); theStr.Truncate(theLen-1); theStr.Cut(0,2); result = aSink->AddDocTypeDecl(*aNode); } return result; } /********************************************************** HTML handles the opening of it's own children **********************************************************/ virtual nsresult HandleStartToken( nsCParserNode* aNode, eHTMLTags aTag, nsDTDContext* aContext, nsIHTMLContentSink* aSink) { nsresult result=NS_OK; switch(aTag) { case eHTMLTag_doctypeDecl: result=HandleDoctypeDecl(aNode,aTag,aContext,aSink); break; case eHTMLTag_frameset: result=aSink->OpenFrameset(*aNode); result=OpenContext(aNode,aTag,aContext,aSink); aContext->mFlags.mHadFrameset=PR_TRUE; break; case eHTMLTag_base: //nothing to do for these empty tags... case eHTMLTag_isindex: case eHTMLTag_link: case eHTMLTag_meta: { CElement* theHead=GetElement(eHTMLTag_head); if(theHead) { result=theHead->OpenContext(aNode,aTag,aContext,aSink); if(NS_SUCCEEDED(result)) { result=aSink->AddLeaf(*aNode); if(NS_SUCCEEDED(result)) { result=theHead->CloseContext(aNode,aTag,aContext,aSink); } } } } break; case eHTMLTag_object: { CElement* theHead=GetElement(eHTMLTag_head); if(theHead) { result=theHead->OpenContext(aNode,aTag,aContext,aSink); if(NS_SUCCEEDED(result)) { result=OpenContainerInContext(aNode,aTag,aContext,aSink); } } } break; case eHTMLTag_script: case eHTMLTag_style: case eHTMLTag_title: result=OpenContext(aNode,aTag,aContext,aSink); //force the title onto the context stack break; case eHTMLTag_newline: case eHTMLTag_whitespace: case eHTMLTag_comment: break; default: CElement* theBody=GetElement(eHTMLTag_body); if(theBody) { CElement *theChildElement=GetElement(aTag); if(theBody->CanContain(theChildElement,aContext)) { //let's auto open the body CToken* theToken=(CStartToken*)aContext->mTokenAllocator->CreateTokenOfType(eToken_start,eHTMLTag_body); nsCParserNode* theNode=aContext->mNodeAllocator->CreateNode(theToken, 0); result=theBody->HandleStartToken(theNode,eHTMLTag_body,aContext,aSink); if(NS_SUCCEEDED(result)) { if(eHTMLTag_body==aContext->Last()) { result=theBody->HandleStartToken(aNode,aTag,aContext,aSink); } } } } //for now, let's drop other elements onto the floor. break; }//switch return result; } /********************************************************** HTML handles the closing of it's own children **********************************************************/ virtual nsresult HandleEndToken(nsCParserNode* aNode,eHTMLTags aTag,nsDTDContext* aContext,nsIHTMLContentSink* aSink) { nsresult result=NS_OK; switch(aTag) { case eHTMLTag_body: aSink->CloseBody(); result=CloseContext(aNode,aTag,aContext,aSink); break; case eHTMLTag_frameset: aSink->CloseFrameset(); result=CloseContext(aNode,aTag,aContext,aSink); break; case eHTMLTag_object: result=CloseContainerInContext(aNode,aTag,aContext,aSink); aSink->CloseHead(); break; case eHTMLTag_script: case eHTMLTag_style: case eHTMLTag_title: result=CloseContext(aNode,aTag,aContext,aSink); //close the title break; case eHTMLTag_unknown: default: result=CTopLevelElement::HandleEndToken(aNode,aTag,aContext,aSink); } return result; } }; /********************************************************** This is for the body element... **********************************************************/ static const eHTMLTags gBodyKids[] = {eHTMLTag_button, eHTMLTag_del, eHTMLTag_ins, eHTMLTag_map,eHTMLTag_script, eHTMLTag_unknown}; static const eHTMLTags gBodyExcludeKids[] = {eHTMLTag_applet, eHTMLTag_button, eHTMLTag_iframe, eHTMLTag_object, eHTMLTag_unknown}; class CBodyElement: public CElement { public: static CGroupMembers& GetGroup(void) { static CGroupMembers theGroup={0}; theGroup.mBits.mTopLevel=1; return theGroup; } CBodyElement(eHTMLTags aTag=eHTMLTag_body) : CElement(aTag) { CGroupMembers theGroups=CBlockElement::GetBlockGroupMembers(); CElement::Initialize(*this,aTag,CBodyElement::GetGroup(),theGroups); mIncludeKids=gBodyKids; mExcludeKids=gBodyExcludeKids; } virtual PRBool CanContain(CElement* anElement,nsDTDContext* aContext) { PRBool result=CElement::CanContain(anElement,aContext); if((!result) && (aContext->mFlags.mTransitional)) { //let's try so additions that are specific to the body tag, //and only work in transitional mode... CGroupMembers& theFlowGroup=CFlowElement::GetContainedGroups(); result=ContainsGroup(theFlowGroup,anElement->mGroup); } return result; } //this gets called after each tag is opened in the given context virtual nsresult OpenContainer(nsCParserNode *aNode,eHTMLTags aTag,nsDTDContext *aContext,nsIHTMLContentSink *aSink) { // XXXldb This method is completely unused because the |aNode| // parameter is declared as |nsCParserNode| rather than // |nsIParserNode| so it doesn't override the member function of // CElement. NS_NOTREACHED("This isn't used. Should it be?"); nsresult result=NS_OK; if(mTag==aTag) { // Close the head before opening a body. CElement* theHead=GetElement(eHTMLTag_head); result=theHead->CloseContext(aNode,aTag,aContext,aSink); if(NS_SUCCEEDED(result)) { result=aSink->OpenBody(*aNode); } } else result=CElement::OpenContainer(aNode,aTag,aContext,aSink); return result; } /********************************************************** this gets called after each tag is opened in the given context **********************************************************/ virtual nsresult OpenContainerInContext(nsCParserNode *aNode,eHTMLTags aTag,nsDTDContext *aContext,nsIHTMLContentSink *aSink) { NS_ASSERTION(aContext!=nsnull,"need a valid context"); nsresult result=NS_OK; // Since BODY is optional, we might come across more than one BODY!. // That is, one that's auto opened and one that came from the document itself. // If that's the case then make sure that we don't open up multiple contexts, however, // don't forget to inform the sink because it needs to account for the BODY attributes. if(aContext) { if(!aContext->mFlags.mHadBody) { result=OpenContext(aNode,aTag,aContext,aSink); aContext->mFlags.mHadBody=PR_TRUE; } } return (NS_SUCCEEDED(result))? OpenContainer(aNode,aTag,aContext,aSink):result; } /********************************************************** Body handles the opening of it's own children **********************************************************/ virtual nsresult HandleStartToken( nsCParserNode* aNode, eHTMLTags aTag, nsDTDContext* aContext, nsIHTMLContentSink* aSink) { //for now, let's drop other elements onto the floor. nsresult result=CElement::HandleStartToken(aNode,aTag,aContext,aSink); if(NS_SUCCEEDED(result)) { if(aNode) { nsCParserNode* theNode=(nsCParserNode*)aNode; eHTMLTokenTypes theType=eHTMLTokenTypes(theNode->GetTokenType()); if(theType==eToken_start) { CStartToken *theToken=(CStartToken*)theNode->mToken; if(theToken && theToken->IsEmpty() && (aTag==aContext->Last())){ result=CElement::HandleEndToken(aNode,aTag,aContext,aSink); } } } } return result; } /********************************************************** Body doesnt really need to handle it's own kids, but it's a really convenient break point for debugging purposes. **********************************************************/ virtual nsresult HandleEndToken(nsCParserNode* aNode,eHTMLTags aTag,nsDTDContext* aContext,nsIHTMLContentSink* aSink) { nsresult result=NS_OK; switch(aTag) { case eHTMLTag_script: result=CloseContext(aNode,aTag,aContext,aSink); break; default: result=CElement::HandleEndToken(aNode,aTag,aContext,aSink); } return result; } /********************************************************** Body is the default place where forwarding stops. **********************************************************/ virtual nsresult HandleEndTokenForChild(CElement *aChild,nsIParserNode* aNode,eHTMLTags aTag,nsDTDContext* aContext,nsIHTMLContentSink* aSink) { nsresult result=NS_OK; return result; } protected: }; /************************************************************************ This describes each group that each HTML element belongs to ************************************************************************/ class CElementTable { public: enum {eGroupCount=4}; CElementTable() : mBodyElement(eHTMLTag_body), mFramesetElement(eHTMLTag_frameset), mHTMLElement(eHTMLTag_html), mScriptElement(), mStyleElement(), mTitleElement(), mTextAreaElement(), mPreElement(eHTMLTag_pre), mLIElement(eHTMLTag_li), mAppletElement(eHTMLTag_applet), mObjectElement(eHTMLTag_object), mFieldsetElement(), mCounterElement(), mFormElement(), mHeadElement(eHTMLTag_head) { memset(mElements,0,sizeof(mElements)); InitializeElements(); //DebugDumpBlockElements("test"); //DebugDumpInlineElements("test"); //DebugDumpContainment("all elements"); } //call this to get a ptr to an element prototype... CElement* GetElement(eHTMLTags aTagID) { if(aTagID>eHTMLTag_unknown) { if(aTagIDmElements[aTag]; } /*********************************************************************************** This method is pretty interesting, because it's where the elements all get initialized for this elementtable. ***********************************************************************************/ void CElementTable::InitializeElements() { int max=sizeof(mElements)/sizeof(mElements[0]); int index=0; for(index=0;indexmTag); const char* prefix=" "; printf("\n\nTag: <%s>\n", NS_ConvertUCS2toUTF8(uctag).get()); printf(prefix); if(aTag->IsContainer()) { if(aTag->mContainsGroups.mBits.mHead) printf("head "); if(aTag->mContainsGroups.mBits.mHeadMisc) printf("headmisc "); if(aTag->mContainsGroups.mBits.mHeadContent) printf("headcontent "); if(aTag->mContainsGroups.mBits.mTable) printf("table "); if(aTag->mContainsGroups.mBits.mTextContainer) printf("text "); if(aTag->mContainsGroups.mBits.mTopLevel) printf("toplevel "); if(aTag->mContainsGroups.mBits.mDTDInternal) printf("internal "); if(aTag->mContainsGroups.mBits.mFlowEntity) { printf("block inline "); } else { if (aTag->mContainsGroups.mBits.mBlockEntity) { printf("blockEntity "); } if (aTag->mContainsGroups.mBits.mBlock) { printf("block "); } if(aTag->mContainsGroups.mBits.mInlineEntity) { printf("inline "); } else { if(aTag->mContainsGroups.mBits.mFontStyle ) printf("fontstyle "); if(aTag->mContainsGroups.mBits.mPhrase) printf("phrase "); if(aTag->mContainsGroups.mBits.mSpecial) printf("special "); if(aTag->mContainsGroups.mBits.mFormControl) printf("form "); if(aTag->mContainsGroups.mBits.mHeading) printf("heading "); if(aTag->mContainsGroups.mBits.mFrame) printf("frame "); if(aTag->mContainsGroups.mBits.mList) printf("list "); if(aTag->mContainsGroups.mBits.mPreformatted) printf("pre "); if(aTag->mContainsGroups.mBits.mSelf) printf("self "); if(aTag->mContainsGroups.mBits.mLeaf) printf("leaf "); if(aTag->mContainsGroups.mBits.mWhiteSpace) printf("ws "); if(aTag->mContainsGroups.mBits.mComment) printf("comment "); } } if(aTag->mIncludeKids) { printf("\n%s",prefix); const eHTMLTags *theKid=aTag->mIncludeKids; printf("+ "); while(eHTMLTag_unknown!=*theKid){ const PRUnichar *t = nsHTMLTags::GetStringValue(*theKid++); printf("%s ", NS_ConvertUCS2toUTF8(t).get()); } } if(aTag->mExcludeKids) { printf("\n%s",prefix); const eHTMLTags *theKid=aTag->mExcludeKids; printf("- "); while(eHTMLTag_unknown!=*theKid){ const PRUnichar *t = nsHTMLTags::GetStringValue(*theKid++); printf("%s ", NS_ConvertUCS2toUTF8(t).get()); } } if(!aTag->mContainsGroups.mBits.mSelf){ printf("\n%s - self",prefix); } } else { printf("empty\n"); } } void CElementTable::DebugDumpContainment(CElement* anElement){ const PRUnichar *uctag = nsHTMLTags::GetStringValue(anElement->mTag); const char* prefix=" "; printf("\n\nTag: <%s>\n", NS_ConvertUCS2toUTF8(uctag).get()); printf(prefix); int count=0; int i=0; for(i=0;iCanContain(theChild,0)){ const PRUnichar *t = nsHTMLTags::GetStringValue(theChild->mTag); printf("%s ", NS_ConvertUCS2toUTF8(t).get()); ++count; if(18==count) { count=0; printf("\n%s",prefix); } } } } void CElementTable::DebugDumpInlineElements(const char* aTitle) { PRInt32 theTagID=eHTMLTag_unknown; PRBool result=PR_FALSE; printf("Inline Elements -- %s: \n",aTitle); while(theTagID<=eHTMLTag_userdefined) { CElement *theTag=GetElement((eHTMLTags)theTagID); if(theTag) { result=theTag->IsInlineElement(eHTMLTag_unknown); if(result) { const PRUnichar *t = nsHTMLTags::GetStringValue(theTag->mTag); printf(" %s\n", NS_ConvertUCS2toUTF8(t).get()); } } theTagID++; } } void CElementTable::DebugDumpBlockElements(const char* aTitle) { PRInt32 theTagID=eHTMLTag_unknown; PRBool result=PR_FALSE; printf("Block Elements -- %s: \n",aTitle); while(theTagID<=eHTMLTag_userdefined) { CElement *theTag=GetElement((eHTMLTags)theTagID); if(theTag) { result=theTag->IsBlockElement(eHTMLTag_unknown); if(result) { const PRUnichar *theName = nsHTMLTags::GetStringValue(theTag->mTag); printf(" %s\n", NS_ConvertUCS2toUTF8(theName).get()); } } theTagID++; } } void CElementTable::DebugDumpContainment(const char* aTitle){ #if 0 DebugDumpContainment(mElements[eHTMLTag_head]); DebugDumpContainment(mElements[eHTMLTag_html]); DebugDumpContainment(mElements[eHTMLTag_table]); printf("\n"); #endif printf("==================================================\n"); printf("%s\n",aTitle); printf("==================================================\n"); int i=0; for(i=1;iTagAt(aParentIndex); if(eHTMLTag_unknown!=theParentTag) { CElement* theParent=gElementTable->mElements[theParentTag]; if(!theParent->CanContain(anElement,aContext)) { if(HasOptionalEndTag(theParentTag)) { if(ListContainsTag(theParent->mAutoClose,anElement->mTag)) { result=theParent->FindAutoCloseIndexForStartTag(anElement,aParentIndex-1,aContext); } else if((theParent->mTag==anElement->mTag) && (!theParent->mContainsGroups.mBits.mSelf)){ result=aParentIndex; } else if(eHTMLTag_body!=theParent->mTag) { result=theParent->FindAutoCloseIndexForStartTag(anElement,aParentIndex-1,aContext); } else result=aParentIndex+1; } else result=kNotFound; } else result=aParentIndex+1; } } return result; } /****************************************************************************** Yes, I know it's inconvenient to find this methods here, but it's easier for the compiler -- and making it's life easier is my top priority. ******************************************************************************/ PRBool CElement::CanBeClosedByEndTag(CElement* anElement,nsDTDContext* aContext) { PRBool result=PR_FALSE; //first, let's see if we can contain the given tag based on group info... if(anElement) { if(ListContainsTag(mAutoClose,anElement->mTag)) { return PR_TRUE; } else if((this==anElement) && (!mContainsGroups.mBits.mSelf)){ return PR_TRUE; } else { eHTMLTags theTag=aContext->Last(); CElement* theElement=gElementTable->mElements[theTag]; if(HasOptionalEndTag(theTag)) { if(anElement->CanContain(theElement,aContext)){ result=PR_TRUE; } } } } return result; } /****************************************************************************** Yes, I know it's inconvenient to find this methods here, but it's easier for the compiler -- and making it's life easier is my top priority. ******************************************************************************/ PRBool CElement::CanContain(CElement* anElement,nsDTDContext* aContext) { PRBool result=PR_FALSE; //first, let's see if we can contain the given tag based on group info... if(anElement) { if(!anElement->mProperties.mDeprecated) { if(anElement!=this) { if(ListContainsTag(mExcludeKids,anElement->mTag)) { return PR_FALSE; } else if(ContainsGroup(mContainsGroups,anElement->mGroup)) { result=PR_TRUE; } else if(ListContainsTag(mIncludeKids,anElement->mTag)) { return PR_TRUE; } } else result=mContainsGroups.mBits.mSelf; } /*************************************************** This is a (cheesy) exception table, that allows us to override containment for transitional documents. A better implementation would be to create unique classes for each of the tags in this table, and to override CanContain() there. ***************************************************/ if((!result) && (aContext->mFlags.mTransitional)) { switch(mTag) { case eHTMLTag_address: if(eHTMLTag_p==anElement->mTag) result=PR_TRUE; break; case eHTMLTag_blockquote: case eHTMLTag_form: case eHTMLTag_iframe: result=ContainsGroup(CFlowElement::GetContainedGroups(),anElement->mGroup); break; case eHTMLTag_button: if((eHTMLTag_iframe==anElement->mTag) || (eHTMLTag_isindex==anElement->mTag)) result=PR_TRUE; break; default: break; } } } return result; } nsresult CElement::WillHandleStartToken( CElement *anElement, nsIParserNode* aNode, eHTMLTags aTag, nsDTDContext* aContext, nsIHTMLContentSink* aSink) { nsresult result=NS_OK; return result; } nsresult CElement::HandleStartToken( nsCParserNode* aNode, eHTMLTags aTag, nsDTDContext* aContext, nsIHTMLContentSink* aSink) { CElement* theElement=gElementTable->mElements[aTag]; nsresult result=WillHandleStartToken(theElement,aNode,aTag,aContext,aSink); #if 0 CElement* theDelegate=theElement->GetDelegate(); if(theDelegate) { result=theDelegate->HandleStartToken(aNode,aTag,aContext,aSink); } else #endif { if(theElement) { if(CanContain(theElement,aContext)) { if(theElement->IsContainer()) { if(theElement->IsSinkContainer()) { result=theElement->OpenContainerInContext(aNode,aTag,aContext,aSink); } else { result=theElement->OpenContext(aNode,aTag,aContext,aSink); } } else { result=aSink->AddLeaf(*aNode); } } else if(theElement->IsBlockCloser()){ //Ok, so we have a start token that is misplaced. Before handing this off //to a default container (parent), let's check the autoclose condition. if(HasOptionalEndTag(mTag)) { //aha! We have a case where this tag is autoclosed by anElement. //Let's close this container, then try to open theElement. PRInt32 theCount=aContext->GetCount(); PRInt32 theIndex=FindAutoCloseIndexForStartTag(theElement,theCount-2,aContext); //continue ripping code out here... if(kNotFound!=theIndex) { eHTMLTags theParentTag=eHTMLTag_unknown; CElement* theParent=0; while(NS_SUCCEEDED(result) && (theCount>theIndex)) { theParentTag=aContext->Last(); theParent=gElementTable->mElements[theParentTag]; nsCParserNode *theNode=aContext->PeekNode(); //this will get popped later... if(theParent->IsSinkContainer()) { CloseContainerInContext(theNode,theParentTag,aContext,aSink); } else CloseContext(theNode,theParentTag,aContext,aSink); theCount--; } if(NS_SUCCEEDED(result)){ theParentTag=aContext->Last(); theParent=gElementTable->mElements[theParentTag]; result=theParent->HandleStartToken(aNode,aTag,aContext,aSink); } } return result; } else { PRBool theElementCanOpen=PR_FALSE; //the logic here is simple: // This operation can only succeed if the given tag is open, AND // all the tags below it have optional end tags. // If these conditions aren't met, we bail out, leaving the tag open. if(mTag!=aTag) { PRInt32 theLastPos=aContext->LastOf(aTag); //see if it's already open... if(-1!=theLastPos) { PRInt32 theCount=aContext->GetCount(); result=HandleEndToken(aNode,aTag,aContext,aSink); theElementCanOpen=PRBool(aContext->GetCount()Last(); CElement* theParent=gElementTable->mElements[theParentTag]; return theParent->HandleStartToken(aNode,aTag,aContext,aSink); } } } //ok, here's our last recourse -- let's let the parent handle it. CElement* theContainer=GetDefaultContainerFor(theElement); if(theContainer) { result=theContainer->HandleMisplacedStartToken(aNode,aTag,aContext,aSink); } } } } return result; } nsresult CElement::HandleEndToken(nsCParserNode* aNode,eHTMLTags aTag,nsDTDContext* aContext,nsIHTMLContentSink* aSink) { nsresult result=NS_OK; if(aContext->Last()==aTag) { CElement* theElement=gElementTable->mElements[aTag]; if(theElement) { if(theElement->IsSinkContainer()) { result=CloseContainerInContext(aNode,aTag,aContext,aSink); } else result=CloseContext(aNode,aTag,aContext,aSink); return result; } } PRInt32 theCount=aContext->GetCount(); PRInt32 theIndex=theCount-1; PRInt32 theCloseTarget=FindAutoCloseTargetForEndTag(aNode,aTag,aContext,aSink,theIndex); if(-1!=theCloseTarget) { while(theCloseTargetLast(); eHTMLTags theGrandParentTag=aContext->TagAt(theCount-2); CElement *theGrandParent=GetElement(theGrandParentTag); result=theGrandParent->HandleEndToken(aNode,theTag,aContext,aSink); theCount--; } //return result; } return result; } inline CElement* CElement::GetDelegate(void) { if(eHTMLTag_unknown!=mDelegate) { return gElementTable->mElements[mDelegate]; } return 0; } inline CElement* CElement::GetDefaultContainerFor(CElement* anElement) { CElement* result=0; if(anElement) { if(anElement->mGroup.mBits.mBlock) { result=gElementTable->mElements[eHTMLTag_body]; } else if(anElement->mGroup.mBits.mHeadContent) { result=gElementTable->mElements[eHTMLTag_head]; } else if(anElement->mGroup.mBits.mHeadMisc) { result=gElementTable->mElements[eHTMLTag_head]; } } return result; } //this tells us whether this tag is a block tag within the given parent //NOTE: aParentID is currently ignored, but shouldn't be. PRBool CElement::IsBlockElement(eHTMLTags aParentID) { CGroupMembers& theBlockGroup=CBlockElement::GetBlockGroupMembers(); PRBool result=ContainsGroup(theBlockGroup,mGroup); return result; } //this tells us whether this tag is an inline tag within the given parent //NOTE: aParentID is currently ignored, but shouldn't be. PRBool CElement::IsInlineElement(eHTMLTags aParentID) { CGroupMembers& theInlineGroup=CInlineElement::GetContainedGroups(); PRBool result=ContainsGroup(theInlineGroup,mGroup); return result; } #endif