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
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.
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 ***** */
51 #include "nsHTMLTags.h"
52 #include "nsHTMLTokens.h"
53 #include "nsIParser.h"
57 #include "nsITokenizer.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) \
74 #define IF_FREE(_ptr, _allocator) \
76 if(_ptr && _allocator) { \
77 _ptr->Release((_allocator)->GetArenaPool()); \
82 // release objects and destroy _ptr
83 #define IF_DELETE(_ptr, _allocator) \
86 _ptr->ReleaseAll(_allocator); \
94 class nsNodeAllocator
;
98 void DebugDumpContainmentRules(nsIDTD
& theDTD
,const char* aFilename
,const char* aTitle
);
99 void DebugDumpContainmentRules2(nsIDTD
& theDTD
,const char* aFilename
,const char* aTitle
);
102 /***************************************************************
103 First, define the tagstack class
104 ***************************************************************/
106 class nsEntryStack
; //forware declare to make compilers happy.
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
;
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;
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
149 * @return index of tag, or kNotFound if not found
151 inline PRInt32
FirstOf(eHTMLTags aTag
) const {
155 while(++index
<mCount
) {
156 if(aTag
==mEntries
[index
].mTag
) {
166 * Find the last instance of given tag on the stack.
167 * @update gess 12/14/99
169 * @return index of tag, or kNotFound if not found
171 inline PRInt32
LastOf(eHTMLTags aTag
) const {
172 PRInt32 index
=mCount
;
174 if(aTag
==mEntries
[index
].mTag
) {
181 nsTagEntry
* mEntries
;
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 **********************************************************/
195 CTableState(CTableState
*aPreviousState
=0) {
196 mHasCaption
=PR_FALSE
;
201 mPrevious
=aPreviousState
;
204 PRBool
CanOpenCaption() {
205 PRBool result
=!(mHasCaption
|| mHasCols
|| mHasTHead
|| mHasTFoot
|| mHasTBody
);
209 PRBool
CanOpenCols() {
210 PRBool result
=!(mHasCols
|| mHasTHead
|| mHasTFoot
|| mHasTBody
);
214 PRBool
CanOpenTBody() {
215 PRBool result
=!(mHasTBody
);
219 PRBool
CanOpenTHead() {
220 PRBool result
=!(mHasTHead
|| mHasTFoot
|| mHasTBody
);
224 PRBool
CanOpenTFoot() {
225 PRBool result
=!(mHasTFoot
|| mHasTBody
);
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
253 CToken
* CreateTokenOfType(eHTMLTokenTypes aType
,eHTMLTags aTag
, const nsAString
& aString
);
254 CToken
* CreateTokenOfType(eHTMLTokenTypes aType
,eHTMLTags aTag
);
256 nsFixedSizeAllocator
& GetArenaPool() { return mArenaPool
; }
259 nsFixedSizeAllocator mArenaPool
;
261 int mTotals
[eToken_last
-1];
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
276 class 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
)); }
289 nsDeque mSharedNodes
;
290 #ifdef DEBUG_TRACK_NODES
296 nsFixedSizeAllocator mNodePool
;
299 /************************************************************************
300 The dtdcontext class defines an ordered list of tags (a context).
301 ************************************************************************/
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
);}
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
;
349 enum { eMaxTags
= MAX_REFLOW_DEPTH
};
350 eHTMLTags mXTags
[eMaxTags
];
354 /**************************************************************
355 Now define the token deallocator class...
356 **************************************************************/
357 class CTokenDeallocator
: public nsDequeFunctor
{
359 nsFixedSizeAllocator
& mArenaPool
;
362 CTokenDeallocator(nsFixedSizeAllocator
& aArenaPool
)
363 : mArenaPool(aArenaPool
) {}
365 virtual void* operator()(void* anObject
) {
366 CToken
* aToken
= (CToken
*)anObject
;
367 aToken
->Release(mArenaPool
);
373 /************************************************************************
374 ITagHandler class offers an API for taking care of specific tokens.
375 ************************************************************************/
376 class nsITagHandler
{
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
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
) {
404 return theTag
-aTagSet
;
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
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
428 **************************************************************/
430 class nsObserverEntry
: public nsIObserverEntry
{
433 nsObserverEntry(const nsAString
& aString
);
434 virtual ~nsObserverEntry();
436 NS_IMETHOD
Notify(nsIParserNode
* aNode
,
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
);
447 nsVoidArray
* mObservers
[NS_HTML_TAG_MAX
+ 1];
448 friend class nsMatchesTopic
;
451 /*********************************************************************************************/
456 const eHTMLTags
*mTags
;
460 * Find the last member of given taglist on the given context
461 * @update gess 12/14/99
464 * @return index of tag, or kNotFound if not found
466 inline PRInt32
LastOf(nsDTDContext
& aContext
, const TagList
& aTagList
){
467 int max
= aContext
.GetCount();
469 for(index
=max
-1;index
>=0;index
--){
470 PRBool result
=FindTagInSet(aContext
[index
],aTagList
.mTags
,aTagList
.mCount
);
479 * Find the first member of given taglist on the given context
480 * @update gess 12/14/99
482 * @param aStartOffset
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();
489 for(index
=aStartOffset
;index
<max
;++index
){
490 PRBool result
=FindTagInSet(aContext
[index
],aTagList
.mTags
,aTagList
.mCount
);
500 * Call this to find out whether the DTD thinks the tag requires an END tag </xxx>
501 * @update gess 01/04/99
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
));