Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / parser / htmlparser / src / nsDTDUtils.cpp
blob2f4ce50dc1e5139e6392481d48a5d07edbfcfb97
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):
24 * Pierre Phaneuf <pp@ludusdesign.com>
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or 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 MPL, 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 MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
41 #include "nsIAtom.h"
42 #include "nsDTDUtils.h"
43 #include "CNavDTD.h"
44 #include "nsIParserNode.h"
45 #include "nsParserNode.h"
46 #include "nsIChannel.h"
47 #include "nsIServiceManager.h"
48 #include "nsUnicharUtils.h"
50 /**************************************************************************************
51 A few notes about how residual style handling is performed:
53 1. The style stack contains nsTagEntry elements.
54 2. Every tag on the containment stack can have it's own residual style stack.
55 3. When a style leaks, it's mParent member is set to the level on the stack where
56 it originated. A node with an mParent of 0 is not opened on tag stack,
57 but is open on stylestack.
58 4. An easy way to tell that a container on the element stack is a residual style tag
59 is that it's use count is >1.
61 **************************************************************************************/
64 /**
65 * Default constructor
66 * @update harishd 04/04/99
67 * @update gess 04/22/99
69 nsEntryStack::nsEntryStack() {
71 MOZ_COUNT_CTOR(nsEntryStack);
73 mCapacity=0;
74 mCount=0;
75 mEntries=0;
78 /**
79 * Default destructor
80 * @update harishd 04/04/99
81 * @update gess 04/22/99
83 nsEntryStack::~nsEntryStack() {
85 MOZ_COUNT_DTOR(nsEntryStack);
87 if(mEntries) {
88 //add code here to recycle the node if you have one...
89 delete [] mEntries;
90 mEntries=0;
93 mCount=mCapacity=0;
96 /**
97 * Release all objects in the entry stack
99 void
100 nsEntryStack::ReleaseAll(nsNodeAllocator* aNodeAllocator)
102 NS_ASSERTION(aNodeAllocator,"no allocator? - potential leak!");
104 if(aNodeAllocator) {
105 NS_ASSERTION(mCount >= 0,"count should not be negative");
106 while(mCount > 0) {
107 nsCParserNode* node=this->Pop();
108 IF_FREE(node,aNodeAllocator);
114 * Resets state of stack to be empty.
115 * @update harishd 04/04/99
117 void nsEntryStack::Empty(void) {
118 mCount=0;
124 * @update gess 04/22/99
126 void nsEntryStack::EnsureCapacityFor(PRInt32 aNewMax,PRInt32 aShiftOffset) {
127 if(mCapacity<aNewMax){
129 const int kDelta=16;
131 PRInt32 theSize = kDelta * ((aNewMax / kDelta) + 1);
132 nsTagEntry* temp=new nsTagEntry[theSize];
133 mCapacity=theSize;
135 if(temp){
136 PRInt32 index=0;
137 for(index=0;index<mCount;++index) {
138 temp[aShiftOffset+index]=mEntries[index];
140 if(mEntries) delete [] mEntries;
141 mEntries=temp;
143 else{
144 //XXX HACK! This is very bad! We failed to get memory.
146 } //if
151 * @update gess 04/22/99
153 void nsEntryStack::Push(nsCParserNode* aNode,
154 nsEntryStack* aStyleStack,
155 PRBool aRefCntNode)
157 if(aNode) {
158 EnsureCapacityFor(mCount+1);
159 mEntries[mCount].mTag = (eHTMLTags)aNode->GetNodeType();
160 if (aRefCntNode) {
161 aNode->mUseCount++;
162 mEntries[mCount].mNode = const_cast<nsCParserNode*>(aNode);
163 IF_HOLD(mEntries[mCount].mNode);
165 mEntries[mCount].mParent=aStyleStack;
166 mEntries[mCount++].mStyles=0;
170 void nsEntryStack::PushTag(eHTMLTags aTag)
172 EnsureCapacityFor(mCount + 1);
173 mEntries[mCount].mTag = aTag;
174 mEntries[mCount].mParent = nsnull;
175 mEntries[mCount].mStyles = nsnull;
176 ++mCount;
181 * This method inserts the given node onto the front of this stack
183 * @update gess 11/10/99
185 void nsEntryStack::PushFront(nsCParserNode* aNode,
186 nsEntryStack* aStyleStack,
187 PRBool aRefCntNode)
189 if(aNode) {
190 if(mCount<mCapacity) {
191 PRInt32 index=0;
192 for(index=mCount;index>0;index--) {
193 mEntries[index]=mEntries[index-1];
196 else {
197 EnsureCapacityFor(mCount+1,1);
199 mEntries[0].mTag = (eHTMLTags)aNode->GetNodeType();
200 if (aRefCntNode) {
201 aNode->mUseCount++;
202 mEntries[0].mNode = const_cast<nsCParserNode*>(aNode);
203 IF_HOLD(mEntries[0].mNode);
205 mEntries[0].mParent=aStyleStack;
206 mEntries[0].mStyles=0;
207 ++mCount;
213 * @update gess 11/10/99
215 void nsEntryStack::Append(nsEntryStack *aStack) {
216 if(aStack) {
218 PRInt32 theCount=aStack->mCount;
220 EnsureCapacityFor(mCount+aStack->mCount,0);
222 PRInt32 theIndex=0;
223 for(theIndex=0;theIndex<theCount;++theIndex){
224 mEntries[mCount]=aStack->mEntries[theIndex];
225 mEntries[mCount++].mParent=0;
231 * This method removes the node for the given tag
232 * from anywhere within this entry stack, and shifts
233 * other entries down.
235 * NOTE: It's odd to be removing an element from the middle
236 * of a stack, but it's necessary because of how MALFORMED
237 * html can be.
239 * anIndex: the index within the stack of the tag to be removed
240 * aTag: the id of the tag to be removed
241 * @update gess 02/25/00
243 nsCParserNode* nsEntryStack::Remove(PRInt32 anIndex,
244 eHTMLTags aTag)
246 nsCParserNode* result = 0;
247 if (0 < mCount && anIndex < mCount){
248 result = mEntries[anIndex].mNode;
249 if (result)
250 result->mUseCount--;
251 PRInt32 theIndex = 0;
252 mCount -= 1;
253 for( theIndex = anIndex; theIndex < mCount; ++theIndex){
254 mEntries[theIndex] = mEntries[theIndex+1];
256 mEntries[mCount].mNode = 0;
257 mEntries[mCount].mStyles = 0;
258 nsEntryStack* theStyleStack = mEntries[anIndex].mParent;
259 if (theStyleStack) {
260 //now we have to tell the residual style stack where this tag
261 //originated that it's no longer in use.
262 PRUint32 scount = theStyleStack->mCount;
263 #ifdef DEBUG_mrbkap
264 NS_ASSERTION(scount != 0, "RemoveStyles has a bad style stack");
265 #endif
266 nsTagEntry *theStyleEntry = theStyleStack->mEntries;
267 for (PRUint32 sindex = scount-1;; --sindex) {
268 if (theStyleEntry->mTag == aTag) {
269 // This tells us that the style is not open at any level.
270 theStyleEntry->mParent = nsnull;
271 break;
273 if (sindex == 0) {
274 #ifdef DEBUG_mrbkap
275 NS_ERROR("Couldn't find the removed style on its parent stack");
276 #endif
277 break;
279 ++theStyleEntry;
283 return result;
287 * Pops an entry from this style stack. If the entry has a parent stack, it
288 * updates the entry so that we know not to try to remove it from the parent
289 * stack since it's no longer open.
291 nsCParserNode* nsEntryStack::Pop(void)
293 nsCParserNode* result = 0;
294 if (0 < mCount) {
295 result = mEntries[--mCount].mNode;
296 if (result)
297 result->mUseCount--;
298 mEntries[mCount].mNode = 0;
299 mEntries[mCount].mStyles = 0;
300 nsEntryStack* theStyleStack = mEntries[mCount].mParent;
301 if (theStyleStack) {
302 //now we have to tell the residual style stack where this tag
303 //originated that it's no longer in use.
304 PRUint32 scount = theStyleStack->mCount;
306 // XXX If this NS_ENSURE_TRUE fails, it means that the style stack was
307 // empty before we were removed.
308 #ifdef DEBUG_mrbkap
309 NS_ASSERTION(scount != 0, "preventing a potential crash.");
310 #endif
311 NS_ENSURE_TRUE(scount != 0, result);
313 nsTagEntry *theStyleEntry = theStyleStack->mEntries;
314 for (PRUint32 sindex = scount - 1;; --sindex) {
315 if (theStyleEntry->mTag == mEntries[mCount].mTag) {
316 // This tells us that the style is not open at any level
317 theStyleEntry->mParent = nsnull;
318 break;
320 if (sindex == 0) {
321 #ifdef DEBUG_mrbkap
322 NS_ERROR("Couldn't find the removed style on its parent stack");
323 #endif
324 break;
326 ++theStyleEntry;
330 return result;
335 * @update harishd 04/04/99
336 * @update gess 04/21/99
338 eHTMLTags nsEntryStack::First() const
340 eHTMLTags result=eHTMLTag_unknown;
341 if(0<mCount){
342 result=mEntries[0].mTag;
344 return result;
349 * @update harishd 04/04/99
350 * @update gess 04/21/99
352 nsCParserNode* nsEntryStack::NodeAt(PRInt32 anIndex) const
354 nsCParserNode* result=0;
355 if((0<mCount) && (anIndex<mCount)) {
356 result=mEntries[anIndex].mNode;
358 return result;
363 * @update harishd 04/04/99
364 * @update gess 04/21/99
366 eHTMLTags nsEntryStack::TagAt(PRInt32 anIndex) const
368 eHTMLTags result=eHTMLTag_unknown;
369 if((0<mCount) && (anIndex<mCount)) {
370 result=mEntries[anIndex].mTag;
372 return result;
377 * @update gess 04/21/99
379 nsTagEntry* nsEntryStack::EntryAt(PRInt32 anIndex) const
381 nsTagEntry *result=0;
382 if((0<mCount) && (anIndex<mCount)) {
383 result=&mEntries[anIndex];
385 return result;
391 * @update harishd 04/04/99
392 * @update gess 04/21/99
394 eHTMLTags nsEntryStack::operator[](PRInt32 anIndex) const
396 eHTMLTags result=eHTMLTag_unknown;
397 if((0<mCount) && (anIndex<mCount)) {
398 result=mEntries[anIndex].mTag;
400 return result;
406 * @update harishd 04/04/99
407 * @update gess 04/21/99
409 eHTMLTags nsEntryStack::Last(void) const
411 eHTMLTags result=eHTMLTag_unknown;
412 if(0<mCount) {
413 result=mEntries[mCount-1].mTag;
415 return result;
418 nsTagEntry*
419 nsEntryStack::PopEntry()
421 nsTagEntry* entry = EntryAt(mCount-1);
422 this->Pop();
423 return entry;
426 void nsEntryStack::PushEntry(nsTagEntry* aEntry,
427 PRBool aRefCntNode)
429 if (aEntry) {
430 EnsureCapacityFor(mCount+1);
431 mEntries[mCount].mNode = aEntry->mNode;
432 mEntries[mCount].mTag = aEntry->mTag;
433 mEntries[mCount].mParent = aEntry->mParent;
434 mEntries[mCount].mStyles = aEntry->mStyles;
435 if (aRefCntNode && mEntries[mCount].mNode) {
436 mEntries[mCount].mNode->mUseCount++;
437 IF_HOLD(mEntries[mCount].mNode);
439 mCount++;
443 /***************************************************************
444 Now define the dtdcontext class
445 ***************************************************************/
450 * @update gess 04.21.2000
452 nsDTDContext::nsDTDContext() : mStack()
454 MOZ_COUNT_CTOR(nsDTDContext);
455 mResidualStyleCount=0;
456 mContextTopIndex=-1;
457 mTokenAllocator=0;
458 mNodeAllocator=0;
460 #ifdef DEBUG
461 memset(mXTags,0,sizeof(mXTags));
462 #endif
467 * @update gess9/10/98
469 nsDTDContext::~nsDTDContext()
471 MOZ_COUNT_DTOR(nsDTDContext);
477 * @update gess7/9/98
479 PRBool nsDTDContext::HasOpenContainer(eHTMLTags aTag) const {
480 PRInt32 theIndex=mStack.LastOf(aTag);
481 return PRBool(-1<theIndex);
486 * @update gess7/9/98
488 void nsDTDContext::Push(nsCParserNode* aNode,
489 nsEntryStack* aStyleStack,
490 PRBool aRefCntNode) {
491 if(aNode) {
492 #ifdef NS_DEBUG
493 eHTMLTags theTag = (eHTMLTags)aNode->GetNodeType();
494 int size = mStack.mCount;
495 if (size < eMaxTags)
496 mXTags[size] = theTag;
497 #endif
498 mStack.Push(aNode, aStyleStack, aRefCntNode);
502 void nsDTDContext::PushTag(eHTMLTags aTag)
504 #ifdef NS_DEBUG
505 if (mStack.mCount < eMaxTags) {
506 mXTags[mStack.mCount] = aTag;
508 #endif
510 mStack.PushTag(aTag);
513 nsTagEntry*
514 nsDTDContext::PopEntry()
516 PRInt32 theSize = mStack.mCount;
517 if(0<theSize) {
518 #ifdef NS_DEBUG
519 if (theSize <= eMaxTags)
520 mXTags[theSize-1]=eHTMLTag_unknown;
521 #endif
522 return mStack.PopEntry();
524 return 0;
527 void nsDTDContext::PushEntry(nsTagEntry* aEntry,
528 PRBool aRefCntNode)
530 #ifdef NS_DEBUG
531 int size=mStack.mCount;
532 if(size< eMaxTags && aEntry)
533 mXTags[size]=aEntry->mTag;
534 #endif
535 mStack.PushEntry(aEntry, aRefCntNode);
538 /* This method will move the top entires, in the entry-stack, into dest context.
539 * @param aDest - Destination context for the entries.
540 * @param aCount - Number of entries, on top of the entry-stack, to be moved.
542 void
543 nsDTDContext::MoveEntries(nsDTDContext& aDest,
544 PRInt32 aCount)
546 NS_ASSERTION(aCount > 0 && mStack.mCount >= aCount, "cannot move entries");
547 if (aCount > 0 && mStack.mCount >= aCount) {
548 while (aCount) {
549 aDest.PushEntry(&mStack.mEntries[--mStack.mCount], PR_FALSE);
550 #ifdef NS_DEBUG
551 if (mStack.mCount < eMaxTags) {
552 mXTags[mStack.mCount] = eHTMLTag_unknown;
554 #endif
555 --aCount;
560 /**
561 * @update gess 11/11/99,
562 * harishd 04/04/99
564 nsCParserNode* nsDTDContext::Pop(nsEntryStack *&aChildStyleStack) {
566 PRInt32 theSize=mStack.mCount;
567 nsCParserNode* result=0;
569 if(0<theSize) {
571 #ifdef NS_DEBUG
572 if ((theSize>0) && (theSize <= eMaxTags))
573 mXTags[theSize-1]=eHTMLTag_unknown;
574 #endif
577 nsTagEntry* theEntry=mStack.EntryAt(mStack.mCount-1);
578 aChildStyleStack=theEntry->mStyles;
580 result=mStack.Pop();
581 theEntry->mParent=0;
584 return result;
589 * @update harishd 04/07/00
592 nsCParserNode* nsDTDContext::Pop() {
593 nsEntryStack *theTempStyleStack=0; // This has no use here...
594 return Pop(theTempStyleStack);
599 * @update gess7/9/98
601 eHTMLTags nsDTDContext::First(void) const {
602 return mStack.First();
607 * @update gess7/9/98
609 eHTMLTags nsDTDContext::TagAt(PRInt32 anIndex) const {
610 return mStack.TagAt(anIndex);
615 * @update gess7/9/98
617 nsTagEntry* nsDTDContext::LastEntry(void) const {
618 return mStack.EntryAt(mStack.mCount-1);
623 * @update gess7/9/98
625 eHTMLTags nsDTDContext::Last() const {
626 return mStack.Last();
632 * @update gess7/9/98
634 nsEntryStack* nsDTDContext::GetStylesAt(PRInt32 anIndex) const {
635 nsEntryStack* result=0;
637 if(anIndex<mStack.mCount){
638 nsTagEntry* theEntry=mStack.EntryAt(anIndex);
639 if(theEntry) {
640 result=theEntry->mStyles;
643 return result;
649 * @update gess 04/28/99
651 void nsDTDContext::PushStyle(nsCParserNode* aNode){
653 nsTagEntry* theEntry=mStack.EntryAt(mStack.mCount-1);
654 if(theEntry ) {
655 nsEntryStack* theStack=theEntry->mStyles;
656 if(!theStack) {
657 theStack=theEntry->mStyles=new nsEntryStack();
659 if(theStack) {
660 theStack->Push(aNode);
661 ++mResidualStyleCount;
663 } //if
668 * Call this when you have an EntryStack full of styles
669 * that you want to push at this level.
671 * @update gess 04/28/99
673 void nsDTDContext::PushStyles(nsEntryStack *aStyles){
675 if(aStyles) {
676 nsTagEntry* theEntry=mStack.EntryAt(mStack.mCount-1);
677 if(theEntry ) {
678 nsEntryStack* theStyles=theEntry->mStyles;
679 if(!theStyles) {
680 theEntry->mStyles=aStyles;
682 PRUint32 scount=aStyles->mCount;
683 PRUint32 sindex=0;
685 theEntry=aStyles->mEntries;
686 for(sindex=0;sindex<scount;++sindex){
687 theEntry->mParent=0; //this tells us that the style is not open at any level
688 ++theEntry;
689 ++mResidualStyleCount;
690 } //for
693 else {
694 theStyles->Append(aStyles);
695 // Delete aStyles since it has been copied to theStyles...
696 delete aStyles;
697 aStyles=0;
699 } //if(theEntry )
700 else if(mStack.mCount==0) {
701 // If you're here it means that we have hit the rock bottom
702 // ,of the stack, and there's no need to handle anymore styles.
703 // Fix for bug 29048
704 IF_DELETE(aStyles,mNodeAllocator);
706 }//if(aStyles)
710 /**
712 * @update gess 04/28/99
714 nsCParserNode* nsDTDContext::PopStyle(void){
715 nsCParserNode *result=0;
717 nsTagEntry *theEntry=mStack.EntryAt(mStack.mCount-1);
718 if(theEntry && (theEntry->mNode)) {
719 nsEntryStack* theStyleStack=theEntry->mParent;
720 if(theStyleStack){
721 result=theStyleStack->Pop();
722 mResidualStyleCount--;
724 } //if
725 return result;
728 /**
730 * @update gess 04/28/99
732 nsCParserNode* nsDTDContext::PopStyle(eHTMLTags aTag){
734 PRInt32 theLevel=0;
735 nsCParserNode* result=0;
737 for(theLevel=mStack.mCount-1;theLevel>0;theLevel--) {
738 nsEntryStack *theStack=mStack.mEntries[theLevel].mStyles;
739 if(theStack) {
740 if(aTag==theStack->Last()) {
741 result=theStack->Pop();
742 mResidualStyleCount--;
743 break; // Fix bug 50710 - Stop after finding a style.
744 } else {
745 // NS_ERROR("bad residual style entry");
750 return result;
753 /**
755 * This is similar to popstyle, except that it removes the
756 * style tag given from anywhere in the style stack, and
757 * not just at the top.
759 * @update gess 01/26/00
761 void nsDTDContext::RemoveStyle(eHTMLTags aTag){
763 PRInt32 theLevel=mStack.mCount;
765 while (theLevel) {
766 nsEntryStack *theStack=GetStylesAt(--theLevel);
767 if (theStack) {
768 PRInt32 index=theStack->mCount;
769 while (index){
770 nsTagEntry *theEntry=theStack->EntryAt(--index);
771 if (aTag==(eHTMLTags)theEntry->mNode->GetNodeType()) {
772 mResidualStyleCount--;
773 nsCParserNode* result=theStack->Remove(index,aTag);
774 IF_FREE(result, mNodeAllocator);
775 return;
783 * This gets called when the parser module is getting unloaded
785 * @return nada
787 void nsDTDContext::ReleaseGlobalObjects(void){
791 /**************************************************************
792 Now define the nsTokenAllocator class...
793 **************************************************************/
795 static const size_t kTokenBuckets[] ={sizeof(CStartToken),sizeof(CAttributeToken),sizeof(CCommentToken),sizeof(CEndToken)};
796 static const PRInt32 kNumTokenBuckets = sizeof(kTokenBuckets) / sizeof(size_t);
797 static const PRInt32 kInitialTokenPoolSize = NS_SIZE_IN_HEAP(sizeof(CToken)) * 200;
801 * @update gess7/25/98
802 * @param
804 nsTokenAllocator::nsTokenAllocator() {
806 MOZ_COUNT_CTOR(nsTokenAllocator);
808 mArenaPool.Init("TokenPool", kTokenBuckets, kNumTokenBuckets, kInitialTokenPoolSize);
810 #ifdef NS_DEBUG
811 int i=0;
812 for(i=0;i<eToken_last-1;++i) {
813 mTotals[i]=0;
815 #endif
820 * Destructor for the token factory
821 * @update gess7/25/98
823 nsTokenAllocator::~nsTokenAllocator() {
825 MOZ_COUNT_DTOR(nsTokenAllocator);
829 class CTokenFinder: public nsDequeFunctor{
830 public:
831 CTokenFinder(CToken* aToken) {mToken=aToken;}
832 virtual void* operator()(void* anObject) {
833 if(anObject==mToken) {
834 return anObject;
836 return 0;
838 CToken* mToken;
842 * Let's get this code ready to be reused by all the contexts.
844 * @update rickg 12June2000
845 * @param aType -- tells you the type of token to create
846 * @param aTag -- tells you the type of tag to init with this token
847 * @param aString -- gives a default string value for the token
849 * @return ptr to new token (or 0).
851 CToken* nsTokenAllocator::CreateTokenOfType(eHTMLTokenTypes aType,eHTMLTags aTag, const nsAString& aString) {
853 CToken* result=0;
855 #ifdef NS_DEBUG
856 mTotals[aType-1]++;
857 #endif
858 switch(aType){
859 case eToken_start: result=new(mArenaPool) CStartToken(aString, aTag); break;
860 case eToken_end: result=new(mArenaPool) CEndToken(aString, aTag); break;
861 case eToken_comment: result=new(mArenaPool) CCommentToken(aString); break;
862 case eToken_entity: result=new(mArenaPool) CEntityToken(aString); break;
863 case eToken_whitespace: result=new(mArenaPool) CWhitespaceToken(aString); break;
864 case eToken_newline: result=new(mArenaPool) CNewlineToken(); break;
865 case eToken_text: result=new(mArenaPool) CTextToken(aString); break;
866 case eToken_attribute: result=new(mArenaPool) CAttributeToken(aString); break;
867 case eToken_instruction: result=new(mArenaPool) CInstructionToken(aString); break;
868 case eToken_cdatasection: result=new(mArenaPool) CCDATASectionToken(aString); break;
869 case eToken_doctypeDecl: result=new(mArenaPool) CDoctypeDeclToken(aString); break;
870 case eToken_markupDecl: result=new(mArenaPool) CMarkupDeclToken(aString); break;
871 default:
872 NS_ASSERTION(PR_FALSE, "nsDTDUtils::CreateTokenOfType: illegal token type");
873 break;
876 return result;
880 * Let's get this code ready to be reused by all the contexts.
882 * @update rickg 12June2000
883 * @param aType -- tells you the type of token to create
884 * @param aTag -- tells you the type of tag to init with this token
886 * @return ptr to new token (or 0).
888 CToken* nsTokenAllocator::CreateTokenOfType(eHTMLTokenTypes aType,eHTMLTags aTag) {
890 CToken* result=0;
892 #ifdef NS_DEBUG
893 mTotals[aType-1]++;
894 #endif
895 switch(aType){
896 case eToken_start: result=new(mArenaPool) CStartToken(aTag); break;
897 case eToken_end: result=new(mArenaPool) CEndToken(aTag); break;
898 case eToken_comment: result=new(mArenaPool) CCommentToken(); break;
899 case eToken_attribute: result=new(mArenaPool) CAttributeToken(); break;
900 case eToken_entity: result=new(mArenaPool) CEntityToken(); break;
901 case eToken_whitespace: result=new(mArenaPool) CWhitespaceToken(); break;
902 case eToken_newline: result=new(mArenaPool) CNewlineToken(); break;
903 case eToken_text: result=new(mArenaPool) CTextToken(); break;
904 case eToken_instruction: result=new(mArenaPool) CInstructionToken(); break;
905 case eToken_cdatasection: result=new(mArenaPool) CCDATASectionToken(aTag); break;
906 case eToken_doctypeDecl: result=new(mArenaPool) CDoctypeDeclToken(aTag); break;
907 case eToken_markupDecl: result=new(mArenaPool) CMarkupDeclToken(); break;
908 default:
909 NS_ASSERTION(PR_FALSE, "nsDTDUtils::CreateTokenOfType: illegal token type");
910 break;
913 return result;
916 #ifdef DEBUG_TRACK_NODES
918 static nsCParserNode* gAllNodes[100];
919 static int gAllNodeCount=0;
921 int FindNode(nsCParserNode *aNode) {
922 int theIndex=0;
923 for(theIndex=0;theIndex<gAllNodeCount;++theIndex) {
924 if(gAllNodes[theIndex]==aNode) {
925 return theIndex;
928 return -1;
931 void AddNode(nsCParserNode *aNode) {
932 if(-1==FindNode(aNode)) {
933 gAllNodes[gAllNodeCount++]=aNode;
935 else {
936 //you tried to recycle a node twice!
940 void RemoveNode(nsCParserNode *aNode) {
941 int theIndex=FindNode(aNode);
942 if(-1<theIndex) {
943 gAllNodes[theIndex]=gAllNodes[--gAllNodeCount];
947 #endif
950 #ifdef HEAP_ALLOCATED_NODES
951 nsNodeAllocator::nsNodeAllocator():mSharedNodes(0){
952 #ifdef DEBUG_TRACK_NODES
953 mCount=0;
954 #endif
955 #else
956 static const size_t kNodeBuckets[] = { sizeof(nsCParserNode), sizeof(nsCParserStartNode) };
957 static const PRInt32 kNumNodeBuckets = sizeof(kNodeBuckets) / sizeof(size_t);
958 static const PRInt32 kInitialNodePoolSize = NS_SIZE_IN_HEAP(sizeof(nsCParserNode)) * 35; // optimal size based on space-trace data
959 nsNodeAllocator::nsNodeAllocator() {
960 mNodePool.Init("NodePool", kNodeBuckets, kNumNodeBuckets, kInitialNodePoolSize);
961 #endif
962 MOZ_COUNT_CTOR(nsNodeAllocator);
965 nsNodeAllocator::~nsNodeAllocator() {
966 MOZ_COUNT_DTOR(nsNodeAllocator);
968 #ifdef HEAP_ALLOCATED_NODES
969 nsCParserNode* theNode = 0;
971 while((theNode=(nsCParserNode*)mSharedNodes.Pop())){
972 #ifdef DEBUG_TRACK_NODES
973 RemoveNode(theNode);
974 #endif
975 ::operator delete(theNode);
976 theNode=nsnull;
978 #ifdef DEBUG_TRACK_NODES
979 if(mCount) {
980 printf("**************************\n");
981 printf("%i out of %i nodes leaked!\n",gAllNodeCount,mCount);
982 printf("**************************\n");
984 #endif
985 #endif
988 nsCParserNode* nsNodeAllocator::CreateNode(CToken* aToken,
989 nsTokenAllocator* aTokenAllocator)
991 nsCParserNode* result = 0;
992 #ifdef HEAP_ALLOCATED_NODES
993 #if 0
994 if(gAllNodeCount!=mSharedNodes.GetSize()) {
995 int x=10; //this is very BAD!
997 #endif
998 result = static_cast<nsCParserNode*>(mSharedNodes.Pop());
999 if (result) {
1000 result->Init(aToken, aTokenAllocator,this);
1002 else{
1003 result = nsCParserNode::Create(aToken, aTokenAllocator,this);
1004 #ifdef DEBUG_TRACK_NODES
1005 ++mCount;
1006 AddNode(static_cast<nsCParserNode*>(result));
1007 #endif
1008 IF_HOLD(result);
1010 #else
1011 eHTMLTokenTypes type = aToken ? eHTMLTokenTypes(aToken->GetTokenType()) : eToken_unknown;
1012 switch (type) {
1013 case eToken_start:
1014 result = nsCParserStartNode::Create(aToken, aTokenAllocator,this);
1015 break;
1016 default :
1017 result = nsCParserNode::Create(aToken, aTokenAllocator,this);
1018 break;
1020 IF_HOLD(result);
1021 #endif
1022 return result;
1025 #ifdef DEBUG
1026 void DebugDumpContainmentRules(nsIDTD& theDTD,const char* aFilename,const char* aTitle) {
1028 #endif
1030 /**************************************************************
1031 This defines the topic object used by the observer service.
1032 The observerService uses a list of these, 1 per topic when
1033 registering tags.
1034 **************************************************************/
1035 NS_IMPL_ISUPPORTS1(nsObserverEntry, nsIObserverEntry)
1037 nsObserverEntry::nsObserverEntry(const nsAString& aTopic) : mTopic(aTopic)
1039 memset(mObservers, 0, sizeof(mObservers));
1042 nsObserverEntry::~nsObserverEntry() {
1043 for (PRInt32 i = 0; i <= NS_HTML_TAG_MAX; ++i){
1044 if (mObservers[i]) {
1045 PRInt32 count = mObservers[i]->Count();
1046 for (PRInt32 j = 0; j < count; ++j) {
1047 nsISupports* obs = (nsISupports*)mObservers[i]->ElementAt(j);
1048 NS_IF_RELEASE(obs);
1050 delete mObservers[i];
1055 NS_IMETHODIMP
1056 nsObserverEntry::Notify(nsIParserNode* aNode,
1057 nsIParser* aParser,
1058 nsISupports* aWebShell,
1059 const PRUint32 aFlags)
1061 NS_ENSURE_ARG_POINTER(aNode);
1062 NS_ENSURE_ARG_POINTER(aParser);
1064 nsresult result = NS_OK;
1065 eHTMLTags theTag = (eHTMLTags)aNode->GetNodeType();
1067 if (theTag <= NS_HTML_TAG_MAX) {
1068 nsVoidArray* theObservers = mObservers[theTag];
1069 if (theObservers) {
1070 PRInt32 theCharsetSource;
1071 nsCAutoString charset;
1072 aParser->GetDocumentCharset(charset,theCharsetSource);
1073 NS_ConvertASCIItoUTF16 theCharsetValue(charset);
1075 PRInt32 theAttrCount = aNode->GetAttributeCount();
1076 PRInt32 theObserversCount = theObservers->Count();
1077 if (0 < theObserversCount){
1078 nsStringArray keys(theAttrCount+4), values(theAttrCount+4);
1080 // XXX this and the following code may be a performance issue.
1081 // Every key and value is copied and added to an voidarray (causing at
1082 // least 2 allocations for mImpl, usually more, plus at least 1 per
1083 // string (total = 2*(keys+3) + 2(or more) array allocations )).
1084 PRInt32 index;
1085 for (index = 0; index < theAttrCount; ++index) {
1086 keys.AppendString(aNode->GetKeyAt(index));
1087 values.AppendString(aNode->GetValueAt(index));
1090 nsAutoString intValue;
1092 keys.AppendString(NS_LITERAL_STRING("charset"));
1093 values.AppendString(theCharsetValue);
1095 keys.AppendString(NS_LITERAL_STRING("charsetSource"));
1096 intValue.AppendInt(PRInt32(theCharsetSource),10);
1097 values.AppendString(intValue);
1099 keys.AppendString(NS_LITERAL_STRING("X_COMMAND"));
1100 values.AppendString(NS_LITERAL_STRING("text/html"));
1102 nsCOMPtr<nsIChannel> channel;
1103 aParser->GetChannel(getter_AddRefs(channel));
1105 for (index=0;index<theObserversCount;++index) {
1106 nsIElementObserver* observer = static_cast<nsIElementObserver*>(theObservers->ElementAt(index));
1107 if (observer) {
1108 result = observer->Notify(aWebShell, channel,
1109 nsHTMLTags::GetStringValue(theTag),
1110 &keys, &values, aFlags);
1111 if (NS_FAILED(result)) {
1112 break;
1115 if (result == NS_HTMLPARSER_VALID_META_CHARSET) {
1116 // Inform the parser that this meta tag contained a valid
1117 // charset. See bug 272815
1118 aParser->SetDocumentCharset(charset, kCharsetFromMetaTag);
1119 result = NS_OK;
1126 return result;
1129 PRBool
1130 nsObserverEntry::Matches(const nsAString& aString) {
1131 PRBool result = aString.Equals(mTopic);
1132 return result;
1135 nsresult
1136 nsObserverEntry::AddObserver(nsIElementObserver *aObserver,
1137 eHTMLTags aTag)
1139 if (aObserver) {
1140 if (!mObservers[aTag]) {
1141 mObservers[aTag] = new nsAutoVoidArray();
1142 if (!mObservers[aTag]) {
1143 return NS_ERROR_OUT_OF_MEMORY;
1146 NS_ADDREF(aObserver);
1147 mObservers[aTag]->AppendElement(aObserver);
1149 return NS_OK;
1152 void
1153 nsObserverEntry::RemoveObserver(nsIElementObserver *aObserver)
1155 for (PRInt32 i=0; i <= NS_HTML_TAG_MAX; ++i){
1156 if (mObservers[i]) {
1157 nsISupports* obs = aObserver;
1158 PRBool removed = mObservers[i]->RemoveElement(obs);
1159 if (removed) {
1160 NS_RELEASE(obs);