Back to index

lightning-sunbird  0.9+nobinonly
nsDTDUtils.h
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 
00047 #ifndef DTDUTILS_
00048 #define DTDUTILS_
00049 
00050 #include "nsHTMLTags.h"
00051 #include "nsHTMLTokens.h"
00052 #include "nsIParser.h"
00053 #include "nsCRT.h"
00054 #include "nsDeque.h"
00055 #include "nsIDTD.h"
00056 #include "nsITokenizer.h"
00057 #include "nsString.h"
00058 #include "nsIParserNode.h"
00059 #include "nsFixedSizeAllocator.h"
00060 #include "nsVoidArray.h"
00061 #include "nsIParserService.h"
00062 #include "nsReadableUtils.h"
00063 
00064 #define IF_HOLD(_ptr) \
00065  PR_BEGIN_MACRO       \
00066  if(_ptr) {           \
00067    _ptr->AddRef();    \
00068  }                    \
00069  PR_END_MACRO
00070 
00071 // recycles _ptr
00072 #define IF_FREE(_ptr, _allocator)                \
00073   PR_BEGIN_MACRO                                 \
00074   if(_ptr && _allocator) {                       \
00075     _ptr->Release((_allocator)->GetArenaPool()); \
00076     _ptr=0;                                      \
00077   }                                              \
00078   PR_END_MACRO   
00079 
00080 // release objects and destroy _ptr
00081 #define IF_DELETE(_ptr, _allocator) \
00082   PR_BEGIN_MACRO                    \
00083   if(_ptr) {                        \
00084     _ptr->ReleaseAll(_allocator);   \
00085     delete(_ptr);                   \
00086     _ptr=0;                         \
00087   }                                 \
00088   PR_END_MACRO
00089 
00090 class nsIParserNode;
00091 class nsCParserNode;
00092 class nsNodeAllocator;
00093 
00094 
00095 #ifdef DEBUG
00096 void DebugDumpContainmentRules(nsIDTD& theDTD,const char* aFilename,const char* aTitle);
00097 void DebugDumpContainmentRules2(nsIDTD& theDTD,const char* aFilename,const char* aTitle);
00098 #endif
00099 
00100 /***************************************************************
00101   First, define the tagstack class
00102  ***************************************************************/
00103 
00104 class nsEntryStack;  //forware declare to make compilers happy.
00105 
00106 struct nsTagEntry {
00107   nsTagEntry ()
00108     : mTag(eHTMLTag_unknown), mNode(0), mParent(0), mStyles(0){}
00109   eHTMLTags       mTag;  //for speedier access to tag id
00110   nsCParserNode*  mNode;
00111   nsEntryStack*   mParent;
00112   nsEntryStack*   mStyles;
00113 };
00114 
00115 class nsEntryStack {
00116 
00117 public:
00118                   nsEntryStack();
00119                   ~nsEntryStack();
00120 
00121   nsTagEntry*     PopEntry();
00122   void            PushEntry(nsTagEntry* aEntry, PRBool aRefCntNode = PR_TRUE);
00123   void            EnsureCapacityFor(PRInt32 aNewMax, PRInt32 aShiftOffset=0);
00124   void            Push(nsCParserNode* aNode,nsEntryStack* aStyleStack=0, PRBool aRefCntNode = PR_TRUE);
00125   void            PushFront(nsCParserNode* aNode,nsEntryStack* aStyleStack=0, PRBool aRefCntNode = PR_TRUE);
00126   void            Append(nsEntryStack *aStack);
00127   nsCParserNode*  Pop(void);
00128   nsCParserNode*  Remove(PRInt32 anIndex,eHTMLTags aTag);
00129   nsCParserNode*  NodeAt(PRInt32 anIndex) const;
00130   eHTMLTags       First() const;
00131   eHTMLTags       TagAt(PRInt32 anIndex) const;
00132   nsTagEntry*     EntryAt(PRInt32 anIndex) const;
00133   eHTMLTags       operator[](PRInt32 anIndex) const;
00134   eHTMLTags       Last() const;
00135   void            Empty(void); 
00136 
00137   /*
00138    * Release all objects in the entry stack
00139    */
00140   void ReleaseAll(nsNodeAllocator* aNodeAllocator);
00141   
00148   inline PRInt32 FirstOf(eHTMLTags aTag) const {
00149     PRInt32 index=-1;
00150     
00151     if(0<mCount) {
00152       while(++index<mCount) {
00153         if(aTag==mEntries[index].mTag) {
00154           return index;
00155         }
00156       } //while
00157     }
00158     return kNotFound;
00159   }
00160 
00161 
00168   inline PRInt32 LastOf(eHTMLTags aTag) const {
00169     PRInt32 index=mCount;
00170     while(--index>=0) {
00171         if(aTag==mEntries[index].mTag) {
00172           return index; 
00173         }
00174     }
00175     return kNotFound;
00176   }
00177 
00178   nsTagEntry* mEntries;
00179   PRInt32    mCount;
00180   PRInt32    mCapacity;
00181 };
00182 
00183 
00184 /**********************************************************
00185   The table state class is used to store info about each
00186   table that is opened on the stack. As tables open and
00187   close on the context, we update these objects to track 
00188   what has/hasn't been seen on a per table basis. 
00189  **********************************************************/
00190 class CTableState {
00191 public:
00192   CTableState(CTableState *aPreviousState=0) {
00193     mHasCaption=PR_FALSE;
00194     mHasCols=PR_FALSE;
00195     mHasTHead=PR_FALSE;
00196     mHasTFoot=PR_FALSE;
00197     mHasTBody=PR_FALSE;    
00198     mPrevious=aPreviousState;
00199   }
00200 
00201   PRBool  CanOpenCaption() {
00202     PRBool result=!(mHasCaption || mHasCols || mHasTHead || mHasTFoot || mHasTBody);
00203     return result;
00204   }
00205 
00206   PRBool  CanOpenCols() {
00207     PRBool result=!(mHasCols || mHasTHead || mHasTFoot || mHasTBody);
00208     return result;
00209   }
00210 
00211   PRBool  CanOpenTBody() {
00212     PRBool result=!(mHasTBody);
00213     return result;
00214   }
00215 
00216   PRBool  CanOpenTHead() {
00217     PRBool result=!(mHasTHead || mHasTFoot || mHasTBody);
00218     return result;
00219   }
00220 
00221   PRBool  CanOpenTFoot() {
00222     PRBool result=!(mHasTFoot || mHasTBody);
00223     return result;
00224   }
00225 
00226   PRPackedBool  mHasCaption;
00227   PRPackedBool  mHasCols;
00228   PRPackedBool  mHasTHead;
00229   PRPackedBool  mHasTFoot;
00230   PRPackedBool  mHasTBody;
00231   CTableState   *mPrevious;
00232 };
00233 
00234 /************************************************************************
00235   nsTokenAllocator class implementation.
00236   This class is used to recycle tokens. 
00237   By using this simple class, we cut WAY down on the number of tokens
00238   that get created during the run of the system.
00239 
00240   Note: The allocator is created per document. It's been shared 
00241         ( but not ref. counted ) by objects, tokenizer,dtd,and dtd context,
00242         that cease to exist when the document is destroyed.
00243  ************************************************************************/
00244 class nsTokenAllocator {
00245 public: 
00246 
00247                   nsTokenAllocator();
00248   virtual         ~nsTokenAllocator();
00249   virtual CToken* CreateTokenOfType(eHTMLTokenTypes aType,eHTMLTags aTag, const nsAString& aString);
00250   virtual CToken* CreateTokenOfType(eHTMLTokenTypes aType,eHTMLTags aTag);
00251 
00252   nsFixedSizeAllocator& GetArenaPool() { return mArenaPool; }
00253 
00254 protected:
00255     nsFixedSizeAllocator mArenaPool;
00256 
00257 
00258 #ifdef  NS_DEBUG
00259     int       mTotals[eToken_last-1];
00260 #endif
00261 };
00262 
00263 /************************************************************************
00264   CNodeRecycler class implementation.
00265   This class is used to recycle nodes. 
00266   By using this simple class, we cut down on the number of nodes
00267   that get created during the run of the system.
00268  ************************************************************************/
00269 
00270 #ifndef HEAP_ALLOCATED_NODES
00271 class nsCParserNode;
00272 #endif
00273 
00274 class nsNodeAllocator {
00275 public:
00276   
00277                          nsNodeAllocator();
00278   virtual                ~nsNodeAllocator();
00279   virtual nsCParserNode* CreateNode(CToken* aToken=nsnull, nsTokenAllocator* aTokenAllocator=0);
00280 
00281   nsFixedSizeAllocator&  GetArenaPool() { return mNodePool; }
00282 
00283 #ifdef HEAP_ALLOCATED_NODES
00284   void Recycle(nsCParserNode* aNode) { mSharedNodes.Push(NS_STATIC_CAST(void*,aNode)); }
00285 protected:
00286   nsDeque mSharedNodes;
00287 #ifdef DEBUG_TRACK_NODES
00288   PRInt32 mCount;
00289 #endif
00290 #endif
00291 
00292 protected:
00293   nsFixedSizeAllocator mNodePool;
00294 };
00295 
00296 /************************************************************************
00297   The dtdcontext class defines an ordered list of tags (a context).
00298  ************************************************************************/
00299 
00300 class nsDTDContext {
00301 public:
00302                   nsDTDContext();
00303                   ~nsDTDContext();
00304 
00305   nsTagEntry*     PopEntry();
00306   void            PushEntry(nsTagEntry* aEntry, PRBool aRefCntNode = PR_TRUE);
00307   void            MoveEntries(nsDTDContext& aDest, PRInt32 aCount);
00308   void            Push(nsCParserNode* aNode,nsEntryStack* aStyleStack=0, PRBool aRefCntNode = PR_TRUE);
00309   nsCParserNode*  Pop(nsEntryStack*& aChildStack);
00310   nsCParserNode*  Pop();
00311   nsCParserNode*  PeekNode() { return mStack.NodeAt(mStack.mCount-1); }
00312   eHTMLTags       First(void) const;
00313   eHTMLTags       Last(void) const;
00314   nsTagEntry*     LastEntry(void) const;
00315   eHTMLTags       TagAt(PRInt32 anIndex) const;
00316   eHTMLTags       operator[](PRInt32 anIndex) const {return TagAt(anIndex);}
00317   PRBool          HasOpenContainer(eHTMLTags aTag) const;
00318   PRInt32         FirstOf(eHTMLTags aTag) const {return mStack.FirstOf(aTag);}
00319   PRInt32         LastOf(eHTMLTags aTag) const {return mStack.LastOf(aTag);}
00320 
00321   void            Empty(void); 
00322   PRInt32         GetCount(void) const {return mStack.mCount;}
00323   PRInt32         GetResidualStyleCount(void) {return mResidualStyleCount;}
00324   nsEntryStack*   GetStylesAt(PRInt32 anIndex) const;
00325   void            PushStyle(nsCParserNode* aNode);
00326   void            PushStyles(nsEntryStack *aStyles);
00327   nsCParserNode*  PopStyle(void);
00328   nsCParserNode*  PopStyle(eHTMLTags aTag);
00329   void            RemoveStyle(eHTMLTags aTag);
00330 
00331   static  void    ReleaseGlobalObjects(void);
00332 
00333   void            SetTokenAllocator(nsTokenAllocator* aTokenAllocator) { mTokenAllocator=aTokenAllocator; }
00334   void            SetNodeAllocator(nsNodeAllocator* aNodeAllocator) { mNodeAllocator=aNodeAllocator; }
00335 
00336   nsEntryStack    mStack; //this will hold a list of tagentries...
00337   PRInt32         mResidualStyleCount;
00338   PRInt32         mContextTopIndex;
00339 
00340     //break this struct out separately so that lame compilers don't gack.
00341     //By using these bits instead of bools, we have a bit-o-memory.
00342   struct CFlags {
00343     PRUint8  mHadBody:1;
00344     PRUint8  mHadFrameset:1;
00345     PRUint8  mHasOpenHead:1;
00346     PRUint8  mTransitional:1;
00347   };
00348 
00349   union {
00350     PRUint32  mAllBits;
00351     CFlags    mFlags;
00352   };    
00353   
00354   nsTokenAllocator  *mTokenAllocator;
00355   nsNodeAllocator   *mNodeAllocator;
00356   CTableState       *mTableStates;
00357   nsDeque           mEntities;
00358 
00359 #ifdef  NS_DEBUG
00360   enum { eMaxTags = 100 };
00361   eHTMLTags       mXTags[eMaxTags];
00362 #endif
00363 };
00364 
00365 /**************************************************************
00366   Now define the token deallocator class...
00367  **************************************************************/
00368 class CTokenDeallocator: public nsDequeFunctor{
00369 protected:
00370   nsFixedSizeAllocator& mArenaPool;
00371 
00372 public:
00373   CTokenDeallocator(nsFixedSizeAllocator& aArenaPool)
00374     : mArenaPool(aArenaPool) {}
00375 
00376   virtual void* operator()(void* anObject) {
00377     CToken* aToken = (CToken*)anObject;
00378     CToken::Destroy(aToken, mArenaPool);
00379     return 0;
00380   }
00381 };
00382 
00383 
00384 /************************************************************************
00385   ITagHandler class offers an API for taking care of specific tokens.
00386  ************************************************************************/
00387 class nsITagHandler {
00388 public:
00389   
00390   virtual void          SetString(const nsString &aTheString)=0;
00391   virtual nsString*     GetString()=0;
00392   virtual PRBool        HandleToken(CToken* aToken,nsIDTD* aDTD)=0;
00393   virtual PRBool        HandleCapturedTokens(CToken* aToken,nsIDTD* aDTD)=0;
00394 };
00395 
00396 /************************************************************************
00397   Here are a few useful utility methods...
00398  ************************************************************************/
00399 
00408 inline PRInt32 IndexOfTagInSet(PRInt32 aTag,const eHTMLTags* aTagSet,PRInt32 aCount)  {
00409 
00410   const eHTMLTags* theEnd=aTagSet+aCount;
00411   const eHTMLTags* theTag=aTagSet;
00412 
00413   while(theTag<theEnd) {
00414     if(aTag==*theTag) {
00415       return theTag-aTagSet;
00416     }
00417     ++theTag;
00418   }
00419 
00420   return kNotFound;
00421 }
00422 
00431 inline PRBool FindTagInSet(PRInt32 aTag,const eHTMLTags *aTagSet,PRInt32 aCount)  {
00432   return PRBool(-1<IndexOfTagInSet(aTag,aTagSet,aCount));
00433 }
00434 
00435 /**************************************************************
00436   This defines the topic object used by the observer service.
00437   The observerService uses a list of these, 1 per topic when
00438   registering tags.
00439  **************************************************************/
00440 
00441 class nsObserverEntry : public nsIObserverEntry {
00442 public:
00443   NS_DECL_ISUPPORTS
00444             nsObserverEntry(const nsAString& aString);
00445   virtual   ~nsObserverEntry();
00446 
00447   NS_IMETHOD Notify(nsIParserNode* aNode,
00448                     nsIParser* aParser,
00449                     nsISupports* aWebShell,
00450                     const PRUint32 aFlags);
00451 
00452   nsresult   AddObserver(nsIElementObserver* aObserver,eHTMLTags aTag);
00453   void       RemoveObserver(nsIElementObserver* aObserver);
00454   PRBool     Matches(const nsAString& aTopic);
00455 
00456 protected:
00457   nsAutoString mTopic; // This will rarely be empty, so make it an auto string
00458   nsVoidArray* mObservers[NS_HTML_TAG_MAX + 1];
00459   friend class nsMatchesTopic;
00460 };
00461 
00462 /*********************************************************************************************/
00463 
00464 
00465 struct TagList {
00466   PRUint32 mCount;
00467   const eHTMLTags *mTags;
00468 };
00469 
00477 inline PRInt32 LastOf(nsDTDContext& aContext, const TagList& aTagList){
00478   int max = aContext.GetCount();
00479   int index;
00480   for(index=max-1;index>=0;index--){
00481     PRBool result=FindTagInSet(aContext[index],aTagList.mTags,aTagList.mCount);
00482     if(result) {
00483       return index;
00484     }
00485   }
00486   return kNotFound;
00487 }
00488  
00497 inline PRInt32 FirstOf(nsDTDContext& aContext,PRInt32 aStartOffset,TagList& aTagList){
00498   int max = aContext.GetCount();
00499   int index;
00500   for(index=aStartOffset;index<max;++index){
00501     PRBool result=FindTagInSet(aContext[index],aTagList.mTags,aTagList.mCount);
00502     if(result) {
00503       return index;
00504     }
00505   }
00506   return kNotFound;
00507 }
00508 
00509 
00516 inline PRBool HasOptionalEndTag(eHTMLTags aTag) {
00517   static eHTMLTags gHasOptionalEndTags[]={eHTMLTag_body,eHTMLTag_colgroup,eHTMLTag_dd,eHTMLTag_dt,
00518                                                     eHTMLTag_head,eHTMLTag_li,eHTMLTag_option,
00519                                                     eHTMLTag_p,eHTMLTag_tbody,eHTMLTag_td,eHTMLTag_tfoot,
00520                                                     eHTMLTag_th,eHTMLTag_thead,eHTMLTag_tr,
00521                                                     eHTMLTag_userdefined,eHTMLTag_unknown};
00522   return FindTagInSet(aTag,gHasOptionalEndTags,sizeof(gHasOptionalEndTags)/sizeof(eHTMLTag_body));
00523 }
00524 #endif