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
5 : *
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/
10 : *
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.
15 : *
16 : * The Original Code is mozilla.org code.
17 : *
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.
22 : *
23 : * Contributor(s):
24 : *
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.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 :
40 : /**
41 : * MODULE NOTES:
42 : * @update gess 4/1/98
43 : *
44 : */
45 :
46 :
47 :
48 : #ifndef DTDUTILS_
49 : #define DTDUTILS_
50 :
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 "nsCOMArray.h"
62 : #include "nsIParserService.h"
63 : #include "nsReadableUtils.h"
64 : #include "nsIHTMLContentSink.h"
65 :
66 : #define IF_HOLD(_ptr) \
67 : PR_BEGIN_MACRO \
68 : if(_ptr) { \
69 : _ptr->AddRef(); \
70 : } \
71 : PR_END_MACRO
72 :
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
81 :
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
91 :
92 : class nsIParserNode;
93 : class nsCParserNode;
94 : class nsNodeAllocator;
95 :
96 :
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
101 :
102 : /***************************************************************
103 : First, define the tagstack class
104 : ***************************************************************/
105 :
106 : class nsEntryStack; //forware declare to make compilers happy.
107 :
108 : struct nsTagEntry {
109 448 : nsTagEntry ()
110 448 : : 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;
115 : };
116 :
117 : class nsEntryStack {
118 :
119 : public:
120 : nsEntryStack();
121 : ~nsEntryStack();
122 :
123 : nsTagEntry* PopEntry();
124 : void PushEntry(nsTagEntry* aEntry, bool aRefCntNode = true);
125 : void EnsureCapacityFor(PRInt32 aNewMax, PRInt32 aShiftOffset=0);
126 : void Push(nsCParserNode* aNode,nsEntryStack* aStyleStack=0, bool aRefCntNode = true);
127 : void PushTag(eHTMLTags aTag);
128 : void PushFront(nsCParserNode* aNode,nsEntryStack* aStyleStack=0, bool aRefCntNode = 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);
139 :
140 : /*
141 : * Release all objects in the entry stack
142 : */
143 : void ReleaseAll(nsNodeAllocator* aNodeAllocator);
144 :
145 : /**
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
150 : */
151 : inline PRInt32 FirstOf(eHTMLTags aTag) const {
152 : PRInt32 index=-1;
153 :
154 : if(0<mCount) {
155 : while(++index<mCount) {
156 : if(aTag==mEntries[index].mTag) {
157 : return index;
158 : }
159 : } //while
160 : }
161 : return kNotFound;
162 : }
163 :
164 :
165 : /**
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
170 : */
171 1885 : inline PRInt32 LastOf(eHTMLTags aTag) const {
172 1885 : PRInt32 index=mCount;
173 6433 : while(--index>=0) {
174 3894 : if(aTag==mEntries[index].mTag) {
175 1231 : return index;
176 : }
177 : }
178 654 : return kNotFound;
179 : }
180 :
181 : nsTagEntry* mEntries;
182 : PRInt32 mCount;
183 : PRInt32 mCapacity;
184 : };
185 :
186 :
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=false;
197 : mHasCols=false;
198 : mHasTHead=false;
199 : mHasTFoot=false;
200 : mHasTBody=false;
201 : mPrevious=aPreviousState;
202 : }
203 :
204 : bool CanOpenCaption() {
205 : bool result=!(mHasCaption || mHasCols || mHasTHead || mHasTFoot || mHasTBody);
206 : return result;
207 : }
208 :
209 : bool CanOpenCols() {
210 : bool result=!(mHasCols || mHasTHead || mHasTFoot || mHasTBody);
211 : return result;
212 : }
213 :
214 : bool CanOpenTBody() {
215 : bool result=!(mHasTBody);
216 : return result;
217 : }
218 :
219 : bool CanOpenTHead() {
220 : bool result=!(mHasTHead || mHasTFoot || mHasTBody);
221 : return result;
222 : }
223 :
224 : bool CanOpenTFoot() {
225 : bool result=!(mHasTFoot || mHasTBody);
226 : return result;
227 : }
228 :
229 : bool mHasCaption;
230 : bool mHasCols;
231 : bool mHasTHead;
232 : bool mHasTFoot;
233 : bool mHasTBody;
234 : CTableState *mPrevious;
235 : };
236 :
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.
242 :
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
248 : {
249 : public:
250 :
251 : nsTokenAllocator();
252 : ~nsTokenAllocator();
253 : CToken* CreateTokenOfType(eHTMLTokenTypes aType,eHTMLTags aTag, const nsAString& aString);
254 : CToken* CreateTokenOfType(eHTMLTokenTypes aType,eHTMLTags aTag);
255 :
256 5060 : nsFixedSizeAllocator& GetArenaPool() { return mArenaPool; }
257 :
258 : protected:
259 : nsFixedSizeAllocator mArenaPool;
260 : #ifdef NS_DEBUG
261 : int mTotals[eToken_last-1];
262 : #endif
263 : };
264 :
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 : ************************************************************************/
271 :
272 : #ifndef HEAP_ALLOCATED_NODES
273 : class nsCParserNode;
274 : #endif
275 :
276 : class nsNodeAllocator
277 : {
278 : public:
279 :
280 : nsNodeAllocator();
281 : ~nsNodeAllocator();
282 : nsCParserNode* CreateNode(CToken* aToken=nsnull, nsTokenAllocator* aTokenAllocator=0);
283 :
284 4081 : nsFixedSizeAllocator& GetArenaPool() { return mNodePool; }
285 :
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
294 :
295 : protected:
296 : nsFixedSizeAllocator mNodePool;
297 : };
298 :
299 : /************************************************************************
300 : The dtdcontext class defines an ordered list of tags (a context).
301 : ************************************************************************/
302 :
303 : class nsDTDContext
304 : {
305 : public:
306 : nsDTDContext();
307 : ~nsDTDContext();
308 :
309 : nsTagEntry* PopEntry();
310 : void PushEntry(nsTagEntry* aEntry, bool aRefCntNode = true);
311 : void MoveEntries(nsDTDContext& aDest, PRInt32 aCount);
312 : void Push(nsCParserNode* aNode,nsEntryStack* aStyleStack=0, bool aRefCntNode = true);
313 : void PushTag(eHTMLTags aTag);
314 : nsCParserNode* Pop(nsEntryStack*& aChildStack);
315 : nsCParserNode* Pop();
316 61 : 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 2381 : eHTMLTags operator[](PRInt32 anIndex) const {return TagAt(anIndex);}
322 : bool HasOpenContainer(eHTMLTags aTag) const;
323 : PRInt32 FirstOf(eHTMLTags aTag) const {return mStack.FirstOf(aTag);}
324 1776 : PRInt32 LastOf(eHTMLTags aTag) const {return mStack.LastOf(aTag);}
325 :
326 : void Empty(void);
327 11121 : 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);
335 :
336 : static void ReleaseGlobalObjects(void);
337 :
338 : void SetTokenAllocator(nsTokenAllocator* aTokenAllocator) { mTokenAllocator=aTokenAllocator; }
339 28 : void SetNodeAllocator(nsNodeAllocator* aNodeAllocator) { mNodeAllocator=aNodeAllocator; }
340 :
341 : nsEntryStack mStack; //this will hold a list of tagentries...
342 : PRInt32 mResidualStyleCount;
343 : PRInt32 mContextTopIndex;
344 :
345 : nsTokenAllocator *mTokenAllocator;
346 : nsNodeAllocator *mNodeAllocator;
347 :
348 : #ifdef NS_DEBUG
349 : enum { eMaxTags = MAX_REFLOW_DEPTH };
350 : eHTMLTags mXTags[eMaxTags];
351 : #endif
352 : };
353 :
354 : /**************************************************************
355 : Now define the token deallocator class...
356 : **************************************************************/
357 : class CTokenDeallocator: public nsDequeFunctor{
358 : protected:
359 : nsFixedSizeAllocator& mArenaPool;
360 :
361 : public:
362 0 : CTokenDeallocator(nsFixedSizeAllocator& aArenaPool)
363 0 : : mArenaPool(aArenaPool) {}
364 :
365 0 : virtual void* operator()(void* anObject) {
366 0 : CToken* aToken = (CToken*)anObject;
367 0 : aToken->Release(mArenaPool);
368 0 : return 0;
369 : }
370 : };
371 :
372 :
373 : /************************************************************************
374 : ITagHandler class offers an API for taking care of specific tokens.
375 : ************************************************************************/
376 : class nsITagHandler {
377 : public:
378 :
379 : virtual void SetString(const nsString &aTheString)=0;
380 : virtual nsString* GetString()=0;
381 : virtual bool HandleToken(CToken* aToken,nsIDTD* aDTD)=0;
382 : virtual bool HandleCapturedTokens(CToken* aToken,nsIDTD* aDTD)=0;
383 : };
384 :
385 : /************************************************************************
386 : Here are a few useful utility methods...
387 : ************************************************************************/
388 :
389 : /**
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
396 : */
397 6394 : inline PRInt32 IndexOfTagInSet(PRInt32 aTag,const eHTMLTags* aTagSet,PRInt32 aCount) {
398 :
399 6394 : const eHTMLTags* theEnd=aTagSet+aCount;
400 6394 : const eHTMLTags* theTag=aTagSet;
401 :
402 37849 : while(theTag<theEnd) {
403 25943 : if(aTag==*theTag) {
404 882 : return theTag-aTagSet;
405 : }
406 25061 : ++theTag;
407 : }
408 :
409 5512 : return kNotFound;
410 : }
411 :
412 : /**
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
419 : */
420 6394 : inline bool FindTagInSet(PRInt32 aTag,const eHTMLTags *aTagSet,PRInt32 aCount) {
421 6394 : return bool(-1<IndexOfTagInSet(aTag,aTagSet,aCount));
422 : }
423 :
424 : /*********************************************************************************************/
425 :
426 : struct TagList {
427 : size_t mCount;
428 : const eHTMLTags *mTags;
429 : };
430 :
431 : /**
432 : * Find the last member of given taglist on the given context
433 : * @update gess 12/14/99
434 : * @param aContext
435 : * @param aTagList
436 : * @return index of tag, or kNotFound if not found
437 : */
438 583 : inline PRInt32 LastOf(nsDTDContext& aContext, const TagList& aTagList){
439 583 : int max = aContext.GetCount();
440 : int index;
441 2035 : for(index=max-1;index>=0;index--){
442 1754 : bool result=FindTagInSet(aContext[index],aTagList.mTags,aTagList.mCount);
443 1754 : if(result) {
444 302 : return index;
445 : }
446 : }
447 281 : return kNotFound;
448 : }
449 :
450 : /**
451 : * Find the first member of given taglist on the given context
452 : * @update gess 12/14/99
453 : * @param aContext
454 : * @param aStartOffset
455 : * @param aTagList
456 : * @return index of tag, or kNotFound if not found
457 : */
458 : inline PRInt32 FirstOf(nsDTDContext& aContext,PRInt32 aStartOffset,TagList& aTagList){
459 : int max = aContext.GetCount();
460 : int index;
461 : for(index=aStartOffset;index<max;++index){
462 : bool result=FindTagInSet(aContext[index],aTagList.mTags,aTagList.mCount);
463 : if(result) {
464 : return index;
465 : }
466 : }
467 : return kNotFound;
468 : }
469 :
470 :
471 : /**
472 : * Call this to find out whether the DTD thinks the tag requires an END tag </xxx>
473 : * @update gess 01/04/99
474 : * @param id of tag
475 : * @return TRUE of the element's end tag is optional
476 : */
477 0 : inline bool HasOptionalEndTag(eHTMLTags aTag) {
478 : static eHTMLTags gHasOptionalEndTags[]={eHTMLTag_body,eHTMLTag_colgroup,eHTMLTag_dd,eHTMLTag_dt,
479 : eHTMLTag_head,eHTMLTag_li,eHTMLTag_option,
480 : eHTMLTag_p,eHTMLTag_tbody,eHTMLTag_td,eHTMLTag_tfoot,
481 : eHTMLTag_th,eHTMLTag_thead,eHTMLTag_tr,
482 : eHTMLTag_userdefined,eHTMLTag_unknown};
483 0 : return FindTagInSet(aTag,gHasOptionalEndTags,sizeof(gHasOptionalEndTags)/sizeof(eHTMLTag_body));
484 : }
485 : #endif
|