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
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 : * MODULE NOTES:
41 : *
42 : * NavDTD is an implementation of the nsIDTD interface.
43 : * In particular, this class captures the behaviors of the original
44 : * Navigator parser productions.
45 : *
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)
54 : * style tags.
55 : *
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.
60 : *
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>)
65 : * 3) CSS based styles
66 : *
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
71 : * tag handling.
72 : *
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.
78 : *
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.
86 : *
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.
93 : *
94 : *
95 : */
96 : #ifndef NS_NAVHTMLDTD__
97 : #define NS_NAVHTMLDTD__
98 :
99 : #include "nsIDTD.h"
100 : #include "nsISupports.h"
101 : #include "nsIParser.h"
102 : #include "nsHTMLTags.h"
103 : #include "nsDeque.h"
104 : #include "nsParserCIID.h"
105 : #include "nsDTDUtils.h"
106 : #include "nsParser.h"
107 : #include "nsCycleCollectionParticipant.h"
108 :
109 : class nsIHTMLContentSink;
110 : class nsIParserNode;
111 : class nsDTDContext;
112 : class nsEntryStack;
113 : class nsITokenizer;
114 : class nsCParserNode;
115 : class nsTokenAllocator;
116 :
117 : /***************************************************************
118 : Now the main event: CNavDTD.
119 :
120 : This not so simple class performs all the duties of token
121 : construction and model building. It works in conjunction with
122 : an nsParser.
123 : ***************************************************************/
124 :
125 : #ifdef _MSC_VER
126 : #pragma warning( disable : 4275 )
127 : #endif
128 :
129 : class CNavDTD : public nsIDTD
130 : {
131 : #ifdef _MSC_VER
132 : #pragma warning( default : 4275 )
133 : #endif
134 :
135 : public:
136 : /**
137 : * Common constructor for navdtd. You probably want to call
138 : * NS_NewNavHTMLDTD().
139 : */
140 : CNavDTD();
141 : virtual ~CNavDTD();
142 :
143 : /**
144 : * This method is offered publically for the sole use from
145 : * nsParser::ParseFragment. In general, you should prefer to use methods
146 : * that are directly on nsIDTD, since those will be guaranteed to do the
147 : * right thing.
148 : *
149 : * @param aNode The parser node that contains the token information for
150 : * this tag.
151 : * @param aTag The actual tag that is being opened (should correspond to
152 : * aNode.
153 : * @param aStyleStack The style stack that aNode might be a member of
154 : * (usually null).
155 : */
156 : nsresult OpenContainer(const nsCParserNode *aNode,
157 : eHTMLTags aTag,
158 : nsEntryStack* aStyleStack = nsnull);
159 :
160 0 : NS_DECL_CYCLE_COLLECTING_ISUPPORTS
161 : NS_DECL_NSIDTD
162 1576 : NS_DECL_CYCLE_COLLECTION_CLASS(CNavDTD)
163 :
164 : private:
165 : /**
166 : * This method is called to determine whether or not a tag
167 : * of one type can contain a tag of another type.
168 : *
169 : * @param aParent Tag of parent container
170 : * @param aChild Tag of child container
171 : * @return true if parent can contain child
172 : */
173 : bool CanPropagate(eHTMLTags aParent,
174 : eHTMLTags aChild,
175 : PRInt32 aParentContains);
176 :
177 : /**
178 : * This method gets called to determine whether a given
179 : * child tag can be omitted by the given parent.
180 : *
181 : * @param aParent Parent tag being asked about omitting given child
182 : * @param aChild Child tag being tested for omittability by parent
183 : * @param aParentContains Can be 0,1,-1 (false,true, unknown)
184 : * XXX should be PRInt32, not bool
185 : * @return true if given tag can be omitted
186 : */
187 : bool CanOmit(eHTMLTags aParent,
188 : eHTMLTags aChild,
189 : PRInt32& aParentContains);
190 :
191 : /**
192 : * Looking at aParent, try to see if we can propagate from aChild to
193 : * aParent. If aParent is a TR tag, then see if we can start at TD instead
194 : * of at aChild.
195 : *
196 : * @param aParent Tag type of parent
197 : * @param aChild Tag type of child
198 : * @return true if closure was achieved -- otherwise false
199 : */
200 : bool ForwardPropagate(nsString& aSequence,
201 : eHTMLTags aParent,
202 : eHTMLTags aChild);
203 :
204 : /**
205 : * Given aParent that does not contain aChild, starting with aChild's
206 : * first root tag, try to find aParent. If we can reach aParent simply by
207 : * going up each first root tag, then return true. Otherwise, we could not
208 : * propagate from aChild up to aParent, so return false.
209 : *
210 : * @param aParent Tag type of parent
211 : * @param aChild Tag type of child
212 : * @return true if closure was achieved -- other false
213 : */
214 : bool BackwardPropagate(nsString& aSequence,
215 : eHTMLTags aParent,
216 : eHTMLTags aChild) const;
217 :
218 : /**
219 : * Attempt forward and/or backward propagation for the given child within
220 : * the current context vector stack. And actually open the required tags.
221 : *
222 : * @param aParent The tag we're trying to open this element inside of.
223 : * @param aChild Type of child to be propagated.
224 : */
225 : void CreateContextStackFor(eHTMLTags aParent, eHTMLTags aChild);
226 :
227 : /**
228 : * Ask if a given container is open anywhere on its stack
229 : *
230 : * @param id of container you want to test for
231 : * @return TRUE if the given container type is open -- otherwise FALSE
232 : */
233 : bool HasOpenContainer(eHTMLTags aContainer) const;
234 :
235 : /**
236 : * This method allows the caller to determine if a any member
237 : * in a set of tags is currently open.
238 : *
239 : * @param aTagSet A set of tags you care about.
240 : * @return true if any of the members of aTagSet are currently open.
241 : */
242 : bool HasOpenContainer(const eHTMLTags aTagSet[], PRInt32 aCount) const;
243 :
244 : /**
245 : * Accessor that retrieves the tag type of the topmost item on the DTD's
246 : * tag stack.
247 : *
248 : * @return The tag type (may be unknown)
249 : */
250 : eHTMLTags GetTopNode() const;
251 :
252 : /**
253 : * Finds the topmost occurrence of given tag within context vector stack.
254 : *
255 : * @param tag to be found
256 : * @return index of topmost tag occurrence -- may be -1 (kNotFound).
257 : */
258 : PRInt32 LastOf(eHTMLTags aTagSet[], PRInt32 aCount) const;
259 :
260 : nsresult HandleToken(CToken* aToken);
261 :
262 : /**
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
267 : * to be stored in
268 : * 2) close the top container, and add this to
269 : * whatever container ends up on top.
270 : *
271 : * @param aToken -- next (start) token to be handled
272 : * @return Whether or not we should block the parser.
273 : */
274 : nsresult HandleStartToken(CToken* aToken);
275 :
276 : /**
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
281 : * to be stored in
282 : * 2) close the top container, and add this to
283 : * whatever container ends up on top.
284 : *
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.
289 : */
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 :
300 : nsresult OpenHTML(const nsCParserNode *aNode);
301 : nsresult OpenBody(const nsCParserNode *aNode);
302 :
303 : /**
304 : * The special purpose methods automatically close
305 : * one or more open containers.
306 : * @return error code - 0 if all went well.
307 : */
308 : nsresult CloseContainer(const eHTMLTags aTag, bool aMalformed);
309 : nsresult CloseContainersTo(eHTMLTags aTag, bool aClosedByStartTag);
310 : nsresult CloseContainersTo(PRInt32 anIndex, eHTMLTags aTag,
311 : bool aClosedByStartTag);
312 : nsresult CloseResidualStyleTags(const eHTMLTags aTag,
313 : bool aClosedByStartTag);
314 :
315 : /**
316 : * Causes leaf to be added to sink at current vector pos.
317 : * @param aNode is leaf node to be added.
318 : * @return error code - 0 if all went well.
319 : */
320 : nsresult AddLeaf(const nsIParserNode *aNode);
321 : nsresult AddHeadContent(nsIParserNode *aNode);
322 :
323 : /**
324 : * This set of methods is used to create and manage the set of
325 : * transient styles that occur as a result of poorly formed HTML
326 : * or bugs in the original navigator.
327 : *
328 : * @param aTag -- represents the transient style tag to be handled.
329 : * @return error code -- usually 0
330 : */
331 : nsresult OpenTransientStyles(eHTMLTags aChildTag,
332 : bool aCloseInvalid = true);
333 : void PopStyle(eHTMLTags aTag);
334 :
335 54 : nsresult PushIntoMisplacedStack(CToken* aToken)
336 : {
337 54 : NS_ENSURE_ARG_POINTER(aToken);
338 54 : aToken->SetNewlineCount(0); // Note: We have already counted the newlines for these tokens
339 :
340 54 : mMisplacedContent.Push(aToken);
341 54 : return NS_OK;
342 : }
343 :
344 : protected:
345 :
346 : nsresult CollectAttributes(nsIParserNode* aNode,eHTMLTags aTag,PRInt32 aCount);
347 :
348 : /**
349 : * This gets called before we've handled a given start tag.
350 : * It's a generic hook to let us do pre processing.
351 : *
352 : * @param aToken contains the tag in question
353 : * @param aTag is the tag itself.
354 : * @param aNode is the node (tag) with associated attributes.
355 : * @return NS_OK if we should continue, a failure code otherwise.
356 : */
357 : nsresult WillHandleStartTag(CToken* aToken,eHTMLTags aChildTag,nsIParserNode& aNode);
358 : nsresult DidHandleStartTag(nsIParserNode& aNode,eHTMLTags aChildTag);
359 :
360 : /**
361 : * This method gets called when a start token has been encountered that
362 : * the parent wants to omit. It stashes it in the current element if that
363 : * element accepts such misplaced tokens.
364 : *
365 : * @param aToken Next (start) token to be handled
366 : * @param aChildTag id of the child in question
367 : * @param aParent id of the parent in question
368 : * @param aNode CParserNode representing this start token
369 : */
370 : void HandleOmittedTag(CToken* aToken, eHTMLTags aChildTag,
371 : eHTMLTags aParent, nsIParserNode *aNode);
372 : nsresult HandleSavedTokens(PRInt32 anIndex);
373 : nsresult HandleKeyGen(nsIParserNode *aNode);
374 : bool IsAlternateTag(eHTMLTags aTag);
375 : bool IsBlockElement(PRInt32 aTagID, PRInt32 aParentID) const;
376 : bool IsInlineElement(PRInt32 aTagID, PRInt32 aParentID) const;
377 :
378 : nsDeque mMisplacedContent;
379 :
380 : nsCOMPtr<nsIHTMLContentSink> mSink;
381 : nsTokenAllocator* mTokenAllocator;
382 : nsDTDContext* mBodyContext;
383 : nsDTDContext* mTempContext;
384 : bool mCountLines;
385 : nsITokenizer* mTokenizer; // weak
386 :
387 : nsString mFilename;
388 : nsString mScratch; //used for various purposes; non-persistent
389 : nsCString mMimeType;
390 :
391 : nsNodeAllocator mNodeAllocator;
392 : nsDTDMode mDTDMode;
393 : eParserDocType mDocType;
394 : eParserCommands mParserCommand; //tells us to viewcontent/viewsource/viewerrors...
395 :
396 : PRInt32 mLineNumber;
397 : PRInt32 mOpenMapCount;
398 : PRInt32 mHeadContainerPosition;
399 :
400 : PRUint16 mFlags;
401 : };
402 :
403 : #endif
404 :
405 :
406 :
|