3107 lines
95 KiB
C++
3107 lines
95 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/*
|
|
* The contents of this file are subject to the Netscape Public License
|
|
* Version 1.0 (the "NPL"); you may not use this file except in
|
|
* compliance with the NPL. You may obtain a copy of the NPL at
|
|
* http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
|
* for the specific language governing rights and limitations under the
|
|
* NPL.
|
|
*
|
|
* The Initial Developer of this code under the NPL is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
|
* Reserved.
|
|
*/
|
|
|
|
#include "nsDebug.h"
|
|
#include "nsIDTDDebug.h"
|
|
#include "CNavDTD.h"
|
|
#include "nsHTMLTokens.h"
|
|
#include "nsCRT.h"
|
|
#include "nsParser.h"
|
|
#include "nsIParser.h"
|
|
#include "nsIHTMLContentSink.h"
|
|
#include "nsScanner.h"
|
|
#include "nsTokenHandler.h"
|
|
#include "nsIDTDDebug.h"
|
|
#include "prenv.h" //this is here for debug reasons...
|
|
#include "prtypes.h" //this is here for debug reasons...
|
|
#include "prio.h"
|
|
#include "plstr.h"
|
|
#include "nsDTDUtils.h"
|
|
#include "nsTagHandler.h"
|
|
#include "nsViewSourceHTML.h"
|
|
#include "nsHTMLTokenizer.h"
|
|
#include "nsTime.h"
|
|
#include "nsIElementObserver.h"
|
|
|
|
#ifdef XP_PC
|
|
#include <direct.h> //this is here for debug reasons...
|
|
#include <iostream.h>
|
|
#endif
|
|
#include "prmem.h"
|
|
|
|
#undef ENABLE_RESIDUALSTYLE
|
|
#define RICKG_DEBUG 0
|
|
#ifdef RICKG_DEBUG
|
|
#include <fstream.h>
|
|
#endif
|
|
|
|
|
|
static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID);
|
|
static NS_DEFINE_IID(kIDTDIID, NS_IDTD_IID);
|
|
static NS_DEFINE_IID(kClassIID, NS_INAVHTML_DTD_IID);
|
|
|
|
static const char* kNullToken = "Error: Null token given";
|
|
static const char* kInvalidTagStackPos = "Error: invalid tag stack position";
|
|
static char* kVerificationDir = "c:/temp";
|
|
static char gShowCRC=0;
|
|
|
|
|
|
static eHTMLTags gFormElementTags[]= {
|
|
eHTMLTag_button, eHTMLTag_fieldset, eHTMLTag_input,
|
|
eHTMLTag_isindex, eHTMLTag_label, eHTMLTag_legend,
|
|
eHTMLTag_option, eHTMLTag_optgroup, eHTMLTag_select,
|
|
eHTMLTag_textarea};
|
|
|
|
static eHTMLTags gTableChildTags[]={
|
|
eHTMLTag_caption, eHTMLTag_col, eHTMLTag_colgroup,
|
|
eHTMLTag_tbody, eHTMLTag_tfoot, eHTMLTag_tr,
|
|
eHTMLTag_th, eHTMLTag_thead, eHTMLTag_td};
|
|
|
|
static eHTMLTags gWhitespaceTags[]={
|
|
eHTMLTag_newline, eHTMLTag_whitespace};
|
|
|
|
static eHTMLTags gHeadChildTags[]={
|
|
eHTMLTag_caption, eHTMLTag_col, eHTMLTag_colgroup, eHTMLTag_tbody,
|
|
eHTMLTag_tfoot, eHTMLTag_tr, eHTMLTag_thead, eHTMLTag_td};
|
|
|
|
static eHTMLTags gNonPropagatedTags[]={
|
|
eHTMLTag_head, eHTMLTag_html, eHTMLTag_body};
|
|
|
|
|
|
#include "nsElementTable.h"
|
|
|
|
|
|
/***************************************************************
|
|
This the ITagHandler deque deallocator, needed by the
|
|
CTagHandlerRegister
|
|
***************************************************************/
|
|
class CTagHandlerDeallocator: public nsDequeFunctor{
|
|
public:
|
|
virtual void* operator()(void* aObject) {
|
|
nsITagHandler* tagHandler = (nsITagHandler*)aObject;
|
|
delete tagHandler;
|
|
return 0;
|
|
}
|
|
};
|
|
|
|
/***************************************************************
|
|
This funtor will be called for each item in the TagHandler que to
|
|
check for a Tag name, and setting the current TagHandler when it is reached
|
|
***************************************************************/
|
|
class CTagFinder: public nsDequeFunctor{
|
|
|
|
public:
|
|
CTagFinder(){}
|
|
void Initialize(const nsString &aTagName) {mTagName = aTagName;}
|
|
|
|
virtual ~CTagFinder() {
|
|
}
|
|
|
|
virtual void* operator()(void* aObject) {
|
|
nsString* theString = ((nsITagHandler*)aObject)->GetString();
|
|
if( theString->Equals(mTagName)){
|
|
return aObject;
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
nsAutoString mTagName;
|
|
};
|
|
|
|
/***************************************************************
|
|
This a an object that will keep track of TagHandlers in
|
|
the DTD. Uses a factory pattern
|
|
***************************************************************/
|
|
class CTagHandlerRegister {
|
|
public:
|
|
|
|
CTagHandlerRegister() : mTagHandlerDeque(new CTagHandlerDeallocator()) {
|
|
}
|
|
|
|
~CTagHandlerRegister() {
|
|
}
|
|
|
|
void RegisterTagHandler(nsITagHandler *aTagHandler){
|
|
mTagHandlerDeque.Push(aTagHandler);
|
|
}
|
|
|
|
nsITagHandler* FindTagHandler(const nsString &aTagName){
|
|
nsITagHandler* foundHandler = nsnull;
|
|
|
|
mTagFinder.Initialize(aTagName);
|
|
mTagHandlerDeque.Begin();
|
|
foundHandler = (nsITagHandler*) mTagHandlerDeque.FirstThat(mTagFinder);
|
|
return foundHandler;
|
|
}
|
|
|
|
nsDeque mTagHandlerDeque;
|
|
CTagFinder mTagFinder;
|
|
};
|
|
|
|
|
|
/************************************************************************
|
|
The CTagHandlerRegister for a CNavDTD.
|
|
This is where special taghanders for our tags can be managed and called from
|
|
Note: This can also be attached to some object so it can be refcounted
|
|
and destroyed if you want this to go away when not imbedded.
|
|
************************************************************************/
|
|
//CTagHandlerRegister gTagHandlerRegister;
|
|
|
|
|
|
/************************************************************************
|
|
And now for the main class -- CNavDTD...
|
|
************************************************************************/
|
|
|
|
/**
|
|
* This method gets called as part of our COM-like interfaces.
|
|
* Its purpose is to create an interface to parser object
|
|
* of some type.
|
|
*
|
|
* @update gess 4/8/98
|
|
* @param nsIID id of object to discover
|
|
* @param aInstancePtr ptr to newly discovered interface
|
|
* @return NS_xxx result code
|
|
*/
|
|
nsresult CNavDTD::QueryInterface(const nsIID& aIID, void** aInstancePtr)
|
|
{
|
|
if (NULL == aInstancePtr) {
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
|
|
if(aIID.Equals(kISupportsIID)) { //do IUnknown...
|
|
*aInstancePtr = (nsIDTD*)(this);
|
|
}
|
|
else if(aIID.Equals(kIDTDIID)) { //do IParser base class...
|
|
*aInstancePtr = (nsIDTD*)(this);
|
|
}
|
|
else if(aIID.Equals(kClassIID)) { //do this class...
|
|
*aInstancePtr = (CNavDTD*)(this);
|
|
}
|
|
else {
|
|
*aInstancePtr=0;
|
|
return NS_NOINTERFACE;
|
|
}
|
|
NS_ADDREF_THIS();
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
/**
|
|
* This method is defined in nsIParser. It is used to
|
|
* cause the COM-like construction of an nsParser.
|
|
*
|
|
* @update gess 4/8/98
|
|
* @param nsIParser** ptr to newly instantiated parser
|
|
* @return NS_xxx error result
|
|
*/
|
|
NS_HTMLPARS nsresult NS_NewNavHTMLDTD(nsIDTD** aInstancePtrResult)
|
|
{
|
|
CNavDTD* it = new CNavDTD();
|
|
|
|
if (it == 0) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
return it->QueryInterface(kClassIID, (void **) aInstancePtrResult);
|
|
}
|
|
|
|
|
|
NS_IMPL_ADDREF(CNavDTD)
|
|
NS_IMPL_RELEASE(CNavDTD)
|
|
|
|
|
|
|
|
/**
|
|
*
|
|
*
|
|
* @update gess 6/9/98
|
|
* @param
|
|
* @return
|
|
*/
|
|
static
|
|
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:
|
|
case eToken_whitespace:
|
|
case eToken_newline:
|
|
case eToken_text:
|
|
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_attribute:
|
|
result=theDTD->HandleAttributeToken(aToken); break;
|
|
case eToken_style:
|
|
result=theDTD->HandleStyleToken(aToken); break;
|
|
case eToken_instruction:
|
|
result=theDTD->HandleProcessingInstructionToken(aToken); break;
|
|
default:
|
|
result=0;
|
|
}//switch
|
|
}//if
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Register a handler.
|
|
*
|
|
* @update gess 4/2/98
|
|
* @param
|
|
* @return
|
|
*/
|
|
CITokenHandler* CNavDTD::AddTokenHandler(CITokenHandler* aHandler) {
|
|
NS_ASSERTION(0!=aHandler,"Error: Null handler");
|
|
|
|
if(aHandler) {
|
|
eHTMLTokenTypes type=(eHTMLTokenTypes)aHandler->GetTokenType();
|
|
if(type<eToken_last) {
|
|
mTokenHandlers[type]=aHandler;
|
|
}
|
|
else {
|
|
//add code here to handle dynamic tokens...
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* init the set of default token handlers...
|
|
*
|
|
* @update gess 3/25/98
|
|
* @param
|
|
* @return
|
|
*/
|
|
void CNavDTD::InitializeDefaultTokenHandlers() {
|
|
AddTokenHandler(new CTokenHandler(NavDispatchTokenHandler,eToken_start));
|
|
|
|
AddTokenHandler(new CTokenHandler(NavDispatchTokenHandler,eToken_end));
|
|
AddTokenHandler(new CTokenHandler(NavDispatchTokenHandler,eToken_comment));
|
|
AddTokenHandler(new CTokenHandler(NavDispatchTokenHandler,eToken_entity));
|
|
|
|
AddTokenHandler(new CTokenHandler(NavDispatchTokenHandler,eToken_whitespace));
|
|
AddTokenHandler(new CTokenHandler(NavDispatchTokenHandler,eToken_newline));
|
|
AddTokenHandler(new CTokenHandler(NavDispatchTokenHandler,eToken_text));
|
|
|
|
AddTokenHandler(new CTokenHandler(NavDispatchTokenHandler,eToken_attribute));
|
|
// AddTokenHandler(new CTokenHandler(NavDispatchTokenHandler,eToken_script));
|
|
AddTokenHandler(new CTokenHandler(NavDispatchTokenHandler,eToken_style));
|
|
AddTokenHandler(new CTokenHandler(NavDispatchTokenHandler,eToken_skippedcontent));
|
|
AddTokenHandler(new CTokenHandler(NavDispatchTokenHandler,eToken_instruction));
|
|
}
|
|
|
|
/**
|
|
* Finds a tag handler for the given tag type, given in string.
|
|
*
|
|
* @update gess 4/2/98
|
|
* @param aString contains name of tag to be handled
|
|
* @return valid tag handler (if found) or null
|
|
*/
|
|
void CNavDTD::DeleteTokenHandlers(void) {
|
|
for(int i=eToken_unknown;i<eToken_last;i++){
|
|
delete mTokenHandlers[i];
|
|
mTokenHandlers[i]=0;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Default constructor
|
|
*
|
|
* @update gess 4/9/98
|
|
* @param
|
|
* @return
|
|
*/
|
|
CNavDTD::CNavDTD() : nsIDTD(), mMisplacedContent(0), mSkippedContent(0) {
|
|
NS_INIT_REFCNT();
|
|
mSink = 0;
|
|
mParser=0;
|
|
mDTDDebug=0;
|
|
mLineNumber=1;
|
|
nsCRT::zero(mTokenHandlers,sizeof(mTokenHandlers));
|
|
mHasOpenBody=PR_FALSE;
|
|
mHasOpenHead=0;
|
|
mHasOpenForm=PR_FALSE;
|
|
mHasOpenMap=PR_FALSE;
|
|
InitializeDefaultTokenHandlers();
|
|
mHeadContext=new nsDTDContext();
|
|
mBodyContext=new nsDTDContext();
|
|
mFormContext=0;
|
|
mMapContext=0;
|
|
mTokenizer=0;
|
|
mComputedCRC32=0;
|
|
mExpectedCRC32=0;
|
|
mSaveBadTokens = PR_FALSE;
|
|
// DebugDumpContainmentRules2(*this,"c:/temp/DTDRules.new","New CNavDTD Containment Rules");
|
|
#ifdef RICKG_DEBUG
|
|
nsHTMLElement::DebugDumpContainment("c:/temp/rules.new","ElementTable Rules");
|
|
nsHTMLElement::DebugDumpMembership("c:/temp/table.out");
|
|
nsHTMLElement::DebugDumpContainType("c:/temp/ctnrules.out");
|
|
#endif
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
* @update gess1/8/99
|
|
* @param
|
|
* @return
|
|
*/
|
|
const nsIID& CNavDTD::GetMostDerivedIID(void)const {
|
|
return kClassIID;
|
|
}
|
|
|
|
/**
|
|
* Default destructor
|
|
*
|
|
* @update gess 4/9/98
|
|
* @param
|
|
* @return
|
|
*/
|
|
CNavDTD::~CNavDTD(){
|
|
DeleteTokenHandlers();
|
|
delete mHeadContext;
|
|
delete mBodyContext;
|
|
if(mTokenizer)
|
|
delete (nsHTMLTokenizer*)mTokenizer;
|
|
NS_IF_RELEASE(mDTDDebug);
|
|
}
|
|
|
|
|
|
/**
|
|
* Call this method if you want the DTD to construct a fresh
|
|
* instance of itself.
|
|
* @update gess7/23/98
|
|
* @param
|
|
* @return
|
|
*/
|
|
nsresult CNavDTD::CreateNewInstance(nsIDTD** aInstancePtrResult){
|
|
return NS_NewNavHTMLDTD(aInstancePtrResult);
|
|
}
|
|
|
|
/**
|
|
* Called by the parser to initiate dtd verification of the
|
|
* internal context stack.
|
|
* @update gess 7/23/98
|
|
* @param
|
|
* @return
|
|
*/
|
|
PRBool CNavDTD::Verify(nsString& aURLRef,nsIParser* aParser){
|
|
PRBool result=PR_TRUE;
|
|
|
|
if(!mDTDDebug){;
|
|
nsresult rval = NS_NewDTDDebug(&mDTDDebug);
|
|
if (NS_OK != rval) {
|
|
fputs("Cannot create parser debugger.\n", stdout);
|
|
result=-PR_FALSE;
|
|
}
|
|
else mDTDDebug->SetVerificationDirectory(kVerificationDir);
|
|
}
|
|
if(mDTDDebug) {
|
|
// mDTDDebug->Verify(this,aParser,mBodyContext->GetCount(),mBodyContext->mStack,aURLRef);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* This method is called to determine if the given DTD can parse
|
|
* a document in a given source-type.
|
|
* NOTE: Parsing always assumes that the end result will involve
|
|
* storing the result in the main content model.
|
|
* @update gess6/24/98
|
|
* @param
|
|
* @return TRUE if this DTD can satisfy the request; FALSE otherwise.
|
|
*/
|
|
eAutoDetectResult CNavDTD::CanParse(nsString& aContentType, nsString& aCommand, nsString& aBuffer, PRInt32 aVersion) {
|
|
eAutoDetectResult result=eUnknownDetect;
|
|
|
|
if(!aCommand.Equals(kViewSourceCommand)) {
|
|
if(PR_TRUE==aContentType.Equals(kHTMLTextContentType)) {
|
|
result=ePrimaryDetect;
|
|
}
|
|
else {
|
|
//otherwise, look into the buffer to see if you recognize anything...
|
|
if(BufferContainsHTML(aBuffer)){
|
|
result=ePrimaryDetect;
|
|
if(0==aContentType.Length())
|
|
aContentType=kHTMLTextContentType;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
PRTime gStartTime;
|
|
|
|
/**
|
|
*
|
|
* @update gess5/18/98
|
|
* @param
|
|
* @return
|
|
*/
|
|
nsresult CNavDTD::WillBuildModel(nsString& aFilename,PRBool aNotifySink,nsString& aSourceType,nsIContentSink* aSink){
|
|
nsresult result=NS_OK;
|
|
|
|
mFilename=aFilename;
|
|
mHasOpenBody=PR_FALSE;
|
|
mHadBodyOrFrameset=PR_FALSE;
|
|
mLineNumber=1;
|
|
mHasOpenScript=PR_FALSE;
|
|
mSink=(nsIHTMLContentSink*)aSink;
|
|
if((aNotifySink) && (mSink)) {
|
|
|
|
#ifdef RGESS_DEBUG
|
|
gStartTime = PR_Now();
|
|
printf("Begin parsing...\n");
|
|
#endif
|
|
|
|
result = mSink->WillBuildModel();
|
|
mSkipTarget=eHTMLTag_unknown;
|
|
mComputedCRC32=0;
|
|
mExpectedCRC32=0;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
CTokenRecycler* gRecycler=0;
|
|
|
|
/**
|
|
* This is called when it's time to read as many tokens from the tokenizer
|
|
* as you can. Not all tokens may make sense, so you may not be able to
|
|
* read them all (until more come in later).
|
|
*
|
|
* @update gess5/18/98
|
|
* @param aParser is the parser object that's driving this process
|
|
* @return error code (almost always 0)
|
|
*/
|
|
nsresult CNavDTD::BuildModel(nsIParser* aParser,nsITokenizer* aTokenizer,nsITokenObserver* anObserver,nsIContentSink* aSink) {
|
|
nsresult result=NS_OK;
|
|
|
|
NS_ADDREF(aSink);
|
|
if(aTokenizer) {
|
|
nsITokenizer* oldTokenizer=mTokenizer;
|
|
mTokenizer=aTokenizer;
|
|
mParser=(nsParser*)aParser;
|
|
mSink=(nsIHTMLContentSink*)aSink;
|
|
gRecycler=(CTokenRecycler*)mTokenizer->GetTokenRecycler();
|
|
while(NS_OK==result){
|
|
CToken* theToken=mTokenizer->PopToken();
|
|
if(theToken) {
|
|
result=HandleToken(theToken,aParser);
|
|
}
|
|
else break;
|
|
}//while
|
|
mTokenizer=oldTokenizer;
|
|
}
|
|
else result=NS_ERROR_HTMLPARSER_BADTOKENIZER;
|
|
NS_IF_RELEASE(aSink);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @update gess5/18/98
|
|
* @param
|
|
* @return
|
|
*/
|
|
nsresult CNavDTD::DidBuildModel(nsresult anErrorCode,PRBool aNotifySink,nsIParser* aParser,nsIContentSink* aSink){
|
|
nsresult result= NS_OK;
|
|
|
|
if((NS_OK==anErrorCode) && (!mHadBodyOrFrameset)) {
|
|
CStartToken theToken(eHTMLTag_body); //open the body container...
|
|
result=HandleStartToken(&theToken);
|
|
}
|
|
|
|
if(aParser){
|
|
mSink=(nsIHTMLContentSink*)aSink;
|
|
if(aNotifySink && mSink){
|
|
if((NS_OK==anErrorCode) && (mBodyContext->GetCount()>0)) {
|
|
while(mBodyContext->GetCount() > 0) {
|
|
eHTMLTags theTarget = mBodyContext->Last();
|
|
if(gHTMLElements[theTarget].HasSpecialProperty(kBadContentWatch))
|
|
result = HandleSavedTokensAbove(theTarget);
|
|
CloseContainersTo(theTarget,PR_FALSE);
|
|
}
|
|
//result = CloseContainersTo(0,eHTMLTag_unknown,PR_FALSE);
|
|
}
|
|
|
|
#ifdef RGESS_DEBUG
|
|
PRTime theEnd= PR_Now();
|
|
PRTime creates, ustoms;
|
|
LL_I2L(ustoms, 1000);
|
|
LL_SUB(creates, theEnd, gStartTime);
|
|
LL_DIV(creates, creates, ustoms);
|
|
printf("End parse elapsed: %lldms\n",creates);
|
|
#endif
|
|
|
|
//let's only grab this state once!
|
|
if(!gShowCRC) {
|
|
gShowCRC=1; //this only indicates we'll not initialize again.
|
|
char* theEnvString = PR_GetEnv("RICKG_CRC");
|
|
if(theEnvString){
|
|
if(('1'==theEnvString[0]) || ('Y'==theEnvString[0]) || ('y'==theEnvString[0])){
|
|
gShowCRC=2; //this indicates that the CRC flag was found in the environment.
|
|
}
|
|
}
|
|
}
|
|
|
|
if(2==gShowCRC) {
|
|
if(mComputedCRC32!=mExpectedCRC32) {
|
|
if(mExpectedCRC32!=0) {
|
|
printf("CRC Computed: %u Expected CRC: %u\n,",mComputedCRC32,mExpectedCRC32);
|
|
result = mSink->DidBuildModel(2);
|
|
}
|
|
else {
|
|
printf("Computed CRC: %u.\n",mComputedCRC32);
|
|
result = mSink->DidBuildModel(3);
|
|
}
|
|
}
|
|
else result = mSink->DidBuildModel(0);
|
|
}
|
|
else result=mSink->DidBuildModel(0);
|
|
|
|
if(mDTDDebug) {
|
|
mDTDDebug->DumpVectorRecord();
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* This big dispatch method is used to route token handler calls to the right place.
|
|
* What's wrong with it? This table, and the dispatch methods themselves need to be
|
|
* moved over to the delegate. Ah, so much to do...
|
|
*
|
|
* @update gess 5/21/98
|
|
* @param aToken
|
|
* @param aParser
|
|
* @return
|
|
*/
|
|
nsresult CNavDTD::HandleToken(CToken* aToken,nsIParser* aParser){
|
|
nsresult result=NS_OK;
|
|
|
|
if(aToken) {
|
|
CHTMLToken* theToken= (CHTMLToken*)(aToken);
|
|
eHTMLTokenTypes theType=eHTMLTokenTypes(theToken->GetTokenType());
|
|
eHTMLTags theTag=(eHTMLTags)theToken->GetTypeID();
|
|
PRBool execSkipContent=PR_FALSE;
|
|
|
|
/* ---------------------------------------------------------------------------------
|
|
To understand this little piece of code, you need to look below too.
|
|
In essence, this code caches "skipped content" until we find a given skiptarget.
|
|
Once we find the skiptarget, we take all skipped content up to that point and
|
|
coallate it. Then we push those tokens back onto the tokenizer deque.
|
|
---------------------------------------------------------------------------------
|
|
*/
|
|
if(mSkipTarget){ //handle a preexisting target...
|
|
if((theTag==mSkipTarget) && (eToken_end==theType)){
|
|
mSkipTarget=eHTMLTag_unknown; //stop skipping.
|
|
//mTokenizer->PushTokenFront(aToken); //push the end token...
|
|
execSkipContent=PR_TRUE;
|
|
gRecycler->RecycleToken(aToken);
|
|
theToken=(CHTMLToken*)mSkippedContent.PopFront();
|
|
// result=HandleStartToken(theToken);
|
|
}
|
|
else {
|
|
mSkippedContent.Push(theToken);
|
|
return result;
|
|
}
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------------
|
|
This section of code is used to "move" misplaced content from one location in
|
|
our document model to another. (Consider what would happen if we found a <P> tag
|
|
and text in the head.) To move content, we throw it onto the misplacedcontent
|
|
deque until we can deal with it.
|
|
---------------------------------------------------------------------------------
|
|
*/
|
|
if(!execSkipContent) {
|
|
static eHTMLTags passThru[]= {eHTMLTag_html,eHTMLTag_comment,eHTMLTag_newline,eHTMLTag_whitespace,eHTMLTag_script};
|
|
if(!FindTagInSet(theTag,passThru,sizeof(passThru)/sizeof(eHTMLTag_unknown))){
|
|
if(!gHTMLElements[eHTMLTag_html].SectionContains(theTag,PR_FALSE)) {
|
|
if(!mHadBodyOrFrameset){
|
|
if(mHasOpenHead) {
|
|
//just fall through and handle current token
|
|
if(!gHTMLElements[eHTMLTag_head].IsChildOfHead(theTag)){
|
|
mMisplacedContent.Push(aToken);
|
|
return result;
|
|
}
|
|
}
|
|
else {
|
|
if(gHTMLElements[eHTMLTag_body].SectionContains(theTag,PR_TRUE)){
|
|
mTokenizer->PushTokenFront(aToken); //put this token back...
|
|
mTokenizer->PrependTokens(mMisplacedContent); //push misplaced content
|
|
theToken=(CHTMLToken*)gRecycler->CreateTokenOfType(eToken_start,eHTMLTag_body);
|
|
//now open a body...
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(theToken){
|
|
//Before dealing with the token normally, we need to deal with skip targets
|
|
if((!execSkipContent) &&
|
|
(theType!=eToken_end) &&
|
|
(eHTMLTag_unknown==mSkipTarget) &&
|
|
(gHTMLElements[theTag].mSkipTarget)){ //create a new target
|
|
mSkipTarget=gHTMLElements[theTag].mSkipTarget;
|
|
mSkippedContent.Push(theToken);
|
|
}
|
|
else {
|
|
|
|
CITokenHandler* theHandler=GetTokenHandler(theType);
|
|
if(theHandler) {
|
|
mParser=(nsParser*)aParser;
|
|
result=(*theHandler)(theToken,this);
|
|
if(NS_SUCCEEDED(result) || (NS_ERROR_HTMLPARSER_BLOCK==result)) {
|
|
gRecycler->RecycleToken(theToken);
|
|
}
|
|
else if(NS_ERROR_HTMLPARSER_MISPLACED!=result)
|
|
mTokenizer->PushTokenFront(theToken);
|
|
else result=NS_OK;
|
|
if (mDTDDebug) {
|
|
//mDTDDebug->Verify(this, mParser, mBodyContext->GetCount(), mBodyContext->mStack, mFilename);
|
|
}
|
|
} //if
|
|
}
|
|
}
|
|
|
|
}//if
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* This method causes all tokens to be dispatched to the given tag handler.
|
|
*
|
|
* @update gess 3/25/98
|
|
* @param aHandler -- object to receive subsequent tokens...
|
|
* @return error code (usually 0)
|
|
*/
|
|
nsresult CNavDTD::CaptureTokenPump(nsITagHandler* aHandler) {
|
|
nsresult result=NS_OK;
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* This method releases the token-pump capture obtained in CaptureTokenPump()
|
|
*
|
|
* @update gess 3/25/98
|
|
* @param aHandler -- object that received tokens...
|
|
* @return error code (usually 0)
|
|
*/
|
|
nsresult CNavDTD::ReleaseTokenPump(nsITagHandler* aHandler){
|
|
nsresult result=NS_OK;
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* This gets called after we've handled a given start tag.
|
|
* It's a generic hook to let us to post processing.
|
|
* @param aToken contains the tag in question
|
|
* @param aChildTag is the tag itself.
|
|
* @return status
|
|
*/
|
|
nsresult CNavDTD::DidHandleStartTag(nsCParserNode& aNode,eHTMLTags aChildTag){
|
|
nsresult result=NS_OK;
|
|
|
|
switch(aChildTag){
|
|
case eHTMLTag_body:
|
|
case eHTMLTag_frameset:
|
|
mHadBodyOrFrameset=PR_TRUE;
|
|
break;
|
|
|
|
case eHTMLTag_pre:
|
|
case eHTMLTag_listing:
|
|
{
|
|
CToken* theNextToken=mTokenizer->PeekToken();
|
|
if(theNextToken) {
|
|
eHTMLTokenTypes theType=eHTMLTokenTypes(theNextToken->GetTokenType());
|
|
if(eToken_newline==theType){
|
|
mTokenizer->PopToken(); //skip 1st newline inside PRE and LISTING
|
|
}//if
|
|
}//if
|
|
}
|
|
break;
|
|
|
|
case eHTMLTag_plaintext:
|
|
case eHTMLTag_xmp:
|
|
//grab the skipped content and dump it out as text...
|
|
{
|
|
const nsString& theText=aNode.GetSkippedContent();
|
|
if(0<theText.Length()) {
|
|
CViewSourceHTML::WriteText(theText,*mSink,PR_TRUE);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}//switch
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Determine whether the given tag is open anywhere
|
|
* in our context stack.
|
|
*
|
|
* @update gess 4/2/98
|
|
* @param eHTMLTags tag to be searched for in stack
|
|
* @return topmost index of tag on stack
|
|
*/
|
|
static
|
|
PRInt32 GetTopmostIndexOf(eHTMLTags aTag,nsEntryStack& aTagStack) {
|
|
int i=0;
|
|
int count = aTagStack.GetCount();
|
|
for(i=(count-1);i>=0;i--){
|
|
if(aTagStack[i]==aTag)
|
|
return i;
|
|
}
|
|
return kNotFound;
|
|
}
|
|
|
|
/**
|
|
* This method is called to determine whether or not a START tag
|
|
* can be autoclosed. This means that based on the current
|
|
* context, the stack should be closed to the nearest matching
|
|
* tag.
|
|
*
|
|
* @param aTag -- tag enum of child to be tested
|
|
* @return index of tag to be closed
|
|
*/
|
|
static PRInt32 FindAutoCloseTargetForStartTag2(eHTMLTags aChild,nsEntryStack& aTagStack,CNavDTD& aDTD) {
|
|
PRInt32 max=aTagStack.GetCount();
|
|
|
|
if(nsHTMLElement::IsContainer(aChild)){
|
|
PRInt32 index=max;
|
|
CTagList* theRootTags=gHTMLElements[aChild].GetRootTags();
|
|
|
|
while(--index>=0){
|
|
eHTMLTags thePrevTag=aTagStack.TagAt(index);
|
|
|
|
if((theRootTags) && (theRootTags->Contains(thePrevTag))) {
|
|
return index+1;
|
|
}
|
|
|
|
if(aDTD.CanOmit(thePrevTag,aChild)){
|
|
return index+1;
|
|
}
|
|
|
|
if(aDTD.CanContain(thePrevTag,aChild)){
|
|
return index+1;
|
|
}
|
|
}
|
|
}
|
|
return max; //which means don't close anything...
|
|
}
|
|
|
|
|
|
/**
|
|
* This method is called to determine whether or not a START tag
|
|
* can be autoclosed. This means that based on the current
|
|
* context, the stack should be closed to the nearest matching
|
|
* tag.
|
|
*
|
|
* @param aTag -- tag enum of child to be tested
|
|
* @return PR_TRUE if autoclosure should occur
|
|
*/
|
|
static
|
|
eHTMLTags FindAutoCloseTargetForStartTag(eHTMLTags aCurrentTag,nsEntryStack& aTagStack) {
|
|
int theTopIndex = aTagStack.GetCount();
|
|
eHTMLTags thePrevTag=aTagStack.Last();
|
|
|
|
if(nsHTMLElement::IsContainer(aCurrentTag)){
|
|
if(thePrevTag==aCurrentTag) {
|
|
return (gHTMLElements[aCurrentTag].CanContainSelf()) ? eHTMLTag_unknown: aCurrentTag;
|
|
}
|
|
CTagList* theRootTags=gHTMLElements[aCurrentTag].GetRootTags();
|
|
PRInt32 theRootIndex=(theRootTags) ? theRootTags->GetTopmostIndexOf(aTagStack) : kNotFound;
|
|
if(nsHTMLElement::IsBlockCloser(aCurrentTag)) {
|
|
if(theRootTags) {
|
|
CTagList* theStartTags=gHTMLElements[aCurrentTag].GetAutoCloseStartTags();
|
|
PRInt32 thePeerIndex=kNotFound;
|
|
if(theStartTags){
|
|
thePeerIndex=theStartTags->GetBottommostIndexOf(aTagStack,theRootIndex+1);
|
|
}
|
|
else {
|
|
//this extra check is need to handle case like this: <DIV><P><DIV>
|
|
//the new div can close the P,but doesn't close the top DIV.
|
|
thePeerIndex=GetTopmostIndexOf(aCurrentTag,aTagStack);
|
|
if(gHTMLElements[aCurrentTag].CanContainSelf()) {
|
|
thePeerIndex++;
|
|
}
|
|
}
|
|
if(theRootIndex<thePeerIndex) {
|
|
return aTagStack[thePeerIndex]; //return the tag that was used in peer test.
|
|
}
|
|
}
|
|
|
|
CTagList* theCloseTags=gHTMLElements[aCurrentTag].GetAutoCloseStartTags();
|
|
if(theCloseTags){
|
|
PRInt32 thePeerIndex=theCloseTags->GetTopmostIndexOf(aTagStack);
|
|
if(kNotFound!=thePeerIndex){
|
|
if(thePeerIndex==theTopIndex-1) {
|
|
//the guy you can autoclose is on the top of the stack...
|
|
return thePrevTag;
|
|
} //if
|
|
} //if
|
|
}//if
|
|
} //if isblockcloser
|
|
if(kNotFound<theRootIndex) {
|
|
//This block handles our fallback cases like: <html><body><center><p> <- <table>
|
|
while((theRootIndex<--theTopIndex) && (!gHTMLElements[aTagStack[theTopIndex]].CanContain(aCurrentTag))) {
|
|
}
|
|
return aTagStack[theTopIndex+1];
|
|
//return aTagStack.mTags[theRootIndex+1];
|
|
} //else
|
|
} //if
|
|
return eHTMLTag_unknown;
|
|
}
|
|
|
|
/**
|
|
* Call this to find the index of a given child, or (if not found)
|
|
* the index of its nearest synonym.
|
|
*
|
|
* @update gess 3/25/98
|
|
* @param aTagStack -- list of open tags
|
|
* @param aTag -- tag to test for containership
|
|
* @return index of kNotFound
|
|
*/
|
|
static
|
|
PRInt32 GetIndexOfChildOrSynonym(nsEntryStack& aTagStack,eHTMLTags aChildTag) {
|
|
PRInt32 theChildIndex=aTagStack.GetTopmostIndexOf(aChildTag);
|
|
if(kNotFound==theChildIndex) {
|
|
CTagList* theSynTags=gHTMLElements[aChildTag].GetSynonymousTags(); //get the list of tags that THIS tag can close
|
|
if(theSynTags) {
|
|
theChildIndex=theSynTags->GetTopmostIndexOf(aTagStack);
|
|
}
|
|
else{
|
|
theChildIndex=aTagStack.GetCount();
|
|
PRInt32 theGroup=gHTMLElements[aChildTag].mParentBits;
|
|
while(-1<--theChildIndex) {
|
|
eHTMLTags theTag=aTagStack[theChildIndex];
|
|
if(gHTMLElements[theTag].IsMemberOf(theGroup)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
return theChildIndex;
|
|
}
|
|
|
|
/**
|
|
* This method is called to determine whether or not the child
|
|
* tag is happy being OPENED in the context of the current
|
|
* tag stack. This is only called if the current parent thinks
|
|
* it wants to contain the given childtag.
|
|
*
|
|
* @param aChildTag -- tag enum of child to be opened
|
|
* @param aTagStack -- ref to current tag stack in DTD.
|
|
* @return PR_TRUE if child agrees to be opened here.
|
|
*/
|
|
static
|
|
PRBool CanBeContained(eHTMLTags aParentTag,eHTMLTags aChildTag,nsEntryStack& aTagStack) {
|
|
PRBool result=PR_TRUE;
|
|
|
|
/* # Interesting test cases: Result:
|
|
* 1. <UL><LI>..<B>..<LI> inner <LI> closes outer <LI>
|
|
* 2. <CENTER><DL><DT><A><CENTER> allow nested <CENTER>
|
|
* 3. <TABLE><TR><TD><TABLE>... allow nested <TABLE>
|
|
* 4. <FRAMESET> ... <FRAMESET>
|
|
*/
|
|
|
|
//Note: This method is going away. First we need to get the elementtable to do closures right, and
|
|
// therefore we must get residual style handling to work.
|
|
|
|
/* // I've removed this on general principle.
|
|
// If style tags want to contain each other, the state should be in the element table.
|
|
|
|
if(nsHTMLElement::IsStyleTag(aParentTag))
|
|
if(nsHTMLElement::IsStyleTag(aChildTag))
|
|
return PR_TRUE;
|
|
*/
|
|
|
|
if(aTagStack.GetCount()){
|
|
CTagList* theRootTags=gHTMLElements[aChildTag].GetRootTags();
|
|
if(theRootTags) {
|
|
PRInt32 theRootIndex=theRootTags->GetTopmostIndexOf(aTagStack);
|
|
PRInt32 theChildIndex=GetIndexOfChildOrSynonym(aTagStack,aChildTag);
|
|
|
|
if((theRootIndex==theChildIndex) && (gHTMLElements[aChildTag].CanContainSelf()))
|
|
result=PR_TRUE;
|
|
else result=PRBool(theRootIndex>theChildIndex);
|
|
}
|
|
}
|
|
|
|
|
|
return result;
|
|
}
|
|
|
|
enum eProcessRule {eIgnore,eTest};
|
|
|
|
eProcessRule GetProcessRule(eHTMLTags aParentTag,eHTMLTags aChildTag){
|
|
int mParentGroup=gHTMLElements[aParentTag].mParentBits;
|
|
int mChildGroup=gHTMLElements[aChildTag].mParentBits;
|
|
|
|
eProcessRule result=eTest;
|
|
|
|
switch(mParentGroup){
|
|
case kSpecial:
|
|
case kPhrase:
|
|
case kFontStyle:
|
|
case kFormControl:
|
|
switch(mChildGroup){
|
|
case kBlock:
|
|
case kHTMLContent:
|
|
case kExtensions:
|
|
//case kFlowEntity:
|
|
case kList:
|
|
case kBlockEntity:
|
|
case kHeading:
|
|
case kHeadMisc:
|
|
case kPreformatted:
|
|
case kNone:
|
|
result=eIgnore;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* This method gets called when a start token has been
|
|
* encountered in the parse process. If the current container
|
|
* can contain this tag, then add it. Otherwise, you have
|
|
* two choices: 1) create an implicit container for this tag
|
|
* to be stored in
|
|
* 2) close the top container, and add this to
|
|
* whatever container ends up on top.
|
|
*
|
|
* @update gess 3/25/98
|
|
* @param aToken -- next (start) token to be handled
|
|
* @param aNode -- CParserNode representing this start token
|
|
* @return PR_TRUE if all went well; PR_FALSE if error occured
|
|
*/
|
|
nsresult CNavDTD::HandleDefaultStartToken(CToken* aToken,eHTMLTags aChildTag,nsIParserNode& aNode) {
|
|
NS_PRECONDITION(0!=aToken,kNullToken);
|
|
|
|
nsresult result=NS_OK;
|
|
|
|
PRBool theCanContainResult=PR_FALSE;
|
|
PRBool theChildAgrees=PR_TRUE;
|
|
PRInt32 theIndex=mBodyContext->GetCount();
|
|
|
|
do {
|
|
|
|
eHTMLTags theParentTag=mBodyContext->TagAt(--theIndex);
|
|
if(CanOmit(theParentTag,aChildTag)){
|
|
//call handleOmittedTag()...
|
|
return result;
|
|
}
|
|
|
|
eProcessRule theRule=eTest; //GetProcessRule(theParentTag,aChildTag);
|
|
switch(theRule){
|
|
case eTest:
|
|
theCanContainResult=CanContain(theParentTag,aChildTag);
|
|
theChildAgrees=PR_TRUE;
|
|
if(theCanContainResult) {
|
|
|
|
eHTMLTags theAncestor=gHTMLElements[aChildTag].mExcludingAncestor;
|
|
if(eHTMLTag_unknown!=theAncestor){
|
|
theChildAgrees=!HasOpenContainer(theAncestor);
|
|
}
|
|
|
|
if(theChildAgrees){
|
|
theAncestor=gHTMLElements[aChildTag].mRequiredAncestor;
|
|
if(eHTMLTag_unknown!=theAncestor){
|
|
theChildAgrees=HasOpenContainer(theAncestor);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!(theCanContainResult && theChildAgrees)) {
|
|
if (!CanPropagate(theParentTag,aChildTag)) {
|
|
if(nsHTMLElement::IsContainer(aChildTag)){
|
|
CloseContainersTo(theIndex,theParentTag,PR_TRUE);
|
|
}//if
|
|
else break;
|
|
}//if
|
|
else {
|
|
CreateContextStackFor(aChildTag);
|
|
theIndex=mBodyContext->GetCount();
|
|
}
|
|
}//if
|
|
break;
|
|
case eIgnore:
|
|
default:
|
|
break;
|
|
|
|
}//switch
|
|
} while(!(theCanContainResult && theChildAgrees));
|
|
|
|
|
|
if(nsHTMLElement::IsContainer(aChildTag)){
|
|
result=OpenContainer(aNode,PR_TRUE);
|
|
}
|
|
else { //we're writing a leaf...
|
|
result=AddLeaf(aNode);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
#if 0
|
|
/**
|
|
* This method gets called when a start token has been
|
|
* encountered in the parse process. If the current container
|
|
* can contain this tag, then add it. Otherwise, you have
|
|
* two choices: 1) create an implicit container for this tag
|
|
* to be stored in
|
|
* 2) close the top container, and add this to
|
|
* whatever container ends up on top.
|
|
*
|
|
* @update gess 3/25/98
|
|
* @param aToken -- next (start) token to be handled
|
|
* @param aNode -- CParserNode representing this start token
|
|
* @return PR_TRUE if all went well; PR_FALSE if error occured
|
|
*/
|
|
|
|
nsresult CNavDTD::HandleDefaultStartToken(CToken* aToken,eHTMLTags aChildTag,nsIParserNode& aNode) {
|
|
NS_PRECONDITION(0!=aToken,kNullToken);
|
|
|
|
nsresult result=NS_OK;
|
|
|
|
//Sick as it sounds, I have to make sure the body has been
|
|
//opened before other tags can be added to the content sink...
|
|
|
|
PRBool rickgSkip=PR_FALSE;
|
|
if(!rickgSkip) {
|
|
|
|
/***********************************************************************
|
|
Subtlety alert:
|
|
|
|
The REAL story on when tags are opened is somewhat confusing, but it's
|
|
important to understand. Since this is where we deal with it, it's time
|
|
for a quick disseration. Here goes:
|
|
|
|
Given a stack of open tags, a new (child) tag comes along and we need
|
|
to see if it can be opened in place. There are at least 2 reasons why
|
|
it cannot be opened: 1) the parent says so; 2) the child says so.
|
|
|
|
Parents refuse to take children they KNOW they can't contain. Consider
|
|
that the <HTML> tag is only *supposed* to contain certain tags --
|
|
no one would expect it to accept a rogue <LI> tag (for example).
|
|
|
|
Here's an interested case we should not break:
|
|
|
|
<HTML>
|
|
<BODY>
|
|
<DL>
|
|
<DT>
|
|
<DD>
|
|
<LI>
|
|
<DT>
|
|
|
|
The DT is not a child of the LI, so the LI closes. Then the DT also
|
|
closes the DD, and it's parent DT. At last, it reopens itself below DL.
|
|
|
|
***********************************************************************/
|
|
|
|
eHTMLTags theParentTag=mBodyContext->Last();
|
|
PRBool theCanContainResult=CanContain(theParentTag,aChildTag);
|
|
PRBool theChildAgrees=(theCanContainResult) ? CanBeContained(theParentTag,aChildTag,mBodyContext->mStack) : PR_FALSE;
|
|
|
|
if(!(theCanContainResult && theChildAgrees)) {
|
|
PRInt32 theIndex=FindAutoCloseTargetForStartTag2(aChildTag,mBodyContext->mStack,*this);
|
|
eHTMLTags theTarg2=mBodyContext->TagAt(theIndex);
|
|
eHTMLTags theTarget=FindAutoCloseTargetForStartTag(aChildTag,mBodyContext->mStack);
|
|
|
|
NS_ASSERTION(theTarg2==theTarget,"Error: target mismatch");
|
|
|
|
if(eHTMLTag_unknown!=theTarget){
|
|
result=CloseContainersTo(theTarget,PR_TRUE);
|
|
theParentTag=mBodyContext->Last();
|
|
theCanContainResult=CanContain(theParentTag,aChildTag);
|
|
}
|
|
}
|
|
|
|
if(PR_FALSE==theCanContainResult){
|
|
if(CanPropagate(theParentTag,aChildTag))
|
|
result=CreateContextStackFor(aChildTag);
|
|
else result=kCantPropagate;
|
|
if(NS_OK!=result) {
|
|
//if you're here, then the new topmost container can't contain aToken.
|
|
//You must determine what container hierarchy you need to hold aToken,
|
|
//and create that on the parsestack.
|
|
result=ReduceContextStackFor(aChildTag);
|
|
|
|
PRBool theCanContainResult=CanContain(mBodyContext->Last(),aChildTag);
|
|
|
|
if(PR_FALSE==theCanContainResult) {
|
|
//we unwound too far; now we have to recreate a valid context stack.
|
|
result=CreateContextStackFor(aChildTag);
|
|
}
|
|
}
|
|
}
|
|
|
|
}//if(!rickGSkip)...
|
|
|
|
if(nsHTMLElement::IsContainer(aChildTag)){
|
|
//first, let's see if it's a style element...
|
|
if(!nsHTMLElement::IsStyleTag(aChildTag)) {
|
|
//it wasn't a style container, so open the element container...
|
|
CloseTransientStyles(aChildTag);
|
|
}
|
|
result=OpenContainer(aNode,PR_TRUE);
|
|
}
|
|
else { //we're writing a leaf...
|
|
result=AddLeaf(aNode);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
#endif
|
|
|
|
#ifdef RICKG_DEBUG
|
|
void WriteTokenToLog(CToken* aToken) {
|
|
|
|
static fstream outputStream("c:/temp/tokenlog.html",ios::out);
|
|
aToken->DebugDumpSource(outputStream); //write token without close bracket...
|
|
}
|
|
#endif
|
|
|
|
/**************************************************************
|
|
Define the a functor used to notify observers...
|
|
**************************************************************/
|
|
class nsObserverNotifier: public nsDequeFunctor{
|
|
public:
|
|
nsObserverNotifier(eHTMLTags aTag,PRUint32 aCount,const PRUnichar** aKeys,
|
|
const PRUnichar** aValues,PRUint32 aUniqueKey){
|
|
mCount=aCount;
|
|
mKeys=aKeys;
|
|
mValues=aValues;
|
|
mUniqueKey=aUniqueKey;
|
|
mTag=aTag;
|
|
}
|
|
|
|
virtual void* operator()(void* anObject) {
|
|
nsIElementObserver* theObserver= (nsIElementObserver*)anObject;
|
|
if(theObserver) {
|
|
mResult = theObserver->Notify(mUniqueKey,mTag,mCount,mKeys,mValues);
|
|
}
|
|
if(NS_OK==mResult)
|
|
return 0;
|
|
return anObject;
|
|
}
|
|
|
|
const PRUnichar** mKeys;
|
|
const PRUnichar** mValues;
|
|
PRUint32 mCount;
|
|
PRUint32 mUniqueKey;
|
|
nsresult mResult;
|
|
eHTMLTags mTag;
|
|
};
|
|
|
|
/**
|
|
* This gets called before we've handled a given start tag.
|
|
* It's a generic hook to let us do pre processing.
|
|
* @param aToken contains the tag in question
|
|
* @param aChildTag is the tag itself.
|
|
* @param aNode is the node (tag) with associated attributes.
|
|
* @return TRUE if tag processing should continue; FALSE if the tag has been handled.
|
|
*/
|
|
nsresult CNavDTD::WillHandleStartTag(CToken* aToken,eHTMLTags aTag,nsCParserNode& aNode){
|
|
nsresult result=NS_OK;
|
|
PRInt32 theAttrCount = aNode.GetAttributeCount();
|
|
|
|
//first let's see if there's some skipped content to deal with...
|
|
if(gHTMLElements[aTag].mSkipTarget) {
|
|
result=CollectSkippedContent(aNode,theAttrCount);
|
|
}
|
|
|
|
/**********************************************************
|
|
THIS WILL ULTIMATELY BECOME THE REAL OBSERVER API...
|
|
**********************************************************/
|
|
static CObserverDictionary gObserverDictionary;
|
|
if(aTag < NS_HTML_TAG_MAX){
|
|
nsDeque* theDeque=gObserverDictionary.GetObserversForTag(aTag);
|
|
if(theDeque){
|
|
PRUint32 theDequeSize=theDeque->GetSize();
|
|
if(0<theDequeSize){
|
|
PRInt32 index = 0;
|
|
const PRUnichar* theKeys[50] = {0,0,0,0,0}; // XXX - should be dynamic
|
|
const PRUnichar* theValues[50]= {0,0,0,0,0}; // XXX - should be dynamic
|
|
for(index=0; index<theAttrCount && index < 50; index++) {
|
|
theKeys[index] = aNode.GetKeyAt(index).GetUnicode();
|
|
theValues[index] = aNode.GetValueAt(index).GetUnicode();
|
|
}
|
|
|
|
nsAutoString charsetValue;
|
|
nsCharsetSource charsetSource;
|
|
nsAutoString theCharsetKey("charset");
|
|
nsAutoString theSourceKey("charsetSource");
|
|
nsAutoString intValue;
|
|
|
|
mParser->GetDocumentCharset(charsetValue, charsetSource);
|
|
// Add pseudo attribute in the end
|
|
if(index < 50) {
|
|
theKeys[index]=theCharsetKey.GetUnicode();
|
|
theValues[index] = charsetValue.GetUnicode();
|
|
index++;
|
|
}
|
|
|
|
if(index < 50) {
|
|
theKeys[index]=theSourceKey.GetUnicode();
|
|
PRInt32 sourceInt = charsetSource;
|
|
intValue.Append(sourceInt,10);
|
|
theValues[index] = intValue.GetUnicode();
|
|
index++;
|
|
}
|
|
|
|
CParserContext* pc=mParser->PeekContext();
|
|
void* theDocID=(pc) ? pc-> mKey : 0;
|
|
nsObserverNotifier theNotifier(aTag,index,theKeys,theValues,(PRUint32)theDocID);
|
|
theDeque->FirstThat(theNotifier);
|
|
result=theNotifier.mResult;
|
|
|
|
}//if
|
|
}
|
|
}
|
|
|
|
|
|
if(eHTMLTag_meta==aTag) {
|
|
PRInt32 theCount=aNode.GetAttributeCount();
|
|
if(1<theCount){
|
|
|
|
const nsString& theKey=aNode.GetKeyAt(0);
|
|
#if 0
|
|
//<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
|
|
if(theKey.EqualsIgnoreCase("HTTP-EQUIV")) {
|
|
const nsString& theKey2=aNode.GetKeyAt(1);
|
|
if(theKey2.EqualsIgnoreCase("CONTENT")) {
|
|
nsScanner* theScanner=mParser->GetScanner();
|
|
if(theScanner) {
|
|
const nsString& theValue=aNode.GetValueAt(1);
|
|
PRInt32 charsetValueStart = theValue.RFind("charset=", PR_TRUE ) ;
|
|
if(kNotFound != charsetValueStart) {
|
|
charsetValueStart += 8; // 8 = "charset=".length
|
|
PRInt32 charsetValueEnd = theValue.FindCharInSet("\'\";", charsetValueStart );
|
|
if(kNotFound == charsetValueEnd )
|
|
charsetValueEnd = theValue.Length();
|
|
nsAutoString theCharset;
|
|
theValue.Mid(theCharset, charsetValueStart, charsetValueEnd - charsetValueStart);
|
|
theScanner->SetDocumentCharset(theCharset, kCharsetFromMetaTag);
|
|
// XXX this should be delete after META charset really work
|
|
nsParser::gHackMetaCharset = theCharset;
|
|
} //if
|
|
} //if
|
|
}
|
|
} //if
|
|
|
|
else
|
|
#endif
|
|
if(theKey.EqualsIgnoreCase("NAME")) {
|
|
const nsString& theValue1=aNode.GetValueAt(0);
|
|
if(theValue1.EqualsIgnoreCase("\"CRC\"")) {
|
|
const nsString& theKey2=aNode.GetKeyAt(1);
|
|
if(theKey2.EqualsIgnoreCase("CONTENT")) {
|
|
const nsString& theValue2=aNode.GetValueAt(1);
|
|
PRInt32 err=0;
|
|
mExpectedCRC32=theValue2.ToInteger(&err);
|
|
} //if
|
|
} //if
|
|
} //else
|
|
|
|
} //if
|
|
}//if
|
|
|
|
if(NS_OK==result) {
|
|
result=gHTMLElements[aTag].HasSpecialProperty(kDiscardTag) ? 1 : NS_OK;
|
|
}
|
|
|
|
PRBool isHeadChild=gHTMLElements[eHTMLTag_head].IsChildOfHead(aTag);
|
|
|
|
//this code is here to make sure the head is closed before we deal
|
|
//with any tags that don't belong in the head.
|
|
if(NS_OK==result) {
|
|
if(mHasOpenHead){
|
|
static eHTMLTags skip2[]={eHTMLTag_newline,eHTMLTag_whitespace};
|
|
if(!FindTagInSet(aTag,skip2,sizeof(skip2)/sizeof(eHTMLTag_unknown))){
|
|
if(!isHeadChild){
|
|
CEndToken theToken(eHTMLTag_head);
|
|
nsCParserNode theNode(&theToken,mLineNumber);
|
|
result=CloseHead(theNode);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* This method gets called when a start token has been encountered that the parent
|
|
* wants to omit.
|
|
*
|
|
* @update gess 3/25/98
|
|
* @param aToken -- next (start) token to be handled
|
|
* @param aChildTag -- id of the child in question
|
|
* @param aParent -- id of the parent in question
|
|
* @param aNode -- CParserNode representing this start token
|
|
* @return PR_TRUE if all went well; PR_FALSE if error occured
|
|
*/
|
|
nsresult CNavDTD::HandleOmittedTag(CToken* aToken,eHTMLTags aChildTag,eHTMLTags aParent,nsIParserNode& aNode) {
|
|
nsresult result=NS_OK;
|
|
|
|
// Some tags need no to be opened regardless of what the parent says.
|
|
if(gHTMLElements[aChildTag].HasSpecialProperty(kLegalOpen)) {
|
|
return !NS_OK;
|
|
}
|
|
//The trick here is to see if the parent can contain the child, but prefers not to.
|
|
//Only if the parent CANNOT contain the child should we look to see if it's potentially a child
|
|
//of another section. If it is, the cache it for later.
|
|
// 1. Get the root node for the child. See if the ultimate node is the BODY, FRAMESET, HEAD or HTML
|
|
PRInt32 theTagCount = mBodyContext->GetCount();
|
|
|
|
if(gHTMLElements[aParent].HasSpecialProperty(kBadContentWatch)) {
|
|
eHTMLTags theTag;
|
|
PRInt32 theBCIndex;
|
|
PRBool isNotWhiteSpace = PR_FALSE;
|
|
PRInt32 attrCount = aToken->GetAttributeCount();
|
|
while(theTagCount > 0) {
|
|
theTag = mBodyContext->TagAt(--theTagCount);
|
|
if(!gHTMLElements[theTag].HasSpecialProperty(kBadContentWatch)) {
|
|
if(!gHTMLElements[theTag].CanContain(aChildTag))
|
|
return result;
|
|
theBCIndex = theTagCount;
|
|
break;
|
|
}
|
|
}
|
|
if(!FindTagInSet(aChildTag,gWhitespaceTags,sizeof(gWhitespaceTags)/sizeof(aChildTag))) {
|
|
isNotWhiteSpace = mSaveBadTokens = PR_TRUE;
|
|
}
|
|
if(mSaveBadTokens) {
|
|
mBodyContext->SaveToken(aToken,theBCIndex);
|
|
// If the token is attributed then save those attributes too.
|
|
if(attrCount > 0) {
|
|
nsCParserNode* theAttrNode = (nsCParserNode*)&aNode;
|
|
while(attrCount > 0){
|
|
mBodyContext->SaveToken(theAttrNode->PopAttributeToken(),theBCIndex);
|
|
attrCount--;
|
|
}
|
|
}
|
|
if(!IsContainer(aChildTag) && isNotWhiteSpace) {
|
|
mSaveBadTokens = PR_FALSE;
|
|
}
|
|
result=NS_ERROR_HTMLPARSER_MISPLACED;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* This method gets called when a start token has been
|
|
* encountered in the parse process. If the current container
|
|
* can contain this tag, then add it. Otherwise, you have
|
|
* two choices: 1) create an implicit container for this tag
|
|
* to be stored in
|
|
* 2) close the top container, and add this to
|
|
* whatever container ends up on top.
|
|
*
|
|
* @update gess 1/04/99
|
|
* @param aToken -- next (start) token to be handled
|
|
* @param aNode -- CParserNode representing this start token
|
|
* @return PR_TRUE if all went well; PR_FALSE if error occured
|
|
*/
|
|
nsresult CNavDTD::HandleStartToken(CToken* aToken) {
|
|
NS_PRECONDITION(0!=aToken,kNullToken);
|
|
|
|
#ifdef RICKG_DEBUG
|
|
WriteTokenToLog(aToken);
|
|
#endif
|
|
|
|
//Begin by gathering up attributes...
|
|
eHTMLTags theChildTag=(eHTMLTags)aToken->GetTypeID();
|
|
nsCParserNode attrNode((CHTMLToken*)aToken,mLineNumber,GetTokenRecycler());
|
|
PRInt16 attrCount=aToken->GetAttributeCount();
|
|
nsresult result=(0==attrCount) ? NS_OK : CollectAttributes(attrNode,theChildTag,attrCount);
|
|
eHTMLTags theParent=mBodyContext->Last();
|
|
|
|
if(NS_OK==result) {
|
|
if(NS_OK==WillHandleStartTag(aToken,theChildTag,attrNode)) {
|
|
|
|
if(nsHTMLElement::IsSectionTag(theChildTag)){
|
|
switch(theChildTag){
|
|
case eHTMLTag_body:
|
|
case eHTMLTag_head:
|
|
if(mHadBodyOrFrameset) {
|
|
result=HandleOmittedTag(aToken,theChildTag,theParent,attrNode);
|
|
if(result == NS_OK)
|
|
return result;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
PRBool theHeadIsParent=nsHTMLElement::IsChildOfHead(theChildTag);
|
|
switch(theChildTag) {
|
|
|
|
case eHTMLTag_area:
|
|
if (mHasOpenMap && mSink)
|
|
result=mSink->AddLeaf(attrNode);
|
|
break;
|
|
|
|
case eHTMLTag_comment:
|
|
case eHTMLTag_userdefined:
|
|
break; //drop them on the floor for now...
|
|
|
|
case eHTMLTag_script:
|
|
theHeadIsParent=(!mHasOpenBody); //intentionally fall through...
|
|
mHasOpenScript=PR_TRUE;
|
|
|
|
default:
|
|
{
|
|
if(theHeadIsParent)
|
|
result=AddHeadLeaf(attrNode);
|
|
else if(CanOmit(theParent,theChildTag))
|
|
result=HandleOmittedTag(aToken,theChildTag,theParent,attrNode);
|
|
else result=HandleDefaultStartToken(aToken,theChildTag,attrNode);
|
|
}
|
|
break;
|
|
} //switch
|
|
//now do any post processing necessary on the tag...
|
|
if(NS_OK==result)
|
|
DidHandleStartTag(attrNode,theChildTag);
|
|
}
|
|
} //if
|
|
|
|
if(eHTMLTag_newline==theChildTag)
|
|
mLineNumber++;
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Call this to see if you have a closeable peer on the stack that
|
|
* is ABOVE one of its root tags.
|
|
*
|
|
* @update gess 4/11/99
|
|
* @param aRootTagList -- list of root tags for aTag
|
|
* @param aTag -- tag to test for containership
|
|
* @return PR_TRUE if given tag can contain other tags
|
|
*/
|
|
static
|
|
PRBool HasCloseablePeerAboveRoot(CTagList& aRootTagList,nsEntryStack& aTagStack,eHTMLTags aTag,PRBool anEndTag) {
|
|
PRInt32 theRootIndex=aRootTagList.GetTopmostIndexOf(aTagStack);
|
|
CTagList* theCloseTags=(anEndTag) ? gHTMLElements[aTag].GetAutoCloseEndTags() : gHTMLElements[aTag].GetAutoCloseStartTags();
|
|
PRInt32 theChildIndex=-1;
|
|
PRBool result=PR_FALSE;
|
|
if(theCloseTags) {
|
|
theChildIndex=theCloseTags->GetTopmostIndexOf(aTagStack);
|
|
}
|
|
else {
|
|
if((anEndTag) || (!gHTMLElements[aTag].CanContainSelf()))
|
|
theChildIndex=aTagStack.GetTopmostIndexOf(aTag);
|
|
}
|
|
// I changed this to theRootIndex<=theChildIndex so to handle this case:
|
|
// <SELECT><OPTGROUP>...</OPTGROUP>
|
|
//
|
|
return PRBool(theRootIndex<=theChildIndex);
|
|
}
|
|
|
|
|
|
/**
|
|
* This method is called to determine whether or not an END tag
|
|
* can be autoclosed. This means that based on the current
|
|
* context, the stack should be closed to the nearest matching
|
|
* tag.
|
|
*
|
|
* @param aTag -- tag enum of child to be tested
|
|
* @return PR_TRUE if autoclosure should occur
|
|
*/
|
|
static
|
|
eHTMLTags FindAutoCloseTargetForEndTag(eHTMLTags aCurrentTag,nsEntryStack& aTagStack) {
|
|
int theTopIndex=aTagStack.GetCount();
|
|
eHTMLTags thePrevTag=aTagStack.Last();
|
|
|
|
if(nsHTMLElement::IsContainer(aCurrentTag)){
|
|
PRInt32 theChildIndex=GetIndexOfChildOrSynonym(aTagStack,aCurrentTag);
|
|
|
|
if(kNotFound<theChildIndex) {
|
|
if(thePrevTag==aTagStack[theChildIndex]){
|
|
return aTagStack[theChildIndex];
|
|
}
|
|
|
|
if(nsHTMLElement::IsBlockCloser(aCurrentTag)) {
|
|
|
|
/*here's what to do:
|
|
Our here is sitting at aChildIndex. There are other tags above it
|
|
on the stack. We have to try to close them out, but we may encounter
|
|
one that can block us. The way to tell is by comparing each tag on
|
|
the stack against our closeTag and rootTag list.
|
|
|
|
For each tag above our hero on the stack, ask 3 questions:
|
|
1. Is it in the closeTag list? If so, the we can skip over it
|
|
2. Is it in the rootTag list? If so, then we're gated by it
|
|
3. Otherwise its non-specified and we simply presume we can close it.
|
|
*/
|
|
|
|
CTagList* theCloseTags=gHTMLElements[aCurrentTag].GetAutoCloseEndTags();
|
|
CTagList* theRootTags=gHTMLElements[aCurrentTag].GetEndRootTags();
|
|
|
|
if(theCloseTags){
|
|
//at a min., this code is needed for H1..H6
|
|
|
|
while(theChildIndex<--theTopIndex) {
|
|
eHTMLTags theNextTag=aTagStack[theTopIndex];
|
|
if(PR_FALSE==theCloseTags->Contains(theNextTag)) {
|
|
if(PR_TRUE==theRootTags->Contains(theNextTag)) {
|
|
return eHTMLTag_unknown; //we encountered a tag in root list so fail (because we're gated).
|
|
}
|
|
//otherwise presume it's something we can simply ignore and continue search...
|
|
}
|
|
//otherwise its in the close list so skip to next tag...
|
|
}
|
|
return aCurrentTag; //if you make it here, we're ungated and found a target!
|
|
}//if
|
|
else if(theRootTags) {
|
|
//since we didn't find any close tags, see if there is an instance of aCurrentTag
|
|
//above the stack from the roottag.
|
|
if(HasCloseablePeerAboveRoot(*theRootTags,aTagStack,aCurrentTag,PR_TRUE))
|
|
return aCurrentTag;
|
|
else return eHTMLTag_unknown;
|
|
}
|
|
} //if blockcloser
|
|
else{
|
|
//Ok, a much more sensible approach for non-block closers; use the tag group to determine closure:
|
|
//For example: %phrasal closes %phrasal, %fontstyle and %special
|
|
return gHTMLElements[aCurrentTag].GetCloseTargetForEndTag(aTagStack,theChildIndex);
|
|
}
|
|
}//if
|
|
} //if
|
|
return eHTMLTag_unknown;
|
|
}
|
|
|
|
/**
|
|
* This method gets called when an end token has been
|
|
* encountered in the parse process. If the end tag matches
|
|
* the start tag on the stack, then simply close it. Otherwise,
|
|
* we have a erroneous state condition. This can be because we
|
|
* have a close tag with no prior open tag (user error) or because
|
|
* we screwed something up in the parse process. I'm not sure
|
|
* yet how to tell the difference.
|
|
*
|
|
* @update gess 3/25/98
|
|
* @param aToken -- next (start) token to be handled
|
|
* @return PR_TRUE if all went well; PR_FALSE if error occured
|
|
*/
|
|
nsresult CNavDTD::HandleEndToken(CToken* aToken) {
|
|
NS_PRECONDITION(0!=aToken,kNullToken);
|
|
|
|
nsresult result=NS_OK;
|
|
eHTMLTags theChildTag=(eHTMLTags)aToken->GetTypeID();
|
|
nsCParserNode theNode((CHTMLToken*)aToken,mLineNumber);
|
|
|
|
#ifdef RICKG_DEBUG
|
|
WriteTokenToLog(aToken);
|
|
#endif
|
|
|
|
switch(theChildTag) {
|
|
|
|
case eHTMLTag_script:
|
|
mHasOpenScript=PR_FALSE;
|
|
case eHTMLTag_style:
|
|
case eHTMLTag_link:
|
|
case eHTMLTag_meta:
|
|
case eHTMLTag_textarea:
|
|
case eHTMLTag_title:
|
|
break;
|
|
|
|
case eHTMLTag_map:
|
|
case eHTMLTag_form:
|
|
case eHTMLTag_head:
|
|
result=CloseContainer(theNode,theChildTag,PR_FALSE);
|
|
break;
|
|
|
|
case eHTMLTag_br:
|
|
{
|
|
CToken* theToken=(CHTMLToken*)gRecycler->CreateTokenOfType(eToken_start,eHTMLTag_br);
|
|
result=HandleStartToken(theToken);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
//now check to see if this token should be omitted, or
|
|
//if it's gated from closing by the presence of another tag.
|
|
if((gHTMLElements[theChildTag].CanOmitEndTag()) || (kNotFound==GetIndexOfChildOrSynonym(mBodyContext->mStack,theChildTag))) {
|
|
UpdateStyleStackForCloseTag(theChildTag,theChildTag);
|
|
}
|
|
else {
|
|
eHTMLTags theTarget=FindAutoCloseTargetForEndTag(theChildTag,mBodyContext->mStack);
|
|
if(gHTMLElements[theTarget].HasSpecialProperty(kBadContentWatch)){
|
|
result = HandleSavedTokensAbove(theTarget);
|
|
}
|
|
if(eHTMLTag_unknown!=theTarget) {
|
|
result=CloseContainersTo(theTarget,PR_FALSE);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* This method will be triggered when the end of a table is
|
|
* encountered. Its primary purpose is to process all the
|
|
* bad-contents pertaining a particular table. The position
|
|
* of the table is the token bank ID.
|
|
*
|
|
* @update harishd 03/24/99
|
|
* @param aTag - This ought to be a table tag
|
|
*
|
|
*/
|
|
|
|
nsresult CNavDTD::HandleSavedTokensAbove(eHTMLTags aTag)
|
|
{
|
|
NS_PRECONDITION(mBodyContext != nsnull && mBodyContext->GetCount() > 0,"invalid context");
|
|
|
|
CToken* theToken;
|
|
eHTMLTags theTag;
|
|
PRInt32 attrCount;
|
|
nsresult result = NS_OK;
|
|
PRInt32 theTopIndex = GetTopmostIndexOf(aTag);
|
|
PRInt32 theTagCount = mBodyContext->GetCount();
|
|
|
|
if(theTopIndex == kNotFound)
|
|
return result;
|
|
|
|
PRInt32 theBadContentIndex = theTopIndex - 1;
|
|
PRInt32 theBadTokenCount = mBodyContext->TokenCountAt(theBadContentIndex);
|
|
|
|
if(theBadTokenCount > 0) {
|
|
// Pause the main context and switch to the new context.
|
|
mSink->BeginContext(theBadContentIndex);
|
|
|
|
nsDTDContext temp;
|
|
|
|
// The body context should contain contents only upto the marked position.
|
|
for(PRInt32 i=0; i<(theTagCount - theTopIndex); i++)
|
|
temp.Push(mBodyContext->Pop());
|
|
|
|
// Now flush out all the bad contents.
|
|
while(theBadTokenCount > 0){
|
|
theToken = mBodyContext->RestoreTokenFrom(theBadContentIndex);
|
|
if(theToken) {
|
|
theTag = (eHTMLTags)theToken->GetTypeID();
|
|
if(theTag != eHTMLTag_unknown) {
|
|
attrCount = theToken->GetAttributeCount();
|
|
// Put back attributes, which once got popped out, into the tokenizer
|
|
for(PRInt32 i=0;i<attrCount; i++){
|
|
CToken* theAttrToken = mBodyContext->RestoreTokenFrom(theBadContentIndex);
|
|
if(theAttrToken) {
|
|
mTokenizer->PushTokenFront(theAttrToken);
|
|
}
|
|
theBadTokenCount--;
|
|
}
|
|
result = HandleStartToken(theToken);
|
|
}
|
|
}
|
|
theBadTokenCount--;
|
|
}
|
|
if(theTopIndex != mBodyContext->GetCount()) {
|
|
CloseContainersTo(theTopIndex,mBodyContext->TagAt(theTopIndex),PR_TRUE);
|
|
}
|
|
|
|
// Bad-contents were successfully processed. Now, itz time to get
|
|
// back to the original body context state.
|
|
for(PRInt32 j=0; j<(theTagCount - theTopIndex); j++)
|
|
mBodyContext->Push(temp.Pop());
|
|
|
|
// Terminate the new context and switch back to the main context
|
|
mSink->EndContext(theBadContentIndex);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* This method gets called when an entity token has been
|
|
* encountered in the parse process.
|
|
*
|
|
* @update gess 3/25/98
|
|
* @param aToken -- next (start) token to be handled
|
|
* @return PR_TRUE if all went well; PR_FALSE if error occured
|
|
*/
|
|
nsresult CNavDTD::HandleEntityToken(CToken* aToken) {
|
|
NS_PRECONDITION(0!=aToken,kNullToken);
|
|
|
|
CEntityToken* et = (CEntityToken*)(aToken);
|
|
nsresult result=NS_OK;
|
|
|
|
if(PR_FALSE==CanOmit(mBodyContext->Last(),eHTMLTag_entity)) {
|
|
nsCParserNode aNode((CHTMLToken*)aToken,mLineNumber);
|
|
|
|
#ifdef RICKG_DEBUG
|
|
WriteTokenToLog(aToken);
|
|
#endif
|
|
|
|
result=AddLeaf(aNode);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* This method gets called when a comment token has been
|
|
* encountered in the parse process. After making sure
|
|
* we're somewhere in the body, we handle the comment
|
|
* in the same code that we use for text.
|
|
*
|
|
* @update gess 3/25/98
|
|
* @param aToken -- next (start) token to be handled
|
|
* @return PR_TRUE if all went well; PR_FALSE if error occured
|
|
*/
|
|
nsresult CNavDTD::HandleCommentToken(CToken* aToken) {
|
|
NS_PRECONDITION(0!=aToken,kNullToken);
|
|
nsCParserNode aNode((CHTMLToken*)aToken,mLineNumber);
|
|
|
|
#ifdef RICKG_DEBUG
|
|
WriteTokenToLog(aToken);
|
|
#endif
|
|
|
|
// You may find this hard to beleive, but this has to be here
|
|
// so that the TBODY doesnt die when it sees a comment.
|
|
// This case occurs on WWW.CREAF.COM
|
|
eHTMLTags theTag=mBodyContext->mStack.Last();
|
|
nsresult result=NS_OK;
|
|
|
|
switch(theTag) {
|
|
case eHTMLTag_table:
|
|
case eHTMLTag_tr:
|
|
case eHTMLTag_tbody:
|
|
case eHTMLTag_td:
|
|
break;
|
|
default:
|
|
if(mHasOpenBody){
|
|
result=(mSink) ? mSink->AddComment(aNode) : NS_OK;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
/**
|
|
* This method gets called when an attribute token has been
|
|
* encountered in the parse process. This is an error, since
|
|
* all attributes should have been accounted for in the prior
|
|
* start or end tokens
|
|
*
|
|
* @update gess 3/25/98
|
|
* @param aToken -- next (start) token to be handled
|
|
* @return PR_TRUE if all went well; PR_FALSE if error occured
|
|
*/
|
|
nsresult CNavDTD::HandleAttributeToken(CToken* aToken) {
|
|
NS_PRECONDITION(0!=aToken,kNullToken);
|
|
NS_ERROR("attribute encountered -- this shouldn't happen!");
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
/**
|
|
* This method gets called when a script token has been
|
|
* encountered in the parse process. n
|
|
*
|
|
* @update gess 3/25/98
|
|
* @param aToken -- next (start) token to be handled
|
|
* @return PR_TRUE if all went well; PR_FALSE if error occured
|
|
*/
|
|
nsresult CNavDTD::HandleScriptToken(nsCParserNode& aNode) {
|
|
PRInt32 pos=GetTopmostIndexOf(eHTMLTag_body);
|
|
PRInt32 attrCount=aNode.GetAttributeCount(PR_TRUE);
|
|
|
|
nsresult result=AddLeaf(aNode);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* This method gets called when a style token has been
|
|
* encountered in the parse process.
|
|
*
|
|
* @update gess 3/25/98
|
|
* @param aToken -- next (start) token to be handled
|
|
* @return PR_TRUE if all went well; PR_FALSE if error occured
|
|
*/
|
|
nsresult CNavDTD::HandleStyleToken(CToken* aToken){
|
|
NS_PRECONDITION(0!=aToken,kNullToken);
|
|
|
|
// CStyleToken* st = (CStyleToken*)(aToken);
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
/**
|
|
* This method gets called when an "instruction" token has been
|
|
* encountered in the parse process.
|
|
*
|
|
* @update gess 3/25/98
|
|
* @param aToken -- next (start) token to be handled
|
|
* @return PR_TRUE if all went well; PR_FALSE if error occured
|
|
*/
|
|
nsresult CNavDTD::HandleProcessingInstructionToken(CToken* aToken){
|
|
NS_PRECONDITION(0!=aToken,kNullToken);
|
|
nsCParserNode aNode((CHTMLToken*)aToken,mLineNumber);
|
|
|
|
#ifdef RICKG_DEBUG
|
|
WriteTokenToLog(aToken);
|
|
#endif
|
|
|
|
nsresult result=(mSink) ? mSink->AddProcessingInstruction(aNode) : NS_OK;
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Retrieve the attributes for this node, and add then into
|
|
* the node.
|
|
*
|
|
* @update gess4/22/98
|
|
* @param aNode is the node you want to collect attributes for
|
|
* @param aCount is the # of attributes you're expecting
|
|
* @return error code (should be 0)
|
|
*/
|
|
nsresult CNavDTD::CollectAttributes(nsCParserNode& aNode,eHTMLTags aTag,PRInt32 aCount){
|
|
int attr=0;
|
|
|
|
nsresult result=NS_OK;
|
|
int theAvailTokenCount=mTokenizer->GetCount() + mSkippedContent.GetSize();
|
|
if(aCount<=theAvailTokenCount) {
|
|
CToken* theToken=0;
|
|
eHTMLTags theSkipTarget=gHTMLElements[aTag].mSkipTarget;
|
|
for(attr=0;attr<aCount;attr++){
|
|
if((eHTMLTag_unknown!=theSkipTarget) && mSkippedContent.GetSize())
|
|
theToken=(CToken*)mSkippedContent.PopFront();
|
|
else theToken=mTokenizer->PopToken();
|
|
if(theToken) {
|
|
|
|
#ifdef RICKG_DEBUG
|
|
WriteTokenToLog(theToken);
|
|
#endif
|
|
|
|
aNode.AddAttribute(theToken);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
result=kEOF;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
/**
|
|
* Causes the next skipped-content token (if any) to
|
|
* be consumed by this node.
|
|
* @update gess5/11/98
|
|
* @param node to consume skipped-content
|
|
* @param holds the number of skipped content elements encountered
|
|
* @return Error condition.
|
|
*/
|
|
nsresult CNavDTD::CollectSkippedContent(nsCParserNode& aNode,PRInt32& aCount) {
|
|
|
|
CTokenRecycler* theRecycler=(CTokenRecycler*)mTokenizer->GetTokenRecycler();
|
|
|
|
int aIndex=0;
|
|
int aMax=mSkippedContent.GetSize();
|
|
nsAutoString theTempStr;
|
|
nsAutoString theStr;
|
|
for(aIndex=0;aIndex<aMax;aIndex++){
|
|
CHTMLToken* theNextToken=(CHTMLToken*)mSkippedContent.PopFront();
|
|
theNextToken->GetSource(theTempStr);
|
|
theStr+=theTempStr;
|
|
theRecycler->RecycleToken(theNextToken);
|
|
}
|
|
aNode.SetSkippedContent(theStr);
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
/**
|
|
* Finds a tag handler for the given tag type.
|
|
*
|
|
* @update gess 4/2/98
|
|
* @param aTagType type of tag to be handled
|
|
* @return valid tag handler (if found) or null
|
|
*/
|
|
CITokenHandler* CNavDTD::GetTokenHandler(eHTMLTokenTypes aType) const {
|
|
CITokenHandler* result=0;
|
|
if((aType>0) && (aType<eToken_last)) {
|
|
result=mTokenHandlers[aType];
|
|
}
|
|
else {
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
/***********************************************************************************
|
|
The preceeding tables determine the set of elements each tag can contain...
|
|
***********************************************************************************/
|
|
|
|
/**
|
|
* This method is called to determine whether or not a tag
|
|
* of one type can contain a tag of another type.
|
|
*
|
|
* @update gess 4/8/98
|
|
* @param aParent -- tag enum of parent container
|
|
* @param aChild -- tag enum of child container
|
|
* @return PR_TRUE if parent can contain child
|
|
*/
|
|
PRBool CNavDTD::CanContain(PRInt32 aParent,PRInt32 aChild) const {
|
|
if(mHasOpenForm) {
|
|
if(aParent==aChild)
|
|
return gHTMLElements[aParent].CanContainSelf();
|
|
if(FindTagInSet(aChild,gFormElementTags,sizeof(gFormElementTags)/sizeof(eHTMLTag_unknown))) {
|
|
return PR_TRUE;
|
|
}//if
|
|
}//if
|
|
if(aChild == eHTMLTag_script)
|
|
return PR_TRUE;
|
|
return gHTMLElements[aParent].CanContain((eHTMLTags)aChild);
|
|
}
|
|
|
|
/**
|
|
* This method is called to determine whether or not
|
|
* the necessary intermediate tags should be propagated
|
|
* between the given parent and given child.
|
|
*
|
|
* @update gess 4/8/98
|
|
* @param aParent -- tag enum of parent container
|
|
* @param aChild -- tag enum of child container
|
|
* @return PR_TRUE if propagation should occur
|
|
*/
|
|
PRBool CNavDTD::CanPropagate(eHTMLTags aParentTag,eHTMLTags aChildTag) const {
|
|
PRBool result=PR_FALSE;
|
|
PRBool parentCanContain=CanContain(aParentTag,aChildTag);
|
|
|
|
if(aParentTag==aChildTag) {
|
|
return result;
|
|
}
|
|
|
|
int thePropLevel=0;
|
|
if(nsHTMLElement::IsContainer(aChildTag)){
|
|
if(!gHTMLElements[aChildTag].HasSpecialProperty(kNoPropagate)){
|
|
if(nsHTMLElement::IsBlockParent(aParentTag) || (gHTMLElements[aParentTag].GetSpecialChildren())) {
|
|
while(eHTMLTag_unknown!=aChildTag) {
|
|
if(parentCanContain){
|
|
result=PR_TRUE;
|
|
break;
|
|
}//if
|
|
CTagList* theTagList=gHTMLElements[aChildTag].GetRootTags();
|
|
aChildTag=theTagList->GetTagAt(0);
|
|
parentCanContain=CanContain(aParentTag,aChildTag);
|
|
++thePropLevel;
|
|
}//while
|
|
}//if
|
|
}//if
|
|
if(thePropLevel>gHTMLElements[aParentTag].mPropagateRange)
|
|
result=PR_FALSE;
|
|
}//if
|
|
else result=parentCanContain;
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* This method gets called to determine whether a given
|
|
* tag can be omitted from opening. Most cannot.
|
|
*
|
|
* @update gess 3/25/98
|
|
* @param aTag -- tag to test for containership
|
|
* @return PR_TRUE if given tag can contain other tags
|
|
*/
|
|
PRBool CNavDTD::CanOmit(eHTMLTags aParent,eHTMLTags aChild) const {
|
|
|
|
eHTMLTags theAncestor=gHTMLElements[aChild].mExcludingAncestor;
|
|
if(eHTMLTag_unknown!=theAncestor){
|
|
if(HasOpenContainer(theAncestor))
|
|
return PR_TRUE;
|
|
}
|
|
|
|
theAncestor=gHTMLElements[aChild].mRequiredAncestor;
|
|
if(eHTMLTag_unknown!=theAncestor){
|
|
return !HasOpenContainer(theAncestor);
|
|
}
|
|
|
|
if(gHTMLElements[aParent].HasSpecialProperty(kOmitWS)) {
|
|
if(nsHTMLElement::IsWhitespaceTag(aChild)) {
|
|
return PR_TRUE;
|
|
}
|
|
}
|
|
|
|
if(gHTMLElements[aParent].CanExclude(aChild)){
|
|
return PR_TRUE;
|
|
}
|
|
|
|
//Now the obvious test: if the parent can contain the child, don't omit.
|
|
if(CanContain(aParent,aChild) || (aChild==aParent)){
|
|
return PR_FALSE;
|
|
}
|
|
|
|
if(nsHTMLElement::IsBlockEntity(aParent)) {
|
|
if(nsHTMLElement::IsInlineEntity(aChild)) { //feel free to drop inlines that a block doesn't contain.
|
|
return PR_TRUE;
|
|
}
|
|
}
|
|
|
|
if(gHTMLElements[aParent].HasSpecialProperty(kBadContentWatch)) {
|
|
if(!gHTMLElements[aParent].CanContain(aChild)){
|
|
return PR_TRUE;
|
|
}
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|
|
|
|
/**
|
|
* This method gets called to determine whether a given
|
|
* tag is itself a container
|
|
*
|
|
* @update gess 4/8/98
|
|
* @param aTag -- tag to test as a container
|
|
* @return PR_TRUE if given tag can contain other tags
|
|
*/
|
|
PRBool CNavDTD::IsContainer(PRInt32 aTag) const {
|
|
return nsHTMLElement::IsContainer((eHTMLTags)aTag);
|
|
}
|
|
|
|
|
|
/**
|
|
* This method tries to design a context vector (without actually
|
|
* changing our parser state) from the parent down to the
|
|
* child.
|
|
*
|
|
* @update gess4/6/98
|
|
* @param aVector is the string where we store our output vector
|
|
* in bottom-up order.
|
|
* @param aParent -- tag type of parent
|
|
* @param aChild -- tag type of child
|
|
* @return TRUE if propagation closes; false otherwise
|
|
*/
|
|
PRBool CNavDTD::ForwardPropagate(nsEntryStack& aStack,eHTMLTags aParentTag,eHTMLTags aChildTag) {
|
|
PRBool result=PR_FALSE;
|
|
|
|
switch(aParentTag) {
|
|
case eHTMLTag_table:
|
|
{
|
|
static eHTMLTags tableTags[]={eHTMLTag_tr,eHTMLTag_td};
|
|
if(FindTagInSet(aChildTag,tableTags,sizeof(tableTags)/sizeof(eHTMLTag_unknown))) {
|
|
return BackwardPropagate(aStack,aParentTag,aChildTag);
|
|
}
|
|
}
|
|
//otherwise, intentionally fall through...
|
|
|
|
case eHTMLTag_tr:
|
|
{
|
|
PRBool theCanContainResult=CanContain(eHTMLTag_td,aChildTag);
|
|
if(PR_TRUE==theCanContainResult) {
|
|
aStack.Push(eHTMLTag_td);
|
|
result=BackwardPropagate(aStack,aParentTag,eHTMLTag_td);
|
|
// result=PR_TRUE;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case eHTMLTag_th:
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}//switch
|
|
return result;
|
|
}
|
|
|
|
|
|
/**
|
|
* This method tries to design a context map (without actually
|
|
* changing our parser state) from the child up to the parent.
|
|
*
|
|
* @update gess4/6/98
|
|
* @param aVector is the string where we store our output vector
|
|
* in bottom-up order.
|
|
* @param aParent -- tag type of parent
|
|
* @param aChild -- tag type of child
|
|
* @return TRUE if propagation closes; false otherwise
|
|
*/
|
|
PRBool CNavDTD::BackwardPropagate(nsEntryStack& aStack,eHTMLTags aParentTag,eHTMLTags aChildTag) const {
|
|
|
|
eHTMLTags theParentTag=aChildTag;
|
|
|
|
do {
|
|
CTagList* theRootTags=gHTMLElements[theParentTag].GetRootTags();
|
|
theParentTag=theRootTags->GetTagAt(0);
|
|
if(theParentTag!=eHTMLTag_unknown) {
|
|
aStack.Push(theParentTag);
|
|
}
|
|
|
|
PRBool theCanContainResult=CanContain(aParentTag,theParentTag);
|
|
|
|
if(theCanContainResult) {
|
|
//we've found a complete sequence, so push the parent...
|
|
theParentTag=aParentTag;
|
|
aStack.Push(theParentTag);
|
|
}
|
|
} while((theParentTag!=eHTMLTag_unknown) && (theParentTag!=aParentTag));
|
|
|
|
return PRBool(aParentTag==theParentTag);
|
|
}
|
|
|
|
|
|
/**
|
|
* This method allows the caller to determine if a given container
|
|
* is currently open
|
|
*
|
|
* @update gess 11/9/98
|
|
* @param
|
|
* @return
|
|
*/
|
|
PRBool CNavDTD::HasOpenContainer(eHTMLTags aContainer) const {
|
|
PRBool result=PR_FALSE;
|
|
|
|
switch(aContainer) {
|
|
case eHTMLTag_form:
|
|
result=mHasOpenForm; break;
|
|
case eHTMLTag_map:
|
|
result=mHasOpenMap; break;
|
|
default:
|
|
result=(kNotFound!=GetTopmostIndexOf(aContainer)); break;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* This method allows the caller to determine if a any member
|
|
* in a set of tags is currently open
|
|
*
|
|
* @update gess 11/9/98
|
|
* @param
|
|
* @return
|
|
*/
|
|
PRBool CNavDTD::HasOpenContainer(const eHTMLTags aTagSet[],PRInt32 aCount) const {
|
|
|
|
int theIndex;
|
|
int theTopIndex=mBodyContext->GetCount()-1;
|
|
|
|
for(theIndex=theTopIndex;theIndex>0;theIndex--){
|
|
if(FindTagInSet((*mBodyContext)[theIndex],aTagSet,aCount))
|
|
return PR_TRUE;
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|
|
/**
|
|
* This method retrieves the HTMLTag type of the topmost
|
|
* container on the stack.
|
|
*
|
|
* @update gess 4/2/98
|
|
* @return tag id of topmost node in contextstack
|
|
*/
|
|
eHTMLTags CNavDTD::GetTopNode() const {
|
|
return mBodyContext->Last();
|
|
}
|
|
|
|
/**
|
|
* Finds the topmost occurance of given tag within context vector stack.
|
|
* @update gess5/11/98
|
|
* @param tag to be found
|
|
* @return index of topmost tag occurance -- may be -1 (kNotFound).
|
|
*/
|
|
static
|
|
PRInt32 GetTopmostIndexOfBelowOffset(eHTMLTags aTag,PRInt32 anOffset){
|
|
PRInt32 result=-1;
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Determine whether the given tag is open anywhere
|
|
* in our context stack.
|
|
*
|
|
* @update gess 4/2/98
|
|
* @param eHTMLTags tag to be searched for in stack
|
|
* @return topmost index of tag on stack
|
|
*/
|
|
PRInt32 CNavDTD::GetTopmostIndexOf(eHTMLTags aTagSet[],PRInt32 aCount) const {
|
|
int theIndex=0;
|
|
for(theIndex=mBodyContext->GetCount()-1;theIndex>=0;theIndex--){
|
|
if(FindTagInSet((*mBodyContext)[theIndex],aTagSet,aCount)) {
|
|
return theIndex;
|
|
}
|
|
}
|
|
return kNotFound;
|
|
}
|
|
|
|
/**
|
|
* Determine whether the given tag is open anywhere
|
|
* in our context stack.
|
|
*
|
|
* @update gess 4/2/98
|
|
* @param eHTMLTags tag to be searched for in stack
|
|
* @return topmost index of tag on stack
|
|
*/
|
|
PRInt32 CNavDTD::GetTopmostIndexOf(eHTMLTags aTag) const {
|
|
return mBodyContext->mStack.GetTopmostIndexOf(aTag);
|
|
}
|
|
|
|
/*********************************************
|
|
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)
|
|
*/
|
|
nsresult CNavDTD::OpenTransientStyles(eHTMLTags aChildTag){
|
|
nsresult result=NS_OK;
|
|
|
|
//later, change this so that transients only open in containers that get leaked in to.
|
|
|
|
#ifdef ENABLE_RESIDUALSTYLE
|
|
eHTMLTags theParentTag=mBodyContext->Last();
|
|
if(!gHTMLElements[theParentTag].HasSpecialProperty(kNoStyleLeaksIn)) {
|
|
if(!FindTagInSet(aChildTag,gWhitespaceTags,sizeof(gWhitespaceTags)/sizeof(aChildTag))){
|
|
//the following code builds the set of style tags to be opened...
|
|
PRUint32 theCount=mBodyContext->GetCount();
|
|
PRUint32 theIndex=0;
|
|
for(theIndex=0;theIndex<theCount;theIndex++){
|
|
nsEntryStack* theStack=mBodyContext->GetStylesAt(theIndex);
|
|
if(theStack){
|
|
PRUint32 scount=theStack->GetCount();
|
|
PRUint32 sindex=0;
|
|
for(sindex=0;sindex<scount;sindex++){
|
|
eHTMLTags theTag=theStack->TagAt(sindex);
|
|
|
|
if(theTag==eHTMLTag_font) //XXX HACK DEBUG!
|
|
theTag=eHTMLTag_b;
|
|
|
|
CStartToken theToken(theTag);
|
|
nsCParserNode theNode(&theToken,mLineNumber);
|
|
theToken.SetTypeID(theTag);
|
|
result=OpenContainer(theNode,PR_FALSE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* It is with great trepidation that I offer this method (privately of course).
|
|
* The gets called just prior when a container gets opened. This methods job is to
|
|
* take a look at the (transient) style stack, and <i>close</i> 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)
|
|
*/
|
|
nsresult CNavDTD::CloseTransientStyles(eHTMLTags aChildTag){
|
|
nsresult result=NS_OK;
|
|
|
|
int theTagPos=0;
|
|
//now iterate style set, and close the containers...
|
|
|
|
#ifdef ENABLE_RESIDUALSTYLE
|
|
nsDeque* theStyleDeque=mBodyContext->GetStyles();
|
|
for(theTagPos=mBodyContext->mOpenStyles;theTagPos>0;theTagPos--){
|
|
eHTMLTags theTag=GetTopNode();
|
|
CStartToken token(theTag);
|
|
nsCParserNode theNode(&token,mLineNumber);
|
|
token.SetTypeID(theTag);
|
|
result=CloseContainer(theNode,theTag,PR_FALSE);
|
|
}
|
|
#endif
|
|
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
|
|
*/
|
|
nsresult CNavDTD::OpenHTML(const nsIParserNode& aNode){
|
|
NS_PRECONDITION(mBodyContext->GetCount() >= 0, kInvalidTagStackPos);
|
|
|
|
nsresult result=(mSink) ? mSink->OpenHTML(aNode) : NS_OK;
|
|
mBodyContext->Push((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
|
|
*/
|
|
nsresult CNavDTD::CloseHTML(const nsIParserNode& aNode){
|
|
NS_PRECONDITION(mBodyContext->GetCount() > 0, kInvalidTagStackPos);
|
|
nsresult result=(mSink) ? mSink->CloseHTML(aNode) : NS_OK;
|
|
mBodyContext->Pop();
|
|
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
|
|
*/
|
|
nsresult CNavDTD::OpenHead(const nsIParserNode& aNode){
|
|
//mBodyContext->Push(eHTMLTag_head);
|
|
if(!mHasOpenHead++) {
|
|
nsresult result=(mSink) ? mSink->OpenHead(aNode) : NS_OK;
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
*/
|
|
nsresult CNavDTD::CloseHead(const nsIParserNode& aNode){
|
|
if(mHasOpenHead) {
|
|
if(0==--mHasOpenHead){
|
|
nsresult result=(mSink) ? mSink->CloseHead(aNode) : NS_OK;
|
|
}
|
|
}
|
|
//mBodyContext->Pop();
|
|
return NS_OK;
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
*/
|
|
nsresult CNavDTD::OpenBody(const nsIParserNode& aNode){
|
|
NS_PRECONDITION(mBodyContext->GetCount() >= 0, kInvalidTagStackPos);
|
|
|
|
nsresult result=NS_OK;
|
|
|
|
PRInt32 theHTMLPos=GetTopmostIndexOf(eHTMLTag_html);
|
|
if(kNotFound==theHTMLPos){ //someone forgot to open HTML. Let's do it for them.
|
|
nsAutoString theEmpty;
|
|
CHTMLToken token(theEmpty,eHTMLTag_html);
|
|
nsCParserNode htmlNode(&token,mLineNumber);
|
|
result=OpenHTML(htmlNode); //open the html container...
|
|
theHTMLPos=GetTopmostIndexOf(eHTMLTag_html);
|
|
}
|
|
|
|
PRBool theBodyIsOpen=HasOpenContainer(eHTMLTag_body);
|
|
if(!theBodyIsOpen){
|
|
//body is not already open, but head may be so close it
|
|
result=CloseContainersTo(theHTMLPos+1,eHTMLTag_body,PR_TRUE); //close current stack containers.
|
|
}
|
|
|
|
if(NS_OK==result) {
|
|
result=(mSink) ? mSink->OpenBody(aNode) : NS_OK;
|
|
if(!theBodyIsOpen) {
|
|
mBodyContext->Push((eHTMLTags)aNode.GetNodeType());
|
|
mTokenizer->PrependTokens(mMisplacedContent);
|
|
}
|
|
}
|
|
|
|
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
|
|
*/
|
|
nsresult CNavDTD::CloseBody(const nsIParserNode& aNode){
|
|
NS_PRECONDITION(mBodyContext->GetCount() >= 0, kInvalidTagStackPos);
|
|
nsresult result=(mSink) ? mSink->CloseBody(aNode) : NS_OK;
|
|
mBodyContext->Pop();
|
|
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
|
|
*/
|
|
nsresult CNavDTD::OpenForm(const nsIParserNode& aNode){
|
|
if(mHasOpenForm)
|
|
CloseForm(aNode);
|
|
nsresult result=(mSink) ? mSink->OpenForm(aNode) : NS_OK;
|
|
if(NS_OK==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
|
|
*/
|
|
nsresult CNavDTD::CloseForm(const nsIParserNode& aNode){
|
|
NS_PRECONDITION(mBodyContext->GetCount() > 0, kInvalidTagStackPos);
|
|
nsresult result=NS_OK;
|
|
if(mHasOpenForm) {
|
|
mHasOpenForm=PR_FALSE;
|
|
result=(mSink) ? mSink->CloseForm(aNode) : NS_OK;
|
|
}
|
|
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
|
|
*/
|
|
nsresult CNavDTD::OpenMap(const nsIParserNode& aNode){
|
|
if(mHasOpenMap)
|
|
CloseMap(aNode);
|
|
nsresult result=(mSink) ? mSink->OpenMap(aNode) : NS_OK;
|
|
if(NS_OK==result) {
|
|
mBodyContext->Push((eHTMLTags)aNode.GetNodeType());
|
|
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
|
|
*/
|
|
nsresult CNavDTD::CloseMap(const nsIParserNode& aNode){
|
|
NS_PRECONDITION(mBodyContext->GetCount() > 0, kInvalidTagStackPos);
|
|
nsresult result=NS_OK;
|
|
if(mHasOpenMap) {
|
|
mHasOpenMap=PR_FALSE;
|
|
result=(mSink) ? mSink->CloseMap(aNode) : NS_OK;
|
|
mBodyContext->Pop();
|
|
}
|
|
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
|
|
*/
|
|
nsresult CNavDTD::OpenFrameset(const nsIParserNode& aNode){
|
|
NS_PRECONDITION(mBodyContext->GetCount() >= 0, kInvalidTagStackPos);
|
|
nsresult result=(mSink) ? mSink->OpenFrameset(aNode) : NS_OK;
|
|
mBodyContext->Push((eHTMLTags)aNode.GetNodeType());
|
|
mHadBodyOrFrameset=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
|
|
*/
|
|
nsresult CNavDTD::CloseFrameset(const nsIParserNode& aNode){
|
|
NS_PRECONDITION(mBodyContext->GetCount() > 0, kInvalidTagStackPos);
|
|
nsresult result=(mSink) ? mSink->CloseFrameset(aNode) : NS_OK;
|
|
mBodyContext->Pop();
|
|
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
|
|
* @param aClosedByStartTag -- ONLY TRUE if the container is being closed by opening of another container.
|
|
* @return TRUE if ok, FALSE if error
|
|
*/
|
|
nsresult
|
|
CNavDTD::OpenContainer(const nsIParserNode& aNode,PRBool aClosedByStartTag){
|
|
NS_PRECONDITION(mBodyContext->GetCount() >= 0, kInvalidTagStackPos);
|
|
|
|
nsresult result=NS_OK;
|
|
eHTMLTags nodeType=(eHTMLTags)aNode.GetNodeType();
|
|
|
|
#define K_OPENOP 100
|
|
CRCStruct theStruct(nodeType,K_OPENOP);
|
|
mComputedCRC32=AccumulateCRC(mComputedCRC32,(char*)&theStruct,sizeof(theStruct));
|
|
|
|
switch(nodeType) {
|
|
|
|
case eHTMLTag_html:
|
|
result=OpenHTML(aNode); break;
|
|
|
|
case eHTMLTag_head:
|
|
// result=OpenHead(aNode); //open the head...
|
|
result=OpenHead(aNode);
|
|
break;
|
|
|
|
case eHTMLTag_body:
|
|
mHasOpenBody=PR_TRUE;
|
|
if(mHasOpenHead)
|
|
mHasOpenHead=1;
|
|
CloseHead(aNode); //do this just in case someone left it open...
|
|
result=OpenBody(aNode);
|
|
break;
|
|
|
|
case eHTMLTag_style:
|
|
case eHTMLTag_title:
|
|
break;
|
|
|
|
case eHTMLTag_textarea:
|
|
{
|
|
PRInt32 theCount;
|
|
nsCParserNode& theCNode=*(nsCParserNode*)&aNode;
|
|
CollectSkippedContent(theCNode,theCount);
|
|
result=AddLeaf(theCNode);
|
|
}
|
|
break;
|
|
|
|
case eHTMLTag_map:
|
|
result=OpenMap(aNode);
|
|
break;
|
|
|
|
case eHTMLTag_form:
|
|
result=OpenForm(aNode); break;
|
|
|
|
case eHTMLTag_frameset:
|
|
if(mHasOpenHead)
|
|
mHasOpenHead=1;
|
|
CloseHead(aNode); //do this just in case someone left it open...
|
|
result=OpenFrameset(aNode); break;
|
|
|
|
case eHTMLTag_script:
|
|
{
|
|
nsCParserNode& theCNode=*(nsCParserNode*)&aNode;
|
|
if(mHasOpenHead)
|
|
mHasOpenHead=1;
|
|
CloseHead(aNode); //do this just in case someone left it open...
|
|
result=HandleScriptToken(theCNode);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
result=(mSink) ? mSink->OpenContainer(aNode) : NS_OK;
|
|
mBodyContext->Push((eHTMLTags)aNode.GetNodeType());
|
|
break;
|
|
}
|
|
|
|
/*
|
|
if((NS_OK==result) && (PR_TRUE==aClosedByStartTag)){
|
|
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
|
|
* @param aTag -- id of tag to be closed
|
|
* @param aClosedByStartTag -- ONLY TRUE if the container is being closed by opening of another container.
|
|
* @return TRUE if ok, FALSE if error
|
|
*/
|
|
nsresult
|
|
CNavDTD::CloseContainer(const nsIParserNode& aNode,eHTMLTags aTag,PRBool aClosedByStartTag){
|
|
nsresult result=NS_OK;
|
|
eHTMLTags nodeType=(eHTMLTags)aNode.GetNodeType();
|
|
|
|
#define K_CLOSEOP 200
|
|
CRCStruct theStruct(nodeType,K_CLOSEOP);
|
|
mComputedCRC32=AccumulateCRC(mComputedCRC32,(char*)&theStruct,sizeof(theStruct));
|
|
|
|
if(!aClosedByStartTag)
|
|
UpdateStyleStackForCloseTag(nodeType,aTag);
|
|
|
|
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_map:
|
|
result=CloseMap(aNode);
|
|
break;
|
|
|
|
case eHTMLTag_form:
|
|
result=CloseForm(aNode); break;
|
|
|
|
case eHTMLTag_frameset:
|
|
result=CloseFrameset(aNode); break;
|
|
|
|
case eHTMLTag_title:
|
|
default:
|
|
result=(mSink) ? mSink->CloseContainer(aNode) : NS_OK;
|
|
mBodyContext->Pop();
|
|
break;
|
|
}
|
|
|
|
if(aClosedByStartTag)
|
|
UpdateStyleStackForOpenTag(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 anIndex
|
|
* @param aTag
|
|
* @param aClosedByStartTag -- if TRUE, then we're closing something because a start tag caused it
|
|
* @return TRUE if ok, FALSE if error
|
|
*/
|
|
nsresult CNavDTD::CloseContainersTo(PRInt32 anIndex,eHTMLTags aTag, PRBool aClosedByStartTag){
|
|
NS_PRECONDITION(mBodyContext->GetCount() > 0, kInvalidTagStackPos);
|
|
nsresult result=NS_OK;
|
|
|
|
nsAutoString theEmpty;
|
|
CEndToken theToken(theEmpty);
|
|
nsCParserNode theNode(&theToken,mLineNumber);
|
|
|
|
if((anIndex<mBodyContext->GetCount()) && (anIndex>=0)) {
|
|
while(mBodyContext->GetCount()>anIndex) {
|
|
eHTMLTags theTag=mBodyContext->Last();
|
|
theToken.SetTypeID(theTag);
|
|
result=CloseContainer(theNode,aTag,aClosedByStartTag);
|
|
}
|
|
}
|
|
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 aTag --
|
|
* @param aClosedByStartTag -- ONLY TRUE if the container is being closed by opening of another container.
|
|
* @return TRUE if ok, FALSE if error
|
|
*/
|
|
nsresult CNavDTD::CloseContainersTo(eHTMLTags aTag,PRBool aClosedByStartTag){
|
|
NS_PRECONDITION(mBodyContext->GetCount() > 0, kInvalidTagStackPos);
|
|
|
|
PRInt32 pos=GetTopmostIndexOf(aTag);
|
|
|
|
if(kNotFound!=pos) {
|
|
//the tag is indeed open, so close it.
|
|
return CloseContainersTo(pos,aTag,aClosedByStartTag);
|
|
}
|
|
|
|
eHTMLTags theTopTag=mBodyContext->Last();
|
|
|
|
PRBool theTagIsSynonymous=((nsHTMLElement::IsStyleTag(aTag)) && (nsHTMLElement::IsStyleTag(theTopTag)));
|
|
if(!theTagIsSynonymous){
|
|
theTagIsSynonymous=((nsHTMLElement::IsHeadingTag(aTag)) && (nsHTMLElement::IsHeadingTag(theTopTag)));
|
|
}
|
|
|
|
if(theTagIsSynonymous) {
|
|
//if you're here, it's because we're trying to close one tag,
|
|
//but a different (synonymous) 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,aClosedByStartTag);
|
|
}
|
|
}
|
|
|
|
nsresult result=NS_OK;
|
|
CTagList* theRootTags=gHTMLElements[aTag].GetRootTags();
|
|
eHTMLTags theParentTag=theRootTags->GetTagAt(0);
|
|
pos=GetTopmostIndexOf(theParentTag);
|
|
if(kNotFound!=pos) {
|
|
//the parent container is open, so close it instead
|
|
result=CloseContainersTo(pos+1,aTag,aClosedByStartTag);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* This method causes the topmost container on the stack
|
|
* to be closed. The closure is ALWAYS due to a new start
|
|
* tag be opened which forces the topmost tag closed.
|
|
*
|
|
* @update gess 4/26/99
|
|
* @return TRUE if ok, FALSE if error
|
|
*/
|
|
nsresult CNavDTD::CloseTopmostContainer(){
|
|
NS_PRECONDITION(mBodyContext->GetCount() > 0, kInvalidTagStackPos);
|
|
|
|
nsAutoString theEmpty;
|
|
CEndToken theToken(theEmpty);
|
|
eHTMLTags theTag=mBodyContext->Last();
|
|
|
|
theToken.SetTypeID(theTag);
|
|
nsCParserNode theNode(&theToken,mLineNumber);
|
|
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 error code; 0 means OK
|
|
*/
|
|
nsresult CNavDTD::AddLeaf(const nsIParserNode& aNode){
|
|
nsresult result=NS_OK;
|
|
|
|
if(mSink){
|
|
eHTMLTags theTag=(eHTMLTags)aNode.GetNodeType();
|
|
OpenTransientStyles(theTag);
|
|
result=mSink->AddLeaf(aNode);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Call this method ONLY when you want to write a leaf
|
|
* into the head container.
|
|
*
|
|
* @update gess 03/14/99
|
|
* @param aNode -- next node to be added to model
|
|
* @return error code; 0 means OK
|
|
*/
|
|
nsresult CNavDTD::AddHeadLeaf(nsIParserNode& aNode){
|
|
nsresult result=NS_OK;
|
|
|
|
static eHTMLTags gNoXTags[]={eHTMLTag_noframes,eHTMLTag_nolayer,eHTMLTag_noscript};
|
|
|
|
//this code has been added to prevent <meta> tags from being processed inside
|
|
//the document if the <meta> tag itself was found in a <noframe>, <nolayer>, or <noscript> tag.
|
|
eHTMLTags theTag=(eHTMLTags)aNode.GetNodeType();
|
|
if(eHTMLTag_meta==theTag)
|
|
if(HasOpenContainer(gNoXTags,sizeof(gNoXTags)/sizeof(eHTMLTag_unknown))) {
|
|
return result;
|
|
}
|
|
|
|
if(mSink) {
|
|
result=OpenHead(aNode);
|
|
if(NS_OK==result) {
|
|
if(eHTMLTag_title==theTag) {
|
|
mSink->SetTitle(aNode.GetSkippedContent());
|
|
}
|
|
else result=AddLeaf(aNode);
|
|
// XXX If the return value tells us to block, go
|
|
// ahead and close the tag out anyway, since its
|
|
// contents will be consumed.
|
|
if (NS_SUCCEEDED(result)) {
|
|
nsresult rv=CloseHead(aNode);
|
|
// XXX Only send along a failure. If the close
|
|
// succeeded we still may need to indicate that the
|
|
// parser has blocked (i.e. return the result of
|
|
// the AddLeaf.
|
|
if (rv != NS_OK) {
|
|
result = rv;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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
|
|
*/
|
|
nsresult CNavDTD::CreateContextStackFor(eHTMLTags aChildTag){
|
|
|
|
static nsEntryStack kPropagationStack;
|
|
kPropagationStack.Empty();
|
|
|
|
nsresult result=(nsresult)kContextMismatch;
|
|
PRInt32 cnt=0;
|
|
eHTMLTags theTop=mBodyContext->Last();
|
|
PRBool bResult=ForwardPropagate(kPropagationStack,theTop,aChildTag);
|
|
|
|
if(PR_FALSE==bResult){
|
|
|
|
if(eHTMLTag_unknown!=theTop) {
|
|
if(theTop!=aChildTag) //dont even bother if we're already inside a similar element...
|
|
bResult=BackwardPropagate(kPropagationStack,theTop,aChildTag);
|
|
|
|
/*****************************************************************************
|
|
OH NOOOO!...
|
|
|
|
We found a pretty fundamental flaw in the backward propagation code.
|
|
The previous version propagated from a child to a target parent, and
|
|
then again from the target parent to the root.
|
|
Only thing is, that won't work in cases where a container exists that's
|
|
not in the usual hiearchy:
|
|
|
|
<html><body>
|
|
<div>
|
|
<table>
|
|
<!--missing TR>
|
|
<td>cell text</td>
|
|
</tr>
|
|
</table> ...etc...
|
|
|
|
In this case, we'd propagate fine from the <TD> to the <TABLE>, and
|
|
then from the table to the <html>. Unfortunately, the <DIV> won't show
|
|
up in the propagated form, and then we get out of sync with the actual
|
|
context stack when it comes to autogenerate containers.
|
|
******************************************************************************/
|
|
|
|
} //if
|
|
else bResult=BackwardPropagate(kPropagationStack,eHTMLTag_html,aChildTag);
|
|
} //elseif
|
|
|
|
if((0==mBodyContext->GetCount()) || (mBodyContext->Last()==kPropagationStack.Pop()))
|
|
result=NS_OK;
|
|
|
|
//now, build up the stack according to the tags
|
|
//you have that aren't in the stack...
|
|
nsAutoString theEmpty;
|
|
CStartToken theToken(theEmpty);
|
|
PRInt32 count = kPropagationStack.GetCount();
|
|
if(PR_TRUE==bResult){
|
|
while(count>0) {
|
|
eHTMLTags theTag=kPropagationStack.Pop();
|
|
theToken.SetTypeID(theTag); //open the container...
|
|
HandleStartToken(&theToken);
|
|
count--;
|
|
}
|
|
result=NS_OK;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
/**
|
|
* This method gets called to ensure that the context
|
|
* stack is properly set up for the given child.
|
|
* We pop containers off the stack (all the way down
|
|
* html) until we get a container that can contain
|
|
* the given child.
|
|
*
|
|
* @update gess 4/8/98
|
|
* @param
|
|
* @return
|
|
*/
|
|
nsresult CNavDTD::ReduceContextStackFor(eHTMLTags aChildTag){
|
|
nsresult result=NS_OK;
|
|
eHTMLTags theTopTag=mBodyContext->Last();
|
|
|
|
while( (theTopTag!=kNotFound) &&
|
|
(PR_FALSE==CanContain(theTopTag,aChildTag)) &&
|
|
(PR_FALSE==CanPropagate(theTopTag,aChildTag))){
|
|
CloseTopmostContainer();
|
|
theTopTag=mBodyContext->Last();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
/**
|
|
* This method causes all explicit style-tag containers that
|
|
* are opened to be reflected on our internal style-stack.
|
|
*
|
|
* @update gess6/4/98
|
|
* @param aTag is the id of the html container being opened
|
|
* @return 0 if all is well.
|
|
*/
|
|
nsresult CNavDTD::UpdateStyleStackForOpenTag(eHTMLTags aTag,eHTMLTags anActualTag){
|
|
nsresult result=0;
|
|
|
|
#ifdef ENABLE_RESIDUALSTYLE
|
|
if(anActualTag!=eHTMLTag_font){
|
|
if(nsHTMLElement::IsStyleTag(aTag)) {
|
|
mBodyContext->PushStyle(aTag);
|
|
}
|
|
}
|
|
#endif
|
|
return result;
|
|
} //update..
|
|
|
|
|
|
/**
|
|
* This method gets called when an explicit style close-tag is encountered.
|
|
* It results in the style tag id being popped from our internal style stack.
|
|
*
|
|
* @update gess6/4/98
|
|
* @param
|
|
* @return 0 if all went well (which it always does)
|
|
*/
|
|
nsresult
|
|
CNavDTD::UpdateStyleStackForCloseTag(eHTMLTags aTag,eHTMLTags anActualTag){
|
|
nsresult result=0;
|
|
|
|
#ifdef ENABLE_RESIDUALSTYLE
|
|
eHTMLTags theTag=mBodyContext->PopStyle();
|
|
#endif
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @update gess8/4/98
|
|
* @param
|
|
* @return
|
|
*/
|
|
nsITokenRecycler* CNavDTD::GetTokenRecycler(void){
|
|
nsITokenizer* theTokenizer=GetTokenizer();
|
|
return theTokenizer->GetTokenRecycler();
|
|
}
|
|
|
|
/**
|
|
* Retrieve the preferred tokenizer for use by this DTD.
|
|
* @update gess12/28/98
|
|
* @param none
|
|
* @return ptr to tokenizer
|
|
*/
|
|
nsITokenizer* CNavDTD::GetTokenizer(void) {
|
|
if(!mTokenizer)
|
|
mTokenizer=new nsHTMLTokenizer();
|
|
return mTokenizer;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @update gess5/18/98
|
|
* @param
|
|
* @return
|
|
*/
|
|
nsresult CNavDTD::WillResumeParse(void){
|
|
nsresult result=(mSink) ? mSink->WillResume() : NS_OK;
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* This method gets called when the parsing process is interrupted
|
|
* due to lack of data (waiting for netlib).
|
|
* @update gess5/18/98
|
|
* @return error code
|
|
*/
|
|
nsresult CNavDTD::WillInterruptParse(void){
|
|
nsresult result=(mSink) ? mSink->WillInterrupt() : NS_OK;
|
|
return result;
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
* @update gpk03/14/99
|
|
* @param
|
|
* @return
|
|
*/
|
|
nsresult CNavDTD::DoFragment(PRBool aFlag)
|
|
{
|
|
return NS_OK;
|
|
}
|
|
|