source: trunk/third/mozilla/htmlparser/src/COtherDTD.cpp @ 20192

Revision 20192, 29.8 KB checked in by rbasch, 21 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r20191, which included commits to RCS files with non-trunk default branches.
Line 
1/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */   
2/* ***** BEGIN LICENSE BLOCK *****
3 * Version: NPL 1.1/GPL 2.0/LGPL 2.1
4 *
5 * The contents of this file are subject to the Netscape Public License
6 * Version 1.1 (the "License"); you may not use this file except in
7 * compliance with the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/NPL/
9 *
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
14 *
15 * The Original Code is mozilla.org code.
16 *
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
21 *
22 * Contributor(s):
23 *          rickg@netscape.com
24 *
25 *
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 2 or later (the "GPL"), or
28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the NPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the NPL, the GPL or the LGPL.
37 *
38 * ***** END LICENSE BLOCK ***** */
39       
40//#define ENABLE_CRC             
41         
42#include "nsDebug.h"     
43#include "nsIAtom.h"
44#include "COtherDTD.h"
45#include "nsHTMLTokens.h"
46#include "nsCRT.h"     
47#include "nsParser.h" 
48#include "nsIParser.h"
49#include "nsIHTMLContentSink.h" 
50#include "nsScanner.h"
51#include "prenv.h"  //this is here for debug reasons...
52#include "prtypes.h"  //this is here for debug reasons...
53#include "prio.h"
54#include "plstr.h" 
55#include "nsDTDUtils.h"
56#include "nsHTMLTokenizer.h"
57#include "nsTime.h"
58#include "nsParserNode.h"
59#include "nsHTMLEntities.h"
60#include "nsLinebreakConverter.h"
61#include "nsUnicharUtils.h"
62
63#include "prmem.h"
64
65static NS_DEFINE_IID(kIHTMLContentSinkIID, NS_IHTML_CONTENT_SINK_IID);
66static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID);                 
67static NS_DEFINE_IID(kIDTDIID,      NS_IDTD_IID);
68static NS_DEFINE_IID(kClassIID,     NS_IOTHERHTML_DTD_IID);
69static const char kVerificationDir[] = "c:/temp";
70 
71
72#ifdef  ENABLE_CRC
73static char gShowCRC;
74#endif
75
76   
77 
78#ifdef MOZ_PERF_METRICS
79#  define START_TIMER()                    \
80    if(mParser) MOZ_TIMER_START(mParser->mParseTime); \
81    if(mParser) MOZ_TIMER_START(mParser->mDTDTime);
82
83#  define STOP_TIMER()                     \
84    if(mParser) MOZ_TIMER_STOP(mParser->mParseTime); \
85    if(mParser) MOZ_TIMER_STOP(mParser->mDTDTime);
86#else
87#  define STOP_TIMER()
88#  define START_TIMER()
89#endif
90
91
92#include "COtherElements.h"
93
94
95/************************************************************************
96  And now for the main class -- COtherDTD...
97 ************************************************************************/
98
99/**
100 *  This method gets called as part of our COM-like interfaces.
101 *  Its purpose is to create an interface to parser object
102 *  of some type.
103 * 
104 *  @update   gess 4/8/98
105 *  @param    nsIID  id of object to discover
106 *  @param    aInstancePtr ptr to newly discovered interface
107 *  @return   NS_xxx result code
108 */ 
109nsresult COtherDTD::QueryInterface(const nsIID& aIID, void** aInstancePtr) 
110{                                                                         
111  if (NULL == aInstancePtr) {                                           
112    return NS_ERROR_NULL_POINTER;                                       
113  }                                                                     
114
115  if(aIID.Equals(kISupportsIID))    {  //do IUnknown...
116    *aInstancePtr = (nsIDTD*)(this);                                       
117  }
118  else if(aIID.Equals(kIDTDIID)) {  //do IParser base class...
119    *aInstancePtr = (nsIDTD*)(this);                                       
120  }
121  else if(aIID.Equals(kClassIID)) {  //do this class...
122    *aInstancePtr = (COtherDTD*)(this);                                       
123  }                 
124  else {
125    *aInstancePtr=0;
126    return NS_NOINTERFACE;
127  }
128  NS_ADDREF_THIS();
129  return NS_OK;                                                       
130}
131
132NS_IMPL_ADDREF(COtherDTD)
133NS_IMPL_RELEASE(COtherDTD)
134
135/**
136 *  Default constructor
137 *   
138 *  @update  gess 4/9/98
139 *  @param   
140 *  @return   
141 */
142COtherDTD::COtherDTD() : nsIDTD() {
143  mSink = 0;
144  mParser=0;       
145  mLineNumber=1; 
146  mHasOpenBody=PR_FALSE;
147  mHasOpenHead=0;
148  mHasOpenForm=PR_FALSE;
149  mHasOpenMap=PR_FALSE;
150  mTokenizer=0;
151  mTokenAllocator=0;
152  mComputedCRC32=0;
153  mExpectedCRC32=0;
154  mDTDState=NS_OK;
155  mDocType=eHTML_Strict;
156  mHadFrameset=PR_FALSE;
157  mHadBody=PR_FALSE;
158  mHasOpenScript=PR_FALSE;
159  mParserCommand=eViewNormal;
160  mNodeAllocator=new nsNodeAllocator();
161  mBodyContext=new nsDTDContext();
162
163#if 0 //set this to 1 if you want strictDTD to be based on the environment setting.
164  char* theEnvString = PR_GetEnv("MOZ_DISABLE_STRICT");
165  mEnableStrict=PRBool(0==theEnvString);
166#else
167  mEnableStrict=PR_TRUE;
168#endif
169
170  if(!gElementTable) {
171    gElementTable = new CElementTable();
172  }
173}
174
175/**
176 *
177 * @update  gess1/8/99
178 * @param
179 * @return
180 */
181const nsIID& COtherDTD::GetMostDerivedIID(void)const {
182  return kClassIID;
183}
184 
185/**
186 *  Default destructor
187 * 
188 *  @update  gess 4/9/98
189 *  @param   
190 *  @return 
191 */
192COtherDTD::~COtherDTD(){
193  delete mBodyContext;
194
195  if(mNodeAllocator) {
196    delete mNodeAllocator;
197    mNodeAllocator=nsnull;
198  }
199
200  NS_IF_RELEASE(mSink);
201}
202 
203/**
204 *  This method is defined in nsIParser. It is used to
205 *  cause the COM-like construction of an nsParser.
206 * 
207 *  @update  gess 4/8/98
208 *  @param   nsIParser** ptr to newly instantiated parser
209 *  @return  NS_xxx error result
210 */
211nsresult NS_NewOtherHTMLDTD(nsIDTD** aInstancePtrResult) {
212  COtherDTD* it = new COtherDTD();
213
214  if (it == 0) {
215    return NS_ERROR_OUT_OF_MEMORY;
216  }
217
218  return it->QueryInterface(kClassIID, (void **) aInstancePtrResult);
219}
220
221/**
222 * Call this method if you want the DTD to construct a fresh
223 * instance of itself.
224 * @update  gess7/23/98
225 * @param
226 * @return
227 */
228NS_IMETHODIMP
229COtherDTD::CreateNewInstance(nsIDTD** aInstancePtrResult)
230{
231  nsresult result=NS_NewOtherHTMLDTD(aInstancePtrResult);
232
233  if(aInstancePtrResult) {
234    COtherDTD *theOtherDTD=(COtherDTD*)*aInstancePtrResult;
235    if(theOtherDTD) {
236      theOtherDTD->mDTDMode=mDTDMode;
237      theOtherDTD->mParserCommand=mParserCommand;
238      theOtherDTD->mDocType=mDocType;
239      theOtherDTD->mEnableStrict=mEnableStrict;
240    }
241  }
242
243  return result;
244}
245
246/**
247 * This method is called to determine if the given DTD can parse
248 * a document in a given source-type.
249 * NOTE: Parsing always assumes that the end result will involve
250 *       storing the result in the main content model.
251 * @update  gess6/24/98
252 * @param   
253 * @return  TRUE if this DTD can satisfy the request; FALSE otherwise.
254 */
255NS_IMETHODIMP_(eAutoDetectResult)
256COtherDTD::CanParse(CParserContext& aParserContext, const nsString& aBuffer,
257                    PRInt32 aVersion)
258{
259  eAutoDetectResult result=eUnknownDetect;
260
261  if(mEnableStrict) {
262    if(aParserContext.mParserCommand != eViewSource) {
263      if(PR_TRUE==aParserContext.mMimeType.EqualsWithConversion(kPlainTextContentType)) {
264        result=eValidDetect;
265      }
266      else if(PR_TRUE==aParserContext.mMimeType.EqualsWithConversion(kHTMLTextContentType)) {
267        switch(aParserContext.mDTDMode) {
268          case eDTDMode_full_standards:
269          case eDTDMode_almost_standards:
270            result=ePrimaryDetect;
271            break;
272          default:
273            result=eValidDetect;
274            break;
275        }
276      }
277      else {
278        //otherwise, look into the buffer to see if you recognize anything...
279        PRBool theBufHasXML=PR_FALSE;
280        if(BufferContainsHTML(aBuffer,theBufHasXML)){
281          result = eValidDetect ;
282          if(0==aParserContext.mMimeType.Length()) {
283            aParserContext.SetMimeType(NS_LITERAL_CSTRING(kHTMLTextContentType));
284            if(!theBufHasXML) {
285              switch(aParserContext.mDTDMode) {
286                case eDTDMode_full_standards:
287                case eDTDMode_almost_standards:
288                  result=ePrimaryDetect;
289                  break;
290                default:
291                  result=eValidDetect;
292                  break;
293              }
294            }
295            else result=eValidDetect;
296          }
297        }
298      }
299    }
300  }
301  return result;
302}
303
304 
305/**
306  * The parser uses a code sandwich to wrap the parsing process. Before
307  * the process begins, WillBuildModel() is called. Afterwards the parser
308  * calls DidBuildModel().
309  * @update     rickg 03.20.2000
310  * @param      aParserContext
311  * @param      aSink
312  * @return     error code (almost always 0)
313  */
314nsresult COtherDTD::WillBuildModel(const CParserContext& aParserContext,
315                                   nsITokenizer* aTokenizer,
316                                   nsIContentSink* aSink){
317  nsresult result=NS_OK;
318
319  mFilename=aParserContext.mScanner->GetFilename();
320  mHasOpenBody=PR_FALSE;
321  mHadFrameset=PR_FALSE;
322  mLineNumber=1;
323  mHasOpenScript=PR_FALSE;
324  mDTDMode=aParserContext.mDTDMode;
325  mParserCommand=aParserContext.mParserCommand;
326  mTokenizer = aTokenizer;
327
328  if((!aParserContext.mPrevContext) && (aSink)) {
329
330    STOP_TIMER();
331    MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: COtherDTD::WillBuildModel(), this=%p\n", this));
332
333    mDocType=aParserContext.mDocType;
334    mBodyContext->mFlags.mTransitional=PR_FALSE;
335
336    if(aSink && (!mSink)) {
337      result=aSink->QueryInterface(kIHTMLContentSinkIID, (void **)&mSink);
338    }
339
340    if(result==NS_OK) {
341      result = aSink->WillBuildModel();
342
343#ifdef DEBUG
344      mBodyContext->ResetCounters();
345#endif
346
347      MOZ_TIMER_DEBUGLOG(("Start: Parse Time: COtherDTD::WillBuildModel(), this=%p\n", this));
348      START_TIMER();
349
350      mSkipTarget=eHTMLTag_unknown;
351      mComputedCRC32=0;
352      mExpectedCRC32=0;
353    }
354  }
355
356  return result;
357}
358
359
360/**
361  * This is called when it's time to read as many tokens from the tokenizer
362  * as you can. Not all tokens may make sense, so you may not be able to
363  * read them all (until more come in later).
364  *
365  * @update gess5/18/98
366  * @param  aParser is the parser object that's driving this process
367  * @return error code (almost always NS_OK)
368  */
369nsresult COtherDTD::BuildModel(nsIParser* aParser,nsITokenizer* aTokenizer,nsITokenObserver* anObserver,nsIContentSink* aSink) {
370  nsresult result=NS_OK;
371
372  if(aTokenizer) {
373    nsITokenizer*  oldTokenizer=mTokenizer;
374    mTokenizer=aTokenizer;
375    mParser=(nsParser*)aParser;
376
377    if(mTokenizer) {
378
379      mTokenAllocator=mTokenizer->GetTokenAllocator();
380     
381      mBodyContext->SetTokenAllocator(mTokenAllocator);
382      mBodyContext->SetNodeAllocator(mNodeAllocator);
383
384      if(mSink) {
385
386        if(!mBodyContext->GetCount()) {
387            //if the content model is empty, then begin by opening <html>...
388          CStartToken *theToken=(CStartToken*)mTokenAllocator->CreateTokenOfType(eToken_start,eHTMLTag_html,NS_LITERAL_STRING("html"));
389          HandleStartToken(theToken); //this token should get pushed on the context stack, don't recycle it.
390        }
391
392        while(NS_SUCCEEDED(result)){
393
394          if(mDTDState!=NS_ERROR_HTMLPARSER_STOPPARSING) {
395            CToken* theToken=mTokenizer->PopToken();
396            if(theToken) {
397              result=HandleToken(theToken,aParser);
398            }
399            else break;
400          }
401          else {
402            result=mDTDState;
403            break;
404          }
405        }//while
406        mTokenizer=oldTokenizer;
407      }
408    }
409  }
410  else result=NS_ERROR_HTMLPARSER_BADTOKENIZER;
411  return result;
412}
413 
414/**
415 *
416 * @update  gess5/18/98
417 * @param
418 * @return
419 */
420nsresult COtherDTD::DidBuildModel(nsresult anErrorCode,PRBool aNotifySink,nsIParser* aParser,nsIContentSink* aSink){
421  nsresult result=NS_OK;
422
423  if(aSink) { 
424
425    if(aParser && (NS_OK==result)){
426      if(aNotifySink){
427        if((NS_OK==anErrorCode) && (mBodyContext->GetCount()>0)) {
428
429          PRInt32 theIndex=mBodyContext->GetCount()-1;
430          eHTMLTags theChild = mBodyContext->TagAt(theIndex);
431          while (theIndex>0) {             
432            eHTMLTags theParent = mBodyContext->TagAt(--theIndex);
433            CElement *theElement = gElementTable->mElements[theParent];
434            nsCParserNode *theNode = mBodyContext->PeekNode();
435            theElement->HandleEndToken(theNode,theChild,mBodyContext,mSink);
436            theChild = theParent;
437          }
438
439          nsEntryStack*  theChildStyles = 0;
440          nsCParserNode* theNode = (nsCParserNode*)mBodyContext->Pop(theChildStyles);   
441          if (theNode) {
442            mSink->CloseHTML();
443          }
444          NS_ASSERTION(!theChildStyles, "there should no residual style information in this dtd");
445          IF_DELETE(theChildStyles, mNodeAllocator);
446        }       
447        else {
448          //If you're here, then an error occured, but we still have nodes on the stack.
449          //At a minimum, we should grab the nodes and recycle them.
450          //Just to be correct, we'll also recycle the nodes.
451 
452          while (mBodyContext->GetCount() > 0) { 
453 
454            nsEntryStack *theChildStyles = 0;
455            nsCParserNode* theNode = (nsCParserNode*)mBodyContext->Pop(theChildStyles);
456            if (theNode) {
457              theNode->mUseCount = 0;
458              if (theChildStyles) {
459                delete theChildStyles;
460              }
461              IF_FREE(theNode, mNodeAllocator);
462            }
463            NS_ASSERTION(!theChildStyles, "there should no residual style information in this dtd");
464            IF_DELETE(theChildStyles, mNodeAllocator);
465          }   
466        }   
467 
468      }
469    } //if aparser 
470
471      //No matter what, you need to call did build model.
472    result = aSink->DidBuildModel();
473
474  } //if asink
475  return result;
476
477
478NS_IMETHODIMP_(void) 
479COtherDTD::Terminate()
480{
481  mDTDState = NS_ERROR_HTMLPARSER_STOPPARSING;
482}
483
484NS_IMETHODIMP_(PRInt32) 
485COtherDTD::GetType()
486{
487  return NS_IPARSER_FLAG_HTML;
488}
489
490NS_IMETHODIMP
491COtherDTD::CollectSkippedContent(PRInt32 aTag, nsAString& aContent, PRInt32 &aLineNo)
492{
493  return NS_OK;
494}
495
496/**
497 *  This big dispatch method is used to route token handler calls to the right place.
498 *  What's wrong with it? This table, and the dispatch methods themselves need to be
499 *  moved over to the delegate. Ah, so much to do...
500 * 
501 *  @update  gess 12/1/99
502 *  @param   aToken
503 *  @param   aParser   
504 *  @return   
505 */
506nsresult COtherDTD::HandleToken(CToken* aToken,nsIParser* aParser){
507  nsresult  result=NS_OK;
508
509  if(aToken) {
510    CHTMLToken*     theToken= (CHTMLToken*)(aToken);
511    eHTMLTokenTypes theType=eHTMLTokenTypes(theToken->GetTokenType());
512
513//    theToken->mUseCount=0;  //assume every token coming into this system needs recycling.
514
515    mParser=(nsParser*)aParser;
516
517    switch(theType) {
518      case eToken_text:
519      case eToken_start:
520      case eToken_whitespace:
521      case eToken_newline:
522      case eToken_doctypeDecl:
523      case eToken_markupDecl:
524        result=HandleStartToken(theToken); break;
525
526      case eToken_entity:
527        result=HandleEntityToken(theToken); break;
528
529      case eToken_end:
530        result=HandleEndToken(theToken); break;
531       
532      default:
533        break;
534    }//switch
535
536
537    if(NS_SUCCEEDED(result) || (NS_ERROR_HTMLPARSER_BLOCK==result)) {
538      IF_FREE(theToken, mTokenAllocator);
539    }
540    else if(result==NS_ERROR_HTMLPARSER_STOPPARSING)
541      mDTDState=result;
542    else return NS_OK;
543
544  }//if
545  return result;
546}
547
548 
549/**
550 * This gets called after we've handled a given start tag.
551 * It's a generic hook to let us to post processing.
552 * @param   aToken contains the tag in question
553 * @param   aTag is the tag itself.
554 * @return  status
555 */
556nsresult COtherDTD::DidHandleStartTag(nsIParserNode& aNode,eHTMLTags aChildTag){
557  nsresult result=NS_OK;
558
559  switch(aChildTag){
560
561    case eHTMLTag_script:
562      mHasOpenScript=PR_TRUE;
563      break;   
564
565    case eHTMLTag_pre:
566    case eHTMLTag_listing:
567      {
568        CToken* theNextToken=mTokenizer->PeekToken();
569        if(theNextToken)  {
570          eHTMLTokenTypes theType=eHTMLTokenTypes(theNextToken->GetTokenType());
571          if(eToken_newline==theType){
572            ++mLineNumber;
573            mTokenizer->PopToken();  //skip 1st newline inside PRE and LISTING
574          }//if
575        }//if
576      }
577      break;
578
579#ifdef DEBUG
580    case eHTMLTag_meta:
581      {
582          //we should only enable user-defined entities in debug builds...
583
584        PRInt32 theCount=aNode.GetAttributeCount();
585        const nsAString* theNamePtr=0;
586        const nsAString* theValuePtr=0;
587
588        if(theCount) {
589          PRInt32 theIndex=0;
590          for(theIndex=0;theIndex<theCount;++theIndex){
591            const nsAString& theKey = aNode.GetKeyAt(theIndex);
592            if(theKey.Equals(NS_LITERAL_STRING("ENTITY"), nsCaseInsensitiveStringComparator())) {
593              const nsAString& theName=aNode.GetValueAt(theIndex);
594              theNamePtr=&theName;
595            }
596            else if(theKey.Equals(NS_LITERAL_STRING("VALUE"), nsCaseInsensitiveStringComparator())) {
597              //store the named enity with the context...
598              const nsAString& theValue=aNode.GetValueAt(theIndex);
599              theValuePtr=&theValue;
600            }
601          }
602        }
603        if(theNamePtr && theValuePtr) {
604          mBodyContext->RegisterEntity(*theNamePtr,*theValuePtr);
605        }
606      }
607      break;
608#endif
609
610    default:
611      break;
612  }//switch
613
614  return result;
615}
616 
617/**
618 * This gets called before we've handled a given start tag.
619 * It's a generic hook to let us do pre processing.
620 * @param   aToken contains the tag in question
621 * @param   aChildTag is the tag itself.
622 * @param   aNode is the node (tag) with associated attributes.
623 * @return  TRUE if tag processing should continue; FALSE if the tag has been handled.
624 */
625nsresult COtherDTD::WillHandleStartTag(CToken* aToken,eHTMLTags aTag,nsIParserNode& aNode){
626  nsresult result=NS_OK;
627
628    //first let's see if there's some skipped content to deal with...
629#if 0
630  PRInt32 theAttrCount  = aNode.GetAttributeCount();
631  if(*gElementTable->mElements[aTag].mSkipTarget) {
632    result=CollectSkippedContent(aNode,theAttrCount);
633  }
634#endif
635 
636  STOP_TIMER()
637  MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: COtherDTD::WillHandleStartTag(), this=%p\n", this));
638
639  if(mParser) {
640
641    switch(aTag) {
642      case eHTMLTag_newline:
643        ++mLineNumber;
644        break;
645      default:
646        break;
647    }
648    mSink->NotifyTagObservers(&aNode);
649  }
650
651  MOZ_TIMER_DEBUGLOG(("Start: Parse Time: COtherDTD::WillHandleStartTag(), this=%p\n", this));
652  START_TIMER()
653
654  return result;
655}
656 
657
658/**
659 *  This method gets called when a start token has been
660 *  encountered in the parse process. If the current container
661 *  can contain this tag, then add it. Otherwise, you have
662 *  two choices: 1) create an implicit container for this tag
663 *                  to be stored in
664 *               2) close the top container, and add this to
665 *                  whatever container ends up on top.
666 *   
667 *  @update  gess 1/04/99
668 *  @param   aToken -- next (start) token to be handled
669 *  @param   aNode -- CParserNode representing this start token
670 *  @return  PR_TRUE if all went well; PR_FALSE if error occured
671 */     
672nsresult COtherDTD::HandleStartToken(CToken* aToken) { 
673
674  //Begin by gathering up attributes... 
675 
676  nsresult  result=NS_OK;
677  nsCParserNode* theNode=mNodeAllocator->CreateNode(aToken, mTokenAllocator);
678  if(theNode) {
679   
680    eHTMLTags     theChildTag=(eHTMLTags)aToken->GetTypeID();
681    PRInt16       attrCount=aToken->GetAttributeCount();
682    eHTMLTags     theParent=mBodyContext->Last();
683   
684    result=(0==attrCount) ? NS_OK : CollectAttributes(*theNode,theChildTag,attrCount);
685 
686    if(NS_OK==result) {
687      result=WillHandleStartTag(aToken,theChildTag,*theNode);
688      if(NS_OK==result) {
689 
690        mLineNumber += aToken->GetNewlineCount();
691 
692        PRBool theTagWasHandled=PR_FALSE;
693 
694        switch(theChildTag) {       
695           
696          case eHTMLTag_html: 
697            if(!mBodyContext->HasOpenContainer(theChildTag)){
698              mSink->OpenHTML(*theNode);
699              mBodyContext->Push(theNode, 0, PR_FALSE);
700            }
701            theTagWasHandled=PR_TRUE;   
702            break;         
703                 
704          default:   
705            CElement* theElement=gElementTable->mElements[theParent];
706            if(theElement) {
707              result=theElement->HandleStartToken(theNode,theChildTag,mBodyContext,mSink); 
708              theTagWasHandled=PR_TRUE; 
709            }   
710            break;   
711        }//switch             
712         
713        if(theTagWasHandled) {
714          DidHandleStartTag(*theNode,theChildTag); 
715        }
716   
717      } //if             
718    }//if           
719    IF_FREE(theNode, mNodeAllocator);
720  }
721  else result=NS_ERROR_OUT_OF_MEMORY;
722         
723  return result;       
724}     
725 
726/**
727 *  This method gets called when an end token has been   
728 *  encountered in the parse process. If the end tag matches
729 *  the start tag on the stack, then simply close it. Otherwise,
730 *  we have a erroneous state condition. This can be because we
731 *  have a close tag with no prior open tag (user error) or because
732 *  we screwed something up in the parse process. I'm not sure
733 *  yet how to tell the difference.
734 *   
735 *  @update  gess 3/25/98 
736 *  @param   aToken -- next (start) token to be handled
737 *  @return  PR_TRUE if all went well; PR_FALSE if error occured
738 */
739nsresult COtherDTD::HandleEndToken(CToken* aToken) {
740  nsresult    result=NS_OK;
741  eHTMLTags   theChildTag=(eHTMLTags)aToken->GetTypeID();
742 
743  switch(theChildTag) {   
744 
745    case eHTMLTag_body: //we intentionally don't let the user close HTML or BODY
746    case eHTMLTag_html:   
747      break;   
748       
749    case eHTMLTag_script:       
750      mHasOpenScript=PR_FALSE;         
751     
752    default:
753      PRInt32 theCount=mBodyContext->GetCount();
754      eHTMLTags theParent=mBodyContext->TagAt(theCount-1);
755      if(theChildTag==theParent) {
756        theParent=mBodyContext->TagAt(theCount-2);
757      }
758      CElement* theElement=gElementTable->mElements[theParent];
759      if(theElement) {
760        nsCParserNode* theNode=mNodeAllocator->CreateNode(aToken, mTokenAllocator);
761        if(theNode) {
762          result=theElement->HandleEndToken(theNode,theChildTag,mBodyContext,mSink);
763          IF_FREE(theNode, mNodeAllocator);
764        }
765      }   
766      break;
767  }     
768   
769  return result;           
770}         
771         
772/**
773 * Retrieve the attributes for this node, and add then into
774 * the node.
775 *
776 * @update  gess4/22/98
777 * @param   aNode is the node you want to collect attributes for
778 * @param   aCount is the # of attributes you're expecting
779 * @return error code (should be 0)
780 */
781nsresult COtherDTD::CollectAttributes(nsIParserNode& aNode,eHTMLTags aTag,PRInt32 aCount){
782  int attr=0;
783
784  nsresult result=NS_OK;
785  int theAvailTokenCount=mTokenizer->GetCount();
786  if(aCount<=theAvailTokenCount) {
787    //gElementTable->mElements[aTag]->GetSkipTarget();
788    CToken* theToken=0;
789    for(attr=0;attr<aCount;++attr){ 
790      theToken=mTokenizer->PopToken();
791      if(theToken)  {
792        // Sanitize the key for it might contain some non-alpha-non-digit characters
793        // at its end.  Ex. <OPTION SELECTED/> - This will be tokenized as "<" "OPTION",
794        // "SELECTED/", and ">". In this case the "SELECTED/" key will be sanitized to
795        // a legitimate "SELECTED" key.
796        ((CAttributeToken*)theToken)->SanitizeKey();
797 
798        aNode.AddAttribute(theToken);
799      }
800    }
801  }
802  else {
803    result=kEOF;
804  }
805  return result;
806}
807
808/**
809 *  This method gets called when an entity token has been
810 *  encountered in the parse process.
811 * 
812 *  @update  gess 3/25/98
813 *  @param   aToken -- next (start) token to be handled
814 *  @return  PR_TRUE if all went well; PR_FALSE if error occured
815 */
816nsresult COtherDTD::HandleEntityToken(CToken* aToken) {
817  nsresult  result=NS_OK;
818
819  nsAutoString theStr;
820  aToken->GetSource(theStr);
821  PRUnichar theChar=theStr.CharAt(0);
822  CToken    *theToken=0;
823
824  if((kHashsign!=theChar) && (-1==nsHTMLEntities::EntityToUnicode(theStr))){
825
826#ifdef DEBUG
827    //before we just toss this away as a bogus entity, let's check...
828    CNamedEntity *theEntity=mBodyContext->GetEntity(theStr);
829    if(theEntity) {
830      theToken=(CTextToken*)mTokenAllocator->CreateTokenOfType(eToken_text,eHTMLTag_text,theEntity->mValue);
831    }
832    else {
833#endif
834      //if you're here we have a bogus entity.
835      //convert it into a text token.
836      nsAutoString entityName;
837      entityName.Assign(NS_LITERAL_STRING("&"));
838      entityName.Append(theStr); //should append the entity name; fix bug 51161.
839      theToken=(CTextToken*)mTokenAllocator->CreateTokenOfType(eToken_text,eHTMLTag_text,entityName);
840#ifdef DEBUG
841    }
842#endif
843    result=HandleStartToken(theToken);
844  }
845  else {
846
847    //add this code to fix bug 42629 (entities were getting dropped).
848    eHTMLTags theParent=mBodyContext->Last();
849    CElement* theElement=gElementTable->mElements[theParent];
850    if(theElement) {
851      nsCParserNode theNode(aToken, 0);
852      result=theElement->HandleStartToken(&theNode,eHTMLTag_text,mBodyContext,mSink); 
853    }
854  }
855  return result;
856}
857           
858 /***********************************************************************************
859   The preceeding tables determine the set of elements each tag can contain...
860  ***********************************************************************************/
861     
862/**
863 *  This method is called to determine whether or not a tag
864 *  of one type can contain a tag of another type.
865 * 
866 *  @update  gess 4/8/98
867 *  @param   aParent -- tag enum of parent container
868 *  @param   aChild -- tag enum of child container
869 *  @return  PR_TRUE if parent can contain child
870 */
871PRBool COtherDTD::CanContain(PRInt32 aParent,PRInt32 aChild) const {
872  CElement *theParent=gElementTable->mElements[eHTMLTags(aParent)];
873  if(theParent) {
874    CElement *theChild=gElementTable->mElements[eHTMLTags(aChild)];
875    if(aChild) {
876      if(eHTMLTag_userdefined == aChild)//bug #67007, dont strip userdefined tags
877        return PR_TRUE;                 
878      else
879        return theParent->CanContain(theChild,mBodyContext);
880    }
881  }
882  return PR_FALSE;
883}
884
885/**
886 * Give rest of world access to our tag enums, so that CanContain(), etc,
887 * become useful.
888 */
889NS_IMETHODIMP
890COtherDTD::StringTagToIntTag(const nsAString &aTag,
891                             PRInt32* aIntTag) const
892{
893  *aIntTag = nsHTMLTags::LookupTag(aTag);
894
895  return NS_OK;
896}
897
898NS_IMETHODIMP_(const PRUnichar *)
899COtherDTD::IntTagToStringTag(PRInt32 aIntTag) const
900{
901  const PRUnichar *str_ptr = nsHTMLTags::GetStringValue((nsHTMLTag)aIntTag);
902
903  NS_ASSERTION(str_ptr, "Bad tag enum passed to COtherDTD::IntTagToStringTag()"
904               "!!");
905
906  return str_ptr;
907
908
909NS_IMETHODIMP_(nsIAtom *)
910COtherDTD::IntTagToAtom(PRInt32 aIntTag) const
911{
912  nsIAtom *atom = nsHTMLTags::GetAtom((nsHTMLTag)aIntTag);
913
914  NS_ASSERTION(atom, "Bad tag enum passed to COtherDTD::IntTagToAtom()"
915               "!!");
916
917  return atom;
918
919
920/**
921 *  This method is called to determine whether or not
922 *  the given childtag is a block element.
923 *
924 *  @update  gess 6June2000
925 *  @param   aChildID -- tag id of child
926 *  @param   aParentID -- tag id of parent (or eHTMLTag_unknown)
927 *  @return  PR_TRUE if this tag is a block tag
928 */
929PRBool COtherDTD::IsBlockElement(PRInt32 aChildID,PRInt32 aParentID) const {
930  PRBool result=PR_FALSE;
931
932  if(gElementTable) {
933    CElement *theElement=gElementTable->GetElement((eHTMLTags)aChildID);
934    result = (theElement) ? theElement->IsBlockElement((eHTMLTags)aParentID) : PR_FALSE;
935  }
936  return result;
937}
938
939/**
940 *  This method is called to determine whether or not
941 *  the given childtag is an inline element.
942 *
943 *  @update  gess 6June2000
944 *  @param   aChildID -- tag id of child
945 *  @param   aParentID -- tag id of parent (or eHTMLTag_unknown)
946 *  @return  PR_TRUE if this tag is an inline element
947 */
948PRBool COtherDTD::IsInlineElement(PRInt32 aChildID,PRInt32 aParentID) const {
949  PRBool result=PR_FALSE;
950
951  if(gElementTable) {
952    CElement *theElement=gElementTable->GetElement((eHTMLTags)aChildID);
953    result = (theElement) ? theElement->IsInlineElement((eHTMLTags)aParentID) : PR_FALSE;
954  }
955  return result;
956}
957     
958/**
959 *  This method gets called to determine whether a given
960 *  tag is itself a container
961 *   
962 *  @update  gess 4/8/98
963 *  @param   aTag -- tag to test as a container
964 *  @return  PR_TRUE if given tag can contain other tags
965 */
966PRBool COtherDTD::IsContainer(PRInt32 aTag) const {
967  return gElementTable->mElements[eHTMLTags(aTag)]->IsContainer();
968}
969 
970/**
971 *
972 * @update  gess5/18/98
973 * @param
974 * @return
975 */
976nsresult COtherDTD::WillResumeParse(nsIContentSink* aSink) {
977
978  STOP_TIMER();
979  MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: COtherDTD::WillResumeParse(), this=%p\n", this));
980
981  nsresult result=(aSink) ? aSink->WillResume() : NS_OK;
982
983  MOZ_TIMER_DEBUGLOG(("Start: Parse Time: COtherDTD::WillResumeParse(), this=%p\n", this));
984  START_TIMER();
985
986  return result;
987}
988
989/**
990 * This method gets called when the parsing process is interrupted
991 * due to lack of data (waiting for netlib).
992 * @update  gess5/18/98
993 * @return  error code
994 */
995nsresult COtherDTD::WillInterruptParse(nsIContentSink* aSink){
996
997  STOP_TIMER();
998  MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: COtherDTD::WillInterruptParse(), this=%p\n", this));
999
1000  nsresult result=(aSink) ? aSink->WillInterrupt() : NS_OK;
1001
1002  MOZ_TIMER_DEBUGLOG(("Start: Parse Time: COtherDTD::WillInterruptParse(), this=%p\n", this));
1003  START_TIMER();
1004
1005  return result;
1006}
1007
1008// CTransitionalDTD is a subclass of COtherDTD that defaults to transitional mode. 
1009// Used by the editor
1010
1011CTransitionalDTD::CTransitionalDTD()
1012{
1013  if (mBodyContext) mBodyContext->mFlags.mTransitional = PR_TRUE;
1014}
1015
1016CTransitionalDTD::~CTransitionalDTD() {}
1017
Note: See TracBrowser for help on using the repository browser.