1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim: set sw=2 ts=2 et tw=78: */
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 ***** */
42 * NavDTD is an implementation of the nsIDTD interface.
43 * In particular, this class captures the behaviors of the original
44 * Navigator parser productions.
46 * This DTD, like any other in NGLayout, provides a few basic services:
47 * - First, the DTD collaborates with the Parser class to convert plain
48 * text into a sequence of HTMLTokens.
49 * - Second, the DTD describes containment rules for known elements.
50 * - Third the DTD controls and coordinates the interaction between the
51 * parsing system and content sink. (The content sink is the interface
52 * that serves as a proxy for content model).
53 * - Fourth the DTD maintains an internal style-stack to handle residual (leaky)
56 * You're most likely working in this class file because
57 * you want to add or change a behavior inherent in this DTD. The remainder
58 * of this section will describe what you need to do to affect the kind of
59 * change you want in this DTD.
61 * RESIDUAL-STYLE HANDLNG:
62 * There are a number of ways to represent style in an HTML document.
63 * 1) explicit style tags (<B>, <I> etc)
64 * 2) implicit styles (like those implicit in <Hn>)
67 * Residual style handling results from explicit style tags that are
68 * not closed. Consider this example: <p>text <b>bold </p>
69 * When the <p> tag closes, the <b> tag is NOT automatically closed.
70 * Unclosed style tags are handled by the process we call residual-style
73 * There are two aspects to residual style tag handling. The first is the
74 * construction and managing of a stack of residual style tags. The
75 * second is the automatic emission of residual style tags onto leaf content
76 * in subsequent portions of the document.This step is necessary to propagate
77 * the expected style behavior to subsequent portions of the document.
79 * Construction and managing the residual style stack is an inline process that
80 * occurs during the model building phase of the parse process. During the model-
81 * building phase of the parse process, a content stack is maintained which tracks
82 * the open container hierarchy. If a style tag(s) fails to be closed when a normal
83 * container is closed, that style tag is placed onto the residual style stack. If
84 * that style tag is subsequently closed (in most contexts), it is popped off the
85 * residual style stack -- and are of no further concern.
87 * Residual style tag emission occurs when the style stack is not empty, and leaf
88 * content occurs. In our earlier example, the <b> tag "leaked" out of the <p>
89 * container. Just before the next leaf is emitted (in this or another container) the
90 * style tags that are on the stack are emitted in succession. These same residual
91 * style tags get closed automatically when the leaf's container closes, or if a
92 * child container is opened.
96 #ifndef NS_NAVHTMLDTD__
97 #define NS_NAVHTMLDTD__
100 #include "nsISupports.h"
101 #include "nsIParser.h"
102 #include "nsHTMLTags.h"
103 #include "nsVoidArray.h"
105 #include "nsParserCIID.h"
107 #include "nsDTDUtils.h"
108 #include "nsParser.h"
109 #include "nsCycleCollectionParticipant.h"
111 class nsIHTMLContentSink
;
117 class nsTokenAllocator
;
119 /***************************************************************
120 Now the main event: CNavDTD.
122 This not so simple class performs all the duties of token
123 construction and model building. It works in conjunction with
125 ***************************************************************/
128 #pragma warning( disable : 4275 )
131 class CNavDTD
: public nsIDTD
134 #pragma warning( default : 4275 )
139 * Common constructor for navdtd. You probably want to call
140 * NS_NewNavHTMLDTD().
146 * This method is offered publically for the sole use from
147 * nsParser::ParseFragment. In general, you should prefer to use methods
148 * that are directly on nsIDTD, since those will be guaranteed to do the
151 * @param aNode The parser node that contains the token information for
153 * @param aTag The actual tag that is being opened (should correspond to
155 * @param aStyleStack The style stack that aNode might be a member of
158 nsresult
OpenContainer(const nsCParserNode
*aNode
,
160 nsEntryStack
* aStyleStack
= nsnull
);
162 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
164 NS_DECL_CYCLE_COLLECTION_CLASS(CNavDTD
)
168 * This method is called to determine whether or not a tag
169 * of one type can contain a tag of another type.
171 * @param aParent Tag of parent container
172 * @param aChild Tag of child container
173 * @return PR_TRUE if parent can contain child
175 PRBool
CanPropagate(eHTMLTags aParent
,
177 PRInt32 aParentContains
);
180 * This method gets called to determine whether a given
181 * child tag can be omitted by the given parent.
183 * @param aParent Parent tag being asked about omitting given child
184 * @param aChild Child tag being tested for omittability by parent
185 * @param aParentContains Can be 0,1,-1 (false,true, unknown)
186 * XXX should be PRInt32, not PRBool
187 * @return PR_TRUE if given tag can be omitted
189 PRBool
CanOmit(eHTMLTags aParent
,
191 PRInt32
& aParentContains
);
194 * Looking at aParent, try to see if we can propagate from aChild to
195 * aParent. If aParent is a TR tag, then see if we can start at TD instead
198 * @param aParent Tag type of parent
199 * @param aChild Tag type of child
200 * @return PR_TRUE if closure was achieved -- otherwise false
202 PRBool
ForwardPropagate(nsString
& aSequence
,
207 * Given aParent that does not contain aChild, starting with aChild's
208 * first root tag, try to find aParent. If we can reach aParent simply by
209 * going up each first root tag, then return true. Otherwise, we could not
210 * propagate from aChild up to aParent, so return false.
212 * @param aParent Tag type of parent
213 * @param aChild Tag type of child
214 * @return PR_TRUE if closure was achieved -- other false
216 PRBool
BackwardPropagate(nsString
& aSequence
,
218 eHTMLTags aChild
) const;
221 * Attempt forward and/or backward propagation for the given child within
222 * the current context vector stack. And actually open the required tags.
224 * @param aParent The tag we're trying to open this element inside of.
225 * @param aChild Type of child to be propagated.
227 void CreateContextStackFor(eHTMLTags aParent
, eHTMLTags aChild
);
230 * Ask if a given container is open anywhere on its stack
232 * @param id of container you want to test for
233 * @return TRUE if the given container type is open -- otherwise FALSE
235 PRBool
HasOpenContainer(eHTMLTags aContainer
) const;
238 * This method allows the caller to determine if a any member
239 * in a set of tags is currently open.
241 * @param aTagSet A set of tags you care about.
242 * @return PR_TRUE if any of the members of aTagSet are currently open.
244 PRBool
HasOpenContainer(const eHTMLTags aTagSet
[], PRInt32 aCount
) const;
247 * Accessor that retrieves the tag type of the topmost item on the DTD's
250 * @return The tag type (may be unknown)
252 eHTMLTags
GetTopNode() const;
255 * Finds the topmost occurrence of given tag within context vector stack.
257 * @param tag to be found
258 * @return index of topmost tag occurrence -- may be -1 (kNotFound).
260 PRInt32
LastOf(eHTMLTags aTagSet
[], PRInt32 aCount
) const;
263 * This method gets called when a start token has been
264 * encountered in the parse process. If the current container
265 * can contain this tag, then add it. Otherwise, you have
266 * two choices: 1) create an implicit container for this tag
268 * 2) close the top container, and add this to
269 * whatever container ends up on top.
271 * @param aToken -- next (start) token to be handled
272 * @return Whether or not we should block the parser.
274 nsresult
HandleStartToken(CToken
* aToken
);
277 * This method gets called when a start token has been
278 * encountered in the parse process. If the current container
279 * can contain this tag, then add it. Otherwise, you have
280 * two choices: 1) create an implicit container for this tag
282 * 2) close the top container, and add this to
283 * whatever container ends up on top.
285 * @param aToken Next (start) token to be handled.
286 * @param aChildTag The tag corresponding to aToken.
287 * @param aNode CParserNode representing this start token
288 * @return A potential request to block the parser.
290 nsresult
HandleDefaultStartToken(CToken
* aToken
, eHTMLTags aChildTag
,
291 nsCParserNode
*aNode
);
292 nsresult
HandleEndToken(CToken
* aToken
);
293 nsresult
HandleEntityToken(CToken
* aToken
);
294 nsresult
HandleCommentToken(CToken
* aToken
);
295 nsresult
HandleAttributeToken(CToken
* aToken
);
296 nsresult
HandleProcessingInstructionToken(CToken
* aToken
);
297 nsresult
HandleDocTypeDeclToken(CToken
* aToken
);
298 nsresult
BuildNeglectedTarget(eHTMLTags aTarget
, eHTMLTokenTypes aType
,
299 nsIParser
* aParser
, nsIContentSink
* aSink
);
301 nsresult
OpenHTML(const nsCParserNode
*aNode
);
302 nsresult
OpenBody(const nsCParserNode
*aNode
);
305 * The special purpose methods automatically close
306 * one or more open containers.
307 * @return error code - 0 if all went well.
309 nsresult
CloseContainer(const eHTMLTags aTag
, PRBool aMalformed
);
310 nsresult
CloseContainersTo(eHTMLTags aTag
, PRBool aClosedByStartTag
);
311 nsresult
CloseContainersTo(PRInt32 anIndex
, eHTMLTags aTag
,
312 PRBool aClosedByStartTag
);
313 nsresult
CloseResidualStyleTags(const eHTMLTags aTag
,
314 PRBool aClosedByStartTag
);
317 * Causes leaf to be added to sink at current vector pos.
318 * @param aNode is leaf node to be added.
319 * @return error code - 0 if all went well.
321 nsresult
AddLeaf(const nsIParserNode
*aNode
);
322 nsresult
AddHeadContent(nsIParserNode
*aNode
);
325 * This set of methods is used to create and manage the set of
326 * transient styles that occur as a result of poorly formed HTML
327 * or bugs in the original navigator.
329 * @param aTag -- represents the transient style tag to be handled.
330 * @return error code -- usually 0
332 nsresult
OpenTransientStyles(eHTMLTags aChildTag
,
333 PRBool aCloseInvalid
= PR_TRUE
);
334 void PopStyle(eHTMLTags aTag
);
336 nsresult
PushIntoMisplacedStack(CToken
* aToken
)
338 NS_ENSURE_ARG_POINTER(aToken
);
339 aToken
->SetNewlineCount(0); // Note: We have already counted the newlines for these tokens
341 mMisplacedContent
.Push(aToken
);
347 nsresult
CollectAttributes(nsIParserNode
* aNode
,eHTMLTags aTag
,PRInt32 aCount
);
350 * This gets called before we've handled a given start tag.
351 * It's a generic hook to let us do pre processing.
353 * @param aToken contains the tag in question
354 * @param aTag is the tag itself.
355 * @param aNode is the node (tag) with associated attributes.
356 * @return NS_OK if we should continue, a failure code otherwise.
358 nsresult
WillHandleStartTag(CToken
* aToken
,eHTMLTags aChildTag
,nsIParserNode
& aNode
);
359 nsresult
DidHandleStartTag(nsIParserNode
& aNode
,eHTMLTags aChildTag
);
362 * This method gets called when a start token has been encountered that
363 * the parent wants to omit. It stashes it in the current element if that
364 * element accepts such misplaced tokens.
366 * @param aToken Next (start) token to be handled
367 * @param aChildTag id of the child in question
368 * @param aParent id of the parent in question
369 * @param aNode CParserNode representing this start token
371 void HandleOmittedTag(CToken
* aToken
, eHTMLTags aChildTag
,
372 eHTMLTags aParent
, nsIParserNode
*aNode
);
373 nsresult
HandleSavedTokens(PRInt32 anIndex
);
374 nsresult
HandleKeyGen(nsIParserNode
*aNode
);
375 PRBool
IsAlternateTag(eHTMLTags aTag
);
376 PRBool
IsBlockElement(PRInt32 aTagID
, PRInt32 aParentID
) const;
377 PRBool
IsInlineElement(PRInt32 aTagID
, PRInt32 aParentID
) const;
379 PRBool
IsParserInDocWrite() const
381 NS_ASSERTION(mParser
&& mParser
->PeekContext(),
382 "Parser must be parsing to use this function");
384 return mParser
->PeekContext()->mPrevContext
!= nsnull
;
387 nsDeque mMisplacedContent
;
389 nsCOMPtr
<nsIHTMLContentSink
> mSink
;
390 nsTokenAllocator
* mTokenAllocator
;
391 nsDTDContext
* mBodyContext
;
392 nsDTDContext
* mTempContext
;
394 nsITokenizer
* mTokenizer
; // weak
397 nsString mScratch
; //used for various purposes; non-persistent
400 nsNodeAllocator mNodeAllocator
;
402 eParserDocType mDocType
;
403 eParserCommands mParserCommand
; //tells us to viewcontent/viewsource/viewerrors...
406 PRInt32 mOpenMapCount
;
407 PRInt32 mHeadContainerPosition
;