Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / parser / htmlparser / src / nsDTDUtils.h
blobce15d4ff925c5e69cbc6ae9e1cb1878d8e84f32b
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 sw=2 et tw=80: */
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998
21 * the Initial Developer. All Rights Reserved.
23 * Contributor(s):
25 * Alternatively, the contents of this file may be used under the terms of
26 * either of the GNU General Public License Version 2 or later (the "GPL"),
27 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
40 /**
41 * MODULE NOTES:
42 * @update gess 4/1/98
48 #ifndef DTDUTILS_
49 #define DTDUTILS_
51 #include "nsHTMLTags.h"
52 #include "nsHTMLTokens.h"
53 #include "nsIParser.h"
54 #include "nsCRT.h"
55 #include "nsDeque.h"
56 #include "nsIDTD.h"
57 #include "nsITokenizer.h"
58 #include "nsString.h"
59 #include "nsIParserNode.h"
60 #include "nsFixedSizeAllocator.h"
61 #include "nsVoidArray.h"
62 #include "nsIParserService.h"
63 #include "nsReadableUtils.h"
64 #include "nsIHTMLContentSink.h"
66 #define IF_HOLD(_ptr) \
67 PR_BEGIN_MACRO \
68 if(_ptr) { \
69 _ptr->AddRef(); \
70 } \
71 PR_END_MACRO
73 // recycles _ptr
74 #define IF_FREE(_ptr, _allocator) \
75 PR_BEGIN_MACRO \
76 if(_ptr && _allocator) { \
77 _ptr->Release((_allocator)->GetArenaPool()); \
78 _ptr=0; \
79 } \
80 PR_END_MACRO
82 // release objects and destroy _ptr
83 #define IF_DELETE(_ptr, _allocator) \
84 PR_BEGIN_MACRO \
85 if(_ptr) { \
86 _ptr->ReleaseAll(_allocator); \
87 delete(_ptr); \
88 _ptr=0; \
89 } \
90 PR_END_MACRO
92 class nsIParserNode;
93 class nsCParserNode;
94 class nsNodeAllocator;
97 #ifdef DEBUG
98 void DebugDumpContainmentRules(nsIDTD& theDTD,const char* aFilename,const char* aTitle);
99 void DebugDumpContainmentRules2(nsIDTD& theDTD,const char* aFilename,const char* aTitle);
100 #endif
102 /***************************************************************
103 First, define the tagstack class
104 ***************************************************************/
106 class nsEntryStack; //forware declare to make compilers happy.
108 struct nsTagEntry {
109 nsTagEntry ()
110 : mTag(eHTMLTag_unknown), mNode(0), mParent(0), mStyles(0){}
111 eHTMLTags mTag; //for speedier access to tag id
112 nsCParserNode* mNode;
113 nsEntryStack* mParent;
114 nsEntryStack* mStyles;
117 class nsEntryStack {
119 public:
120 nsEntryStack();
121 ~nsEntryStack();
123 nsTagEntry* PopEntry();
124 void PushEntry(nsTagEntry* aEntry, PRBool aRefCntNode = PR_TRUE);
125 void EnsureCapacityFor(PRInt32 aNewMax, PRInt32 aShiftOffset=0);
126 void Push(nsCParserNode* aNode,nsEntryStack* aStyleStack=0, PRBool aRefCntNode = PR_TRUE);
127 void PushTag(eHTMLTags aTag);
128 void PushFront(nsCParserNode* aNode,nsEntryStack* aStyleStack=0, PRBool aRefCntNode = PR_TRUE);
129 void Append(nsEntryStack *aStack);
130 nsCParserNode* Pop(void);
131 nsCParserNode* Remove(PRInt32 anIndex,eHTMLTags aTag);
132 nsCParserNode* NodeAt(PRInt32 anIndex) const;
133 eHTMLTags First() const;
134 eHTMLTags TagAt(PRInt32 anIndex) const;
135 nsTagEntry* EntryAt(PRInt32 anIndex) const;
136 eHTMLTags operator[](PRInt32 anIndex) const;
137 eHTMLTags Last() const;
138 void Empty(void);
141 * Release all objects in the entry stack
143 void ReleaseAll(nsNodeAllocator* aNodeAllocator);
146 * Find the first instance of given tag on the stack.
147 * @update gess 12/14/99
148 * @param aTag
149 * @return index of tag, or kNotFound if not found
151 inline PRInt32 FirstOf(eHTMLTags aTag) const {
152 PRInt32 index=-1;
154 if(0<mCount) {
155 while(++index<mCount) {
156 if(aTag==mEntries[index].mTag) {
157 return index;
159 } //while
161 return kNotFound;
166 * Find the last instance of given tag on the stack.
167 * @update gess 12/14/99
168 * @param aTag
169 * @return index of tag, or kNotFound if not found
171 inline PRInt32 LastOf(eHTMLTags aTag) const {
172 PRInt32 index=mCount;
173 while(--index>=0) {
174 if(aTag==mEntries[index].mTag) {
175 return index;
178 return kNotFound;
181 nsTagEntry* mEntries;
182 PRInt32 mCount;
183 PRInt32 mCapacity;
187 /**********************************************************
188 The table state class is used to store info about each
189 table that is opened on the stack. As tables open and
190 close on the context, we update these objects to track
191 what has/hasn't been seen on a per table basis.
192 **********************************************************/
193 class CTableState {
194 public:
195 CTableState(CTableState *aPreviousState=0) {
196 mHasCaption=PR_FALSE;
197 mHasCols=PR_FALSE;
198 mHasTHead=PR_FALSE;
199 mHasTFoot=PR_FALSE;
200 mHasTBody=PR_FALSE;
201 mPrevious=aPreviousState;
204 PRBool CanOpenCaption() {
205 PRBool result=!(mHasCaption || mHasCols || mHasTHead || mHasTFoot || mHasTBody);
206 return result;
209 PRBool CanOpenCols() {
210 PRBool result=!(mHasCols || mHasTHead || mHasTFoot || mHasTBody);
211 return result;
214 PRBool CanOpenTBody() {
215 PRBool result=!(mHasTBody);
216 return result;
219 PRBool CanOpenTHead() {
220 PRBool result=!(mHasTHead || mHasTFoot || mHasTBody);
221 return result;
224 PRBool CanOpenTFoot() {
225 PRBool result=!(mHasTFoot || mHasTBody);
226 return result;
229 PRPackedBool mHasCaption;
230 PRPackedBool mHasCols;
231 PRPackedBool mHasTHead;
232 PRPackedBool mHasTFoot;
233 PRPackedBool mHasTBody;
234 CTableState *mPrevious;
237 /************************************************************************
238 nsTokenAllocator class implementation.
239 This class is used to recycle tokens.
240 By using this simple class, we cut WAY down on the number of tokens
241 that get created during the run of the system.
243 Note: The allocator is created per document. It's been shared
244 ( but not ref. counted ) by objects, tokenizer,dtd,and dtd context,
245 that cease to exist when the document is destroyed.
246 ************************************************************************/
247 class nsTokenAllocator
249 public:
251 nsTokenAllocator();
252 ~nsTokenAllocator();
253 CToken* CreateTokenOfType(eHTMLTokenTypes aType,eHTMLTags aTag, const nsAString& aString);
254 CToken* CreateTokenOfType(eHTMLTokenTypes aType,eHTMLTags aTag);
256 nsFixedSizeAllocator& GetArenaPool() { return mArenaPool; }
258 protected:
259 nsFixedSizeAllocator mArenaPool;
260 #ifdef NS_DEBUG
261 int mTotals[eToken_last-1];
262 #endif
265 /************************************************************************
266 CNodeRecycler class implementation.
267 This class is used to recycle nodes.
268 By using this simple class, we cut down on the number of nodes
269 that get created during the run of the system.
270 ************************************************************************/
272 #ifndef HEAP_ALLOCATED_NODES
273 class nsCParserNode;
274 #endif
276 class nsNodeAllocator
278 public:
280 nsNodeAllocator();
281 ~nsNodeAllocator();
282 nsCParserNode* CreateNode(CToken* aToken=nsnull, nsTokenAllocator* aTokenAllocator=0);
284 nsFixedSizeAllocator& GetArenaPool() { return mNodePool; }
286 #ifdef HEAP_ALLOCATED_NODES
287 void Recycle(nsCParserNode* aNode) { mSharedNodes.Push(static_cast<void*>(aNode)); }
288 protected:
289 nsDeque mSharedNodes;
290 #ifdef DEBUG_TRACK_NODES
291 PRInt32 mCount;
292 #endif
293 #endif
295 protected:
296 nsFixedSizeAllocator mNodePool;
299 /************************************************************************
300 The dtdcontext class defines an ordered list of tags (a context).
301 ************************************************************************/
303 class nsDTDContext
305 public:
306 nsDTDContext();
307 ~nsDTDContext();
309 nsTagEntry* PopEntry();
310 void PushEntry(nsTagEntry* aEntry, PRBool aRefCntNode = PR_TRUE);
311 void MoveEntries(nsDTDContext& aDest, PRInt32 aCount);
312 void Push(nsCParserNode* aNode,nsEntryStack* aStyleStack=0, PRBool aRefCntNode = PR_TRUE);
313 void PushTag(eHTMLTags aTag);
314 nsCParserNode* Pop(nsEntryStack*& aChildStack);
315 nsCParserNode* Pop();
316 nsCParserNode* PeekNode() { return mStack.NodeAt(mStack.mCount-1); }
317 eHTMLTags First(void) const;
318 eHTMLTags Last(void) const;
319 nsTagEntry* LastEntry(void) const;
320 eHTMLTags TagAt(PRInt32 anIndex) const;
321 eHTMLTags operator[](PRInt32 anIndex) const {return TagAt(anIndex);}
322 PRBool HasOpenContainer(eHTMLTags aTag) const;
323 PRInt32 FirstOf(eHTMLTags aTag) const {return mStack.FirstOf(aTag);}
324 PRInt32 LastOf(eHTMLTags aTag) const {return mStack.LastOf(aTag);}
326 void Empty(void);
327 PRInt32 GetCount(void) const {return mStack.mCount;}
328 PRInt32 GetResidualStyleCount(void) {return mResidualStyleCount;}
329 nsEntryStack* GetStylesAt(PRInt32 anIndex) const;
330 void PushStyle(nsCParserNode* aNode);
331 void PushStyles(nsEntryStack *aStyles);
332 nsCParserNode* PopStyle(void);
333 nsCParserNode* PopStyle(eHTMLTags aTag);
334 void RemoveStyle(eHTMLTags aTag);
336 static void ReleaseGlobalObjects(void);
338 void SetTokenAllocator(nsTokenAllocator* aTokenAllocator) { mTokenAllocator=aTokenAllocator; }
339 void SetNodeAllocator(nsNodeAllocator* aNodeAllocator) { mNodeAllocator=aNodeAllocator; }
341 nsEntryStack mStack; //this will hold a list of tagentries...
342 PRInt32 mResidualStyleCount;
343 PRInt32 mContextTopIndex;
345 nsTokenAllocator *mTokenAllocator;
346 nsNodeAllocator *mNodeAllocator;
348 #ifdef NS_DEBUG
349 enum { eMaxTags = MAX_REFLOW_DEPTH };
350 eHTMLTags mXTags[eMaxTags];
351 #endif
354 /**************************************************************
355 Now define the token deallocator class...
356 **************************************************************/
357 class CTokenDeallocator: public nsDequeFunctor{
358 protected:
359 nsFixedSizeAllocator& mArenaPool;
361 public:
362 CTokenDeallocator(nsFixedSizeAllocator& aArenaPool)
363 : mArenaPool(aArenaPool) {}
365 virtual void* operator()(void* anObject) {
366 CToken* aToken = (CToken*)anObject;
367 aToken->Release(mArenaPool);
368 return 0;
373 /************************************************************************
374 ITagHandler class offers an API for taking care of specific tokens.
375 ************************************************************************/
376 class nsITagHandler {
377 public:
379 virtual void SetString(const nsString &aTheString)=0;
380 virtual nsString* GetString()=0;
381 virtual PRBool HandleToken(CToken* aToken,nsIDTD* aDTD)=0;
382 virtual PRBool HandleCapturedTokens(CToken* aToken,nsIDTD* aDTD)=0;
385 /************************************************************************
386 Here are a few useful utility methods...
387 ************************************************************************/
390 * This method quickly scans the given set of tags,
391 * looking for the given tag.
392 * @update gess8/27/98
393 * @param aTag -- tag to be search for in set
394 * @param aTagSet -- set of tags to be searched
395 * @return
397 inline PRInt32 IndexOfTagInSet(PRInt32 aTag,const eHTMLTags* aTagSet,PRInt32 aCount) {
399 const eHTMLTags* theEnd=aTagSet+aCount;
400 const eHTMLTags* theTag=aTagSet;
402 while(theTag<theEnd) {
403 if(aTag==*theTag) {
404 return theTag-aTagSet;
406 ++theTag;
409 return kNotFound;
413 * This method quickly scans the given set of tags,
414 * looking for the given tag.
415 * @update gess8/27/98
416 * @param aTag -- tag to be search for in set
417 * @param aTagSet -- set of tags to be searched
418 * @return
420 inline PRBool FindTagInSet(PRInt32 aTag,const eHTMLTags *aTagSet,PRInt32 aCount) {
421 return PRBool(-1<IndexOfTagInSet(aTag,aTagSet,aCount));
424 /**************************************************************
425 This defines the topic object used by the observer service.
426 The observerService uses a list of these, 1 per topic when
427 registering tags.
428 **************************************************************/
430 class nsObserverEntry : public nsIObserverEntry {
431 public:
432 NS_DECL_ISUPPORTS
433 nsObserverEntry(const nsAString& aString);
434 virtual ~nsObserverEntry();
436 NS_IMETHOD Notify(nsIParserNode* aNode,
437 nsIParser* aParser,
438 nsISupports* aWebShell,
439 const PRUint32 aFlags);
441 nsresult AddObserver(nsIElementObserver* aObserver,eHTMLTags aTag);
442 void RemoveObserver(nsIElementObserver* aObserver);
443 PRBool Matches(const nsAString& aTopic);
445 protected:
446 nsString mTopic;
447 nsVoidArray* mObservers[NS_HTML_TAG_MAX + 1];
448 friend class nsMatchesTopic;
451 /*********************************************************************************************/
454 struct TagList {
455 PRUint32 mCount;
456 const eHTMLTags *mTags;
460 * Find the last member of given taglist on the given context
461 * @update gess 12/14/99
462 * @param aContext
463 * @param aTagList
464 * @return index of tag, or kNotFound if not found
466 inline PRInt32 LastOf(nsDTDContext& aContext, const TagList& aTagList){
467 int max = aContext.GetCount();
468 int index;
469 for(index=max-1;index>=0;index--){
470 PRBool result=FindTagInSet(aContext[index],aTagList.mTags,aTagList.mCount);
471 if(result) {
472 return index;
475 return kNotFound;
479 * Find the first member of given taglist on the given context
480 * @update gess 12/14/99
481 * @param aContext
482 * @param aStartOffset
483 * @param aTagList
484 * @return index of tag, or kNotFound if not found
486 inline PRInt32 FirstOf(nsDTDContext& aContext,PRInt32 aStartOffset,TagList& aTagList){
487 int max = aContext.GetCount();
488 int index;
489 for(index=aStartOffset;index<max;++index){
490 PRBool result=FindTagInSet(aContext[index],aTagList.mTags,aTagList.mCount);
491 if(result) {
492 return index;
495 return kNotFound;
500 * Call this to find out whether the DTD thinks the tag requires an END tag </xxx>
501 * @update gess 01/04/99
502 * @param id of tag
503 * @return TRUE of the element's end tag is optional
505 inline PRBool HasOptionalEndTag(eHTMLTags aTag) {
506 static eHTMLTags gHasOptionalEndTags[]={eHTMLTag_body,eHTMLTag_colgroup,eHTMLTag_dd,eHTMLTag_dt,
507 eHTMLTag_head,eHTMLTag_li,eHTMLTag_option,
508 eHTMLTag_p,eHTMLTag_tbody,eHTMLTag_td,eHTMLTag_tfoot,
509 eHTMLTag_th,eHTMLTag_thead,eHTMLTag_tr,
510 eHTMLTag_userdefined,eHTMLTag_unknown};
511 return FindTagInSet(aTag,gHasOptionalEndTags,sizeof(gHasOptionalEndTags)/sizeof(eHTMLTag_body));
513 #endif