Moved nsFileSpec and related classes into this obsolete library. Moved nsRegistry and related libreg functionality into the obsolete library. Updated many callers using the obsolete nsFile spec to use nsIFile and Necko to do file IO. Combined the following DLLs (source -> dest) uriloader -> docshell shistory -> docshell jsurl -> jsdom gkview -> gklayout Moved nsAdapterEnumerator out of xpcom/ds and into mailnews, since they're the only consumer Modifed the xpt_link tool so that you can specify a only include cid list that can mask CIDs that you are not interested in. Added build options: Prevent the building of xpinstall (--disable-xpinstall) Prevent the building js component loader (--disable-jsloader) A build option to only build a single profile (--enable-single-profile) A build flag to only built the required xpfe components (--disable-xpfe-components). Removal or hiding of unused functions and classes including nsEscape*, nsDequeIterator, nsRecyclingAllocatorImpl, nsDiscriminatedUnion, nsOpaqueKey, nsCRT::strlen, NS_NewCommandLineService Bug 194240, r/sr = darin, alec. git-svn-id: svn://10.0.0.236/trunk@139480 18797224-902f-48f8-a5cc-f745e15eee43
1020 lines
30 KiB
C++
1020 lines
30 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: NPL 1.1/GPL 2.0/LGPL 2.1
|
|
*
|
|
* The contents of this file are subject to the Netscape Public License
|
|
* Version 1.1 (the "License"); you may not use this file except in
|
|
* compliance with the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is mozilla.org code.
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* Netscape Communications Corporation.
|
|
* Portions created by the Initial Developer are Copyright (C) 1998
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* rickg@netscape.com
|
|
*
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
* use your version of this file under the terms of the NPL, indicate your
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
* the provisions above, a recipient may use your version of this file under
|
|
* the terms of any one of the NPL, the GPL or the LGPL.
|
|
*
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
//#define ENABLE_CRC
|
|
|
|
#include "nsDebug.h"
|
|
#include "nsIAtom.h"
|
|
#include "COtherDTD.h"
|
|
#include "nsHTMLTokens.h"
|
|
#include "nsCRT.h"
|
|
#include "nsParser.h"
|
|
#include "nsIParser.h"
|
|
#include "nsIHTMLContentSink.h"
|
|
#include "nsScanner.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 "nsHTMLTokenizer.h"
|
|
#include "nsTime.h"
|
|
#include "nsViewSourceHTML.h"
|
|
#include "nsParserNode.h"
|
|
#include "nsHTMLEntities.h"
|
|
#include "nsLinebreakConverter.h"
|
|
#include "nsUnicharUtils.h"
|
|
|
|
#include "prmem.h"
|
|
|
|
|
|
static NS_DEFINE_IID(kIHTMLContentSinkIID, NS_IHTML_CONTENT_SINK_IID);
|
|
static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID);
|
|
static NS_DEFINE_IID(kIDTDIID, NS_IDTD_IID);
|
|
static NS_DEFINE_IID(kClassIID, NS_IOTHERHTML_DTD_IID);
|
|
static NS_DEFINE_IID(kParserServiceCID, NS_PARSERSERVICE_CID);
|
|
static const char kVerificationDir[] = "c:/temp";
|
|
|
|
|
|
#ifdef ENABLE_CRC
|
|
static char gShowCRC;
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef MOZ_PERF_METRICS
|
|
# define START_TIMER() \
|
|
if(mParser) MOZ_TIMER_START(mParser->mParseTime); \
|
|
if(mParser) MOZ_TIMER_START(mParser->mDTDTime);
|
|
|
|
# define STOP_TIMER() \
|
|
if(mParser) MOZ_TIMER_STOP(mParser->mParseTime); \
|
|
if(mParser) MOZ_TIMER_STOP(mParser->mDTDTime);
|
|
#else
|
|
# define STOP_TIMER()
|
|
# define START_TIMER()
|
|
#endif
|
|
|
|
|
|
#include "COtherElements.h"
|
|
|
|
|
|
/************************************************************************
|
|
And now for the main class -- COtherDTD...
|
|
************************************************************************/
|
|
|
|
/**
|
|
* 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 COtherDTD::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 = (COtherDTD*)(this);
|
|
}
|
|
else {
|
|
*aInstancePtr=0;
|
|
return NS_NOINTERFACE;
|
|
}
|
|
NS_ADDREF_THIS();
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMPL_ADDREF(COtherDTD)
|
|
NS_IMPL_RELEASE(COtherDTD)
|
|
|
|
/**
|
|
* Default constructor
|
|
*
|
|
* @update gess 4/9/98
|
|
* @param
|
|
* @return
|
|
*/
|
|
COtherDTD::COtherDTD() : nsIDTD() {
|
|
mSink = 0;
|
|
mParser=0;
|
|
mLineNumber=1;
|
|
mHasOpenBody=PR_FALSE;
|
|
mHasOpenHead=0;
|
|
mHasOpenForm=PR_FALSE;
|
|
mHasOpenMap=PR_FALSE;
|
|
mTokenizer=0;
|
|
mTokenAllocator=0;
|
|
mComputedCRC32=0;
|
|
mExpectedCRC32=0;
|
|
mDTDState=NS_OK;
|
|
mDocType=eHTML_Strict;
|
|
mHadFrameset=PR_FALSE;
|
|
mHadBody=PR_FALSE;
|
|
mHasOpenScript=PR_FALSE;
|
|
mParserCommand=eViewNormal;
|
|
mNodeAllocator=new nsNodeAllocator();
|
|
mBodyContext=new nsDTDContext();
|
|
|
|
#if 0 //set this to 1 if you want strictDTD to be based on the environment setting.
|
|
char* theEnvString = PR_GetEnv("MOZ_DISABLE_STRICT");
|
|
mEnableStrict=PRBool(0==theEnvString);
|
|
#else
|
|
mEnableStrict=PR_TRUE;
|
|
#endif
|
|
|
|
if(!gElementTable) {
|
|
gElementTable = new CElementTable();
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @update gess1/8/99
|
|
* @param
|
|
* @return
|
|
*/
|
|
const nsIID& COtherDTD::GetMostDerivedIID(void)const {
|
|
return kClassIID;
|
|
}
|
|
|
|
/**
|
|
* Default destructor
|
|
*
|
|
* @update gess 4/9/98
|
|
* @param
|
|
* @return
|
|
*/
|
|
COtherDTD::~COtherDTD(){
|
|
delete mBodyContext;
|
|
|
|
if(mNodeAllocator) {
|
|
delete mNodeAllocator;
|
|
mNodeAllocator=nsnull;
|
|
}
|
|
|
|
NS_IF_RELEASE(mSink);
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
*/
|
|
nsresult NS_NewOtherHTMLDTD(nsIDTD** aInstancePtrResult) {
|
|
COtherDTD* it = new COtherDTD();
|
|
|
|
if (it == 0) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
return it->QueryInterface(kClassIID, (void **) aInstancePtrResult);
|
|
}
|
|
|
|
/**
|
|
* Call this method if you want the DTD to construct a fresh
|
|
* instance of itself.
|
|
* @update gess7/23/98
|
|
* @param
|
|
* @return
|
|
*/
|
|
NS_IMETHODIMP
|
|
COtherDTD::CreateNewInstance(nsIDTD** aInstancePtrResult)
|
|
{
|
|
nsresult result=NS_NewOtherHTMLDTD(aInstancePtrResult);
|
|
|
|
if(aInstancePtrResult) {
|
|
COtherDTD *theOtherDTD=(COtherDTD*)*aInstancePtrResult;
|
|
if(theOtherDTD) {
|
|
theOtherDTD->mDTDMode=mDTDMode;
|
|
theOtherDTD->mParserCommand=mParserCommand;
|
|
theOtherDTD->mDocType=mDocType;
|
|
theOtherDTD->mEnableStrict=mEnableStrict;
|
|
}
|
|
}
|
|
|
|
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.
|
|
*/
|
|
NS_IMETHODIMP_(eAutoDetectResult)
|
|
COtherDTD::CanParse(CParserContext& aParserContext, const nsString& aBuffer,
|
|
PRInt32 aVersion)
|
|
{
|
|
eAutoDetectResult result=eUnknownDetect;
|
|
|
|
if(mEnableStrict) {
|
|
if(aParserContext.mParserCommand != eViewSource) {
|
|
if(PR_TRUE==aParserContext.mMimeType.EqualsWithConversion(kPlainTextContentType)) {
|
|
result=eValidDetect;
|
|
}
|
|
else if(PR_TRUE==aParserContext.mMimeType.EqualsWithConversion(kHTMLTextContentType)) {
|
|
switch(aParserContext.mDTDMode) {
|
|
case eDTDMode_full_standards:
|
|
case eDTDMode_almost_standards:
|
|
result=ePrimaryDetect;
|
|
break;
|
|
default:
|
|
result=eValidDetect;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
//otherwise, look into the buffer to see if you recognize anything...
|
|
PRBool theBufHasXML=PR_FALSE;
|
|
if(BufferContainsHTML(aBuffer,theBufHasXML)){
|
|
result = eValidDetect ;
|
|
if(0==aParserContext.mMimeType.Length()) {
|
|
aParserContext.SetMimeType(NS_LITERAL_CSTRING(kHTMLTextContentType));
|
|
if(!theBufHasXML) {
|
|
switch(aParserContext.mDTDMode) {
|
|
case eDTDMode_full_standards:
|
|
case eDTDMode_almost_standards:
|
|
result=ePrimaryDetect;
|
|
break;
|
|
default:
|
|
result=eValidDetect;
|
|
break;
|
|
}
|
|
}
|
|
else result=eValidDetect;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
/**
|
|
* The parser uses a code sandwich to wrap the parsing process. Before
|
|
* the process begins, WillBuildModel() is called. Afterwards the parser
|
|
* calls DidBuildModel().
|
|
* @update rickg 03.20.2000
|
|
* @param aParserContext
|
|
* @param aSink
|
|
* @return error code (almost always 0)
|
|
*/
|
|
nsresult COtherDTD::WillBuildModel(const CParserContext& aParserContext,
|
|
nsITokenizer* aTokenizer,
|
|
nsIContentSink* aSink){
|
|
nsresult result=NS_OK;
|
|
|
|
mFilename=aParserContext.mScanner->GetFilename();
|
|
mHasOpenBody=PR_FALSE;
|
|
mHadFrameset=PR_FALSE;
|
|
mLineNumber=1;
|
|
mHasOpenScript=PR_FALSE;
|
|
mDTDMode=aParserContext.mDTDMode;
|
|
mParserCommand=aParserContext.mParserCommand;
|
|
mTokenizer = aTokenizer;
|
|
|
|
if((!aParserContext.mPrevContext) && (aSink)) {
|
|
|
|
STOP_TIMER();
|
|
MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: COtherDTD::WillBuildModel(), this=%p\n", this));
|
|
|
|
mDocType=aParserContext.mDocType;
|
|
mBodyContext->mFlags.mTransitional=PR_FALSE;
|
|
|
|
if(aSink && (!mSink)) {
|
|
result=aSink->QueryInterface(kIHTMLContentSinkIID, (void **)&mSink);
|
|
}
|
|
|
|
if(result==NS_OK) {
|
|
result = aSink->WillBuildModel();
|
|
|
|
#ifdef DEBUG
|
|
mBodyContext->ResetCounters();
|
|
#endif
|
|
|
|
MOZ_TIMER_DEBUGLOG(("Start: Parse Time: COtherDTD::WillBuildModel(), this=%p\n", this));
|
|
START_TIMER();
|
|
|
|
mSkipTarget=eHTMLTag_unknown;
|
|
mComputedCRC32=0;
|
|
mExpectedCRC32=0;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
/**
|
|
* 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 NS_OK)
|
|
*/
|
|
nsresult COtherDTD::BuildModel(nsIParser* aParser,nsITokenizer* aTokenizer,nsITokenObserver* anObserver,nsIContentSink* aSink) {
|
|
nsresult result=NS_OK;
|
|
|
|
if(aTokenizer) {
|
|
nsITokenizer* oldTokenizer=mTokenizer;
|
|
mTokenizer=aTokenizer;
|
|
mParser=(nsParser*)aParser;
|
|
|
|
if(mTokenizer) {
|
|
|
|
mTokenAllocator=mTokenizer->GetTokenAllocator();
|
|
|
|
mBodyContext->SetTokenAllocator(mTokenAllocator);
|
|
mBodyContext->SetNodeAllocator(mNodeAllocator);
|
|
|
|
if(mSink) {
|
|
|
|
if(!mBodyContext->GetCount()) {
|
|
//if the content model is empty, then begin by opening <html>...
|
|
CStartToken *theToken=(CStartToken*)mTokenAllocator->CreateTokenOfType(eToken_start,eHTMLTag_html,NS_LITERAL_STRING("html"));
|
|
HandleStartToken(theToken); //this token should get pushed on the context stack, don't recycle it.
|
|
}
|
|
|
|
while(NS_SUCCEEDED(result)){
|
|
|
|
if(mDTDState!=NS_ERROR_HTMLPARSER_STOPPARSING) {
|
|
CToken* theToken=mTokenizer->PopToken();
|
|
if(theToken) {
|
|
result=HandleToken(theToken,aParser);
|
|
}
|
|
else break;
|
|
}
|
|
else {
|
|
result=mDTDState;
|
|
break;
|
|
}
|
|
}//while
|
|
mTokenizer=oldTokenizer;
|
|
}
|
|
}
|
|
}
|
|
else result=NS_ERROR_HTMLPARSER_BADTOKENIZER;
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @update gess5/18/98
|
|
* @param
|
|
* @return
|
|
*/
|
|
nsresult COtherDTD::DidBuildModel(nsresult anErrorCode,PRBool aNotifySink,nsIParser* aParser,nsIContentSink* aSink){
|
|
nsresult result=NS_OK;
|
|
|
|
if(aSink) {
|
|
|
|
if(aParser && (NS_OK==result)){
|
|
if(aNotifySink){
|
|
if((NS_OK==anErrorCode) && (mBodyContext->GetCount()>0)) {
|
|
|
|
PRInt32 theIndex=mBodyContext->GetCount()-1;
|
|
eHTMLTags theChild = mBodyContext->TagAt(theIndex);
|
|
while (theIndex>0) {
|
|
eHTMLTags theParent = mBodyContext->TagAt(--theIndex);
|
|
CElement *theElement = gElementTable->mElements[theParent];
|
|
nsCParserNode *theNode = mBodyContext->PeekNode();
|
|
theElement->HandleEndToken(theNode,theChild,mBodyContext,mSink);
|
|
theChild = theParent;
|
|
}
|
|
|
|
nsEntryStack* theChildStyles = 0;
|
|
nsCParserNode* theNode = (nsCParserNode*)mBodyContext->Pop(theChildStyles);
|
|
if (theNode) {
|
|
mSink->CloseHTML();
|
|
}
|
|
NS_ASSERTION(!theChildStyles, "there should no residual style information in this dtd");
|
|
IF_DELETE(theChildStyles, mNodeAllocator);
|
|
}
|
|
else {
|
|
//If you're here, then an error occured, but we still have nodes on the stack.
|
|
//At a minimum, we should grab the nodes and recycle them.
|
|
//Just to be correct, we'll also recycle the nodes.
|
|
|
|
while (mBodyContext->GetCount() > 0) {
|
|
|
|
nsEntryStack *theChildStyles = 0;
|
|
nsCParserNode* theNode = (nsCParserNode*)mBodyContext->Pop(theChildStyles);
|
|
if (theNode) {
|
|
theNode->mUseCount = 0;
|
|
if (theChildStyles) {
|
|
delete theChildStyles;
|
|
}
|
|
IF_FREE(theNode, mNodeAllocator);
|
|
}
|
|
NS_ASSERTION(!theChildStyles, "there should no residual style information in this dtd");
|
|
IF_DELETE(theChildStyles, mNodeAllocator);
|
|
}
|
|
}
|
|
|
|
}
|
|
} //if aparser
|
|
|
|
//No matter what, you need to call did build model.
|
|
result = aSink->DidBuildModel(0);
|
|
|
|
} //if asink
|
|
return result;
|
|
}
|
|
|
|
NS_IMETHODIMP_(void)
|
|
COtherDTD::Terminate()
|
|
{
|
|
mDTDState = NS_ERROR_HTMLPARSER_STOPPARSING;
|
|
}
|
|
|
|
NS_IMETHODIMP_(PRInt32)
|
|
COtherDTD::GetType()
|
|
{
|
|
return NS_IPARSER_FLAG_HTML;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
COtherDTD::CollectSkippedContent(PRInt32 aTag, nsAString& aContent, PRInt32 &aLineNo)
|
|
{
|
|
return NS_OK;
|
|
}
|
|
|
|
/**
|
|
* 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 12/1/99
|
|
* @param aToken
|
|
* @param aParser
|
|
* @return
|
|
*/
|
|
nsresult COtherDTD::HandleToken(CToken* aToken,nsIParser* aParser){
|
|
nsresult result=NS_OK;
|
|
|
|
if(aToken) {
|
|
CHTMLToken* theToken= (CHTMLToken*)(aToken);
|
|
eHTMLTokenTypes theType=eHTMLTokenTypes(theToken->GetTokenType());
|
|
|
|
// theToken->mUseCount=0; //assume every token coming into this system needs recycling.
|
|
|
|
mParser=(nsParser*)aParser;
|
|
|
|
switch(theType) {
|
|
case eToken_text:
|
|
case eToken_start:
|
|
case eToken_whitespace:
|
|
case eToken_newline:
|
|
case eToken_doctypeDecl:
|
|
case eToken_markupDecl:
|
|
result=HandleStartToken(theToken); break;
|
|
|
|
case eToken_entity:
|
|
result=HandleEntityToken(theToken); break;
|
|
|
|
case eToken_end:
|
|
result=HandleEndToken(theToken); break;
|
|
|
|
default:
|
|
break;
|
|
}//switch
|
|
|
|
|
|
if(NS_SUCCEEDED(result) || (NS_ERROR_HTMLPARSER_BLOCK==result)) {
|
|
IF_FREE(theToken, mTokenAllocator);
|
|
}
|
|
else if(result==NS_ERROR_HTMLPARSER_STOPPARSING)
|
|
mDTDState=result;
|
|
else return NS_OK;
|
|
|
|
}//if
|
|
return result;
|
|
}
|
|
|
|
|
|
/**
|
|
* This gets called after we've handled a given start tag.
|
|
* It's a generic hook to let us to post processing.
|
|
* @param aToken contains the tag in question
|
|
* @param aChildTag is the tag itself.
|
|
* @return status
|
|
*/
|
|
nsresult COtherDTD::DidHandleStartTag(nsIParserNode& aNode,eHTMLTags aChildTag){
|
|
nsresult result=NS_OK;
|
|
|
|
switch(aChildTag){
|
|
|
|
case eHTMLTag_script:
|
|
mHasOpenScript=PR_TRUE;
|
|
break;
|
|
|
|
case eHTMLTag_pre:
|
|
case eHTMLTag_listing:
|
|
{
|
|
CToken* theNextToken=mTokenizer->PeekToken();
|
|
if(theNextToken) {
|
|
eHTMLTokenTypes theType=eHTMLTokenTypes(theNextToken->GetTokenType());
|
|
if(eToken_newline==theType){
|
|
++mLineNumber;
|
|
mTokenizer->PopToken(); //skip 1st newline inside PRE and LISTING
|
|
}//if
|
|
}//if
|
|
}
|
|
break;
|
|
|
|
#ifdef DEBUG
|
|
case eHTMLTag_meta:
|
|
{
|
|
//we should only enable user-defined entities in debug builds...
|
|
|
|
PRInt32 theCount=aNode.GetAttributeCount();
|
|
const nsAString* theNamePtr=0;
|
|
const nsAString* theValuePtr=0;
|
|
|
|
if(theCount) {
|
|
PRInt32 theIndex=0;
|
|
for(theIndex=0;theIndex<theCount;++theIndex){
|
|
const nsAString& theKey = aNode.GetKeyAt(theIndex);
|
|
if(theKey.Equals(NS_LITERAL_STRING("ENTITY"), nsCaseInsensitiveStringComparator())) {
|
|
const nsAString& theName=aNode.GetValueAt(theIndex);
|
|
theNamePtr=&theName;
|
|
}
|
|
else if(theKey.Equals(NS_LITERAL_STRING("VALUE"), nsCaseInsensitiveStringComparator())) {
|
|
//store the named enity with the context...
|
|
const nsAString& theValue=aNode.GetValueAt(theIndex);
|
|
theValuePtr=&theValue;
|
|
}
|
|
}
|
|
}
|
|
if(theNamePtr && theValuePtr) {
|
|
mBodyContext->RegisterEntity(*theNamePtr,*theValuePtr);
|
|
}
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
break;
|
|
}//switch
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* 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 COtherDTD::WillHandleStartTag(CToken* aToken,eHTMLTags aTag,nsIParserNode& aNode){
|
|
nsresult result=NS_OK;
|
|
|
|
//first let's see if there's some skipped content to deal with...
|
|
#if 0
|
|
PRInt32 theAttrCount = aNode.GetAttributeCount();
|
|
if(*gElementTable->mElements[aTag].mSkipTarget) {
|
|
result=CollectSkippedContent(aNode,theAttrCount);
|
|
}
|
|
#endif
|
|
|
|
STOP_TIMER()
|
|
MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: COtherDTD::WillHandleStartTag(), this=%p\n", this));
|
|
|
|
if(mParser) {
|
|
|
|
switch(aTag) {
|
|
case eHTMLTag_newline:
|
|
++mLineNumber;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
mSink->NotifyTagObservers(&aNode);
|
|
}
|
|
|
|
MOZ_TIMER_DEBUGLOG(("Start: Parse Time: COtherDTD::WillHandleStartTag(), this=%p\n", this));
|
|
START_TIMER()
|
|
|
|
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 COtherDTD::HandleStartToken(CToken* aToken) {
|
|
|
|
//Begin by gathering up attributes...
|
|
|
|
nsresult result=NS_OK;
|
|
nsCParserNode* theNode=mNodeAllocator->CreateNode(aToken, mTokenAllocator);
|
|
if(theNode) {
|
|
|
|
eHTMLTags theChildTag=(eHTMLTags)aToken->GetTypeID();
|
|
PRInt16 attrCount=aToken->GetAttributeCount();
|
|
eHTMLTags theParent=mBodyContext->Last();
|
|
|
|
result=(0==attrCount) ? NS_OK : CollectAttributes(*theNode,theChildTag,attrCount);
|
|
|
|
if(NS_OK==result) {
|
|
result=WillHandleStartTag(aToken,theChildTag,*theNode);
|
|
if(NS_OK==result) {
|
|
|
|
mLineNumber += aToken->GetNewlineCount();
|
|
|
|
PRBool theTagWasHandled=PR_FALSE;
|
|
|
|
switch(theChildTag) {
|
|
|
|
case eHTMLTag_html:
|
|
if(!mBodyContext->HasOpenContainer(theChildTag)){
|
|
mSink->OpenHTML(*theNode);
|
|
mBodyContext->Push(theNode, 0, PR_FALSE);
|
|
}
|
|
theTagWasHandled=PR_TRUE;
|
|
break;
|
|
|
|
default:
|
|
CElement* theElement=gElementTable->mElements[theParent];
|
|
if(theElement) {
|
|
result=theElement->HandleStartToken(theNode,theChildTag,mBodyContext,mSink);
|
|
theTagWasHandled=PR_TRUE;
|
|
}
|
|
break;
|
|
}//switch
|
|
|
|
if(theTagWasHandled) {
|
|
DidHandleStartTag(*theNode,theChildTag);
|
|
}
|
|
|
|
} //if
|
|
}//if
|
|
IF_FREE(theNode, mNodeAllocator);
|
|
}
|
|
else result=NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* This method gets called when an end token has been
|
|
* encountered in the parse process. If the end tag matches
|
|
* the start tag on the stack, then simply close it. Otherwise,
|
|
* we have a erroneous state condition. This can be because we
|
|
* have a close tag with no prior open tag (user error) or because
|
|
* we screwed something up in the parse process. I'm not sure
|
|
* yet how to tell the difference.
|
|
*
|
|
* @update gess 3/25/98
|
|
* @param aToken -- next (start) token to be handled
|
|
* @return PR_TRUE if all went well; PR_FALSE if error occured
|
|
*/
|
|
nsresult COtherDTD::HandleEndToken(CToken* aToken) {
|
|
nsresult result=NS_OK;
|
|
eHTMLTags theChildTag=(eHTMLTags)aToken->GetTypeID();
|
|
|
|
switch(theChildTag) {
|
|
|
|
case eHTMLTag_body: //we intentionally don't let the user close HTML or BODY
|
|
case eHTMLTag_html:
|
|
break;
|
|
|
|
case eHTMLTag_script:
|
|
mHasOpenScript=PR_FALSE;
|
|
|
|
default:
|
|
PRInt32 theCount=mBodyContext->GetCount();
|
|
eHTMLTags theParent=mBodyContext->TagAt(theCount-1);
|
|
if(theChildTag==theParent) {
|
|
theParent=mBodyContext->TagAt(theCount-2);
|
|
}
|
|
CElement* theElement=gElementTable->mElements[theParent];
|
|
if(theElement) {
|
|
nsCParserNode* theNode=mNodeAllocator->CreateNode(aToken, mTokenAllocator);
|
|
if(theNode) {
|
|
result=theElement->HandleEndToken(theNode,theChildTag,mBodyContext,mSink);
|
|
IF_FREE(theNode, mNodeAllocator);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
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 COtherDTD::CollectAttributes(nsIParserNode& aNode,eHTMLTags aTag,PRInt32 aCount){
|
|
int attr=0;
|
|
|
|
nsresult result=NS_OK;
|
|
int theAvailTokenCount=mTokenizer->GetCount();
|
|
if(aCount<=theAvailTokenCount) {
|
|
//gElementTable->mElements[aTag]->GetSkipTarget();
|
|
CToken* theToken=0;
|
|
for(attr=0;attr<aCount;++attr){
|
|
theToken=mTokenizer->PopToken();
|
|
if(theToken) {
|
|
// Sanitize the key for it might contain some non-alpha-non-digit characters
|
|
// at its end. Ex. <OPTION SELECTED/> - This will be tokenized as "<" "OPTION",
|
|
// "SELECTED/", and ">". In this case the "SELECTED/" key will be sanitized to
|
|
// a legitimate "SELECTED" key.
|
|
((CAttributeToken*)theToken)->SanitizeKey();
|
|
|
|
aNode.AddAttribute(theToken);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
result=kEOF;
|
|
}
|
|
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 COtherDTD::HandleEntityToken(CToken* aToken) {
|
|
nsresult result=NS_OK;
|
|
|
|
nsAutoString theStr;
|
|
aToken->GetSource(theStr);
|
|
PRUnichar theChar=theStr.CharAt(0);
|
|
CToken *theToken=0;
|
|
|
|
if((kHashsign!=theChar) && (-1==nsHTMLEntities::EntityToUnicode(theStr))){
|
|
|
|
#ifdef DEBUG
|
|
//before we just toss this away as a bogus entity, let's check...
|
|
CNamedEntity *theEntity=mBodyContext->GetEntity(theStr);
|
|
if(theEntity) {
|
|
theToken=(CTextToken*)mTokenAllocator->CreateTokenOfType(eToken_text,eHTMLTag_text,theEntity->mValue);
|
|
}
|
|
else {
|
|
#endif
|
|
//if you're here we have a bogus entity.
|
|
//convert it into a text token.
|
|
nsAutoString entityName;
|
|
entityName.Assign(NS_LITERAL_STRING("&"));
|
|
entityName.Append(theStr); //should append the entity name; fix bug 51161.
|
|
theToken=(CTextToken*)mTokenAllocator->CreateTokenOfType(eToken_text,eHTMLTag_text,entityName);
|
|
#ifdef DEBUG
|
|
}
|
|
#endif
|
|
result=HandleStartToken(theToken);
|
|
}
|
|
else {
|
|
|
|
//add this code to fix bug 42629 (entities were getting dropped).
|
|
eHTMLTags theParent=mBodyContext->Last();
|
|
CElement* theElement=gElementTable->mElements[theParent];
|
|
if(theElement) {
|
|
nsCParserNode theNode(aToken, 0);
|
|
result=theElement->HandleStartToken(&theNode,eHTMLTag_text,mBodyContext,mSink);
|
|
}
|
|
}
|
|
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 COtherDTD::CanContain(PRInt32 aParent,PRInt32 aChild) const {
|
|
CElement *theParent=gElementTable->mElements[eHTMLTags(aParent)];
|
|
if(theParent) {
|
|
CElement *theChild=gElementTable->mElements[eHTMLTags(aChild)];
|
|
if(aChild) {
|
|
if(eHTMLTag_userdefined == aChild)//bug #67007, dont strip userdefined tags
|
|
return PR_TRUE;
|
|
else
|
|
return theParent->CanContain(theChild,mBodyContext);
|
|
}
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|
|
/**
|
|
* Give rest of world access to our tag enums, so that CanContain(), etc,
|
|
* become useful.
|
|
*/
|
|
NS_IMETHODIMP
|
|
COtherDTD::StringTagToIntTag(const nsAString &aTag,
|
|
PRInt32* aIntTag) const
|
|
{
|
|
*aIntTag = nsHTMLTags::LookupTag(aTag);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP_(const PRUnichar *)
|
|
COtherDTD::IntTagToStringTag(PRInt32 aIntTag) const
|
|
{
|
|
const PRUnichar *str_ptr = nsHTMLTags::GetStringValue((nsHTMLTag)aIntTag);
|
|
|
|
NS_ASSERTION(str_ptr, "Bad tag enum passed to COtherDTD::IntTagToStringTag()"
|
|
"!!");
|
|
|
|
return str_ptr;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
COtherDTD::ConvertEntityToUnicode(const nsAString& aEntity,
|
|
PRInt32* aUnicode) const
|
|
{
|
|
*aUnicode = nsHTMLEntities::EntityToUnicode(aEntity);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
/**
|
|
* This method is called to determine whether or not
|
|
* the given childtag is a block element.
|
|
*
|
|
* @update gess 6June2000
|
|
* @param aChildID -- tag id of child
|
|
* @param aParentID -- tag id of parent (or eHTMLTag_unknown)
|
|
* @return PR_TRUE if this tag is a block tag
|
|
*/
|
|
PRBool COtherDTD::IsBlockElement(PRInt32 aChildID,PRInt32 aParentID) const {
|
|
PRBool result=PR_FALSE;
|
|
|
|
if(gElementTable) {
|
|
CElement *theElement=gElementTable->GetElement((eHTMLTags)aChildID);
|
|
result = (theElement) ? theElement->IsBlockElement((eHTMLTags)aParentID) : PR_FALSE;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* This method is called to determine whether or not
|
|
* the given childtag is an inline element.
|
|
*
|
|
* @update gess 6June2000
|
|
* @param aChildID -- tag id of child
|
|
* @param aParentID -- tag id of parent (or eHTMLTag_unknown)
|
|
* @return PR_TRUE if this tag is an inline element
|
|
*/
|
|
PRBool COtherDTD::IsInlineElement(PRInt32 aChildID,PRInt32 aParentID) const {
|
|
PRBool result=PR_FALSE;
|
|
|
|
if(gElementTable) {
|
|
CElement *theElement=gElementTable->GetElement((eHTMLTags)aChildID);
|
|
result = (theElement) ? theElement->IsInlineElement((eHTMLTags)aParentID) : PR_FALSE;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* 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 COtherDTD::IsContainer(PRInt32 aTag) const {
|
|
return gElementTable->mElements[eHTMLTags(aTag)]->IsContainer();
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @update gess5/18/98
|
|
* @param
|
|
* @return
|
|
*/
|
|
nsresult COtherDTD::WillResumeParse(nsIContentSink* aSink) {
|
|
|
|
STOP_TIMER();
|
|
MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: COtherDTD::WillResumeParse(), this=%p\n", this));
|
|
|
|
nsresult result=(aSink) ? aSink->WillResume() : NS_OK;
|
|
|
|
MOZ_TIMER_DEBUGLOG(("Start: Parse Time: COtherDTD::WillResumeParse(), this=%p\n", this));
|
|
START_TIMER();
|
|
|
|
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 COtherDTD::WillInterruptParse(nsIContentSink* aSink){
|
|
|
|
STOP_TIMER();
|
|
MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: COtherDTD::WillInterruptParse(), this=%p\n", this));
|
|
|
|
nsresult result=(aSink) ? aSink->WillInterrupt() : NS_OK;
|
|
|
|
MOZ_TIMER_DEBUGLOG(("Start: Parse Time: COtherDTD::WillInterruptParse(), this=%p\n", this));
|
|
START_TIMER();
|
|
|
|
return result;
|
|
}
|
|
|
|
// CTransitionalDTD is a subclass of COtherDTD that defaults to transitional mode.
|
|
// Used by the editor
|
|
|
|
CTransitionalDTD::CTransitionalDTD()
|
|
{
|
|
if (mBodyContext) mBodyContext->mFlags.mTransitional = PR_TRUE;
|
|
}
|
|
|
|
CTransitionalDTD::~CTransitionalDTD() {}
|
|
|