Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / parser / htmlparser / src / CNavDTD.cpp
blob623f8f84e144805f57f0a3ef71d6ae2408c05222
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
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
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):
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 ***** */
39 #include "nsDebug.h"
40 #include "nsIAtom.h"
41 #include "CNavDTD.h"
42 #include "nsHTMLTokens.h"
43 #include "nsCRT.h"
44 #include "nsParser.h"
45 #include "nsIParser.h"
46 #include "nsIHTMLContentSink.h"
47 #include "nsScanner.h"
48 #include "prenv.h"
49 #include "prtypes.h"
50 #include "prio.h"
51 #include "plstr.h"
52 #include "nsDTDUtils.h"
53 #include "nsHTMLTokenizer.h"
54 #include "nsTime.h"
55 #include "nsParserNode.h"
56 #include "nsHTMLEntities.h"
57 #include "nsLinebreakConverter.h"
58 #include "nsIFormProcessor.h"
59 #include "nsVoidArray.h"
60 #include "nsReadableUtils.h"
61 #include "nsUnicharUtils.h"
62 #include "prmem.h"
63 #include "nsIServiceManager.h"
65 #ifdef NS_DEBUG
66 #include "nsLoggingSink.h"
67 #endif
70 * Ignore kFontStyle and kPhrase tags when the stack is deep, bug 58917.
72 #define FONTSTYLE_IGNORE_DEPTH (MAX_REFLOW_DEPTH * 80 / 100)
73 #define PHRASE_IGNORE_DEPTH (MAX_REFLOW_DEPTH * 90 / 100)
75 static NS_DEFINE_CID(kFormProcessorCID, NS_FORMPROCESSOR_CID);
77 #ifdef DEBUG
78 static const char kNullToken[] = "Error: Null token given";
79 static const char kInvalidTagStackPos[] = "Error: invalid tag stack position";
80 #endif
82 #include "nsElementTable.h"
84 #ifdef MOZ_PERF_METRICS
85 # define START_TIMER() \
86 if (mParser) MOZ_TIMER_START(mParser->mParseTime); \
87 if (mParser) MOZ_TIMER_START(mParser->mDTDTime);
89 # define STOP_TIMER() \
90 if (mParser) MOZ_TIMER_STOP(mParser->mParseTime); \
91 if (mParser) MOZ_TIMER_STOP(mParser->mDTDTime);
92 #else
93 # define STOP_TIMER()
94 # define START_TIMER()
95 #endif
97 // Some flags for use by the DTD.
98 #define NS_DTD_FLAG_NONE 0x00000000
99 #define NS_DTD_FLAG_HAS_OPEN_HEAD 0x00000001
100 #define NS_DTD_FLAG_HAS_OPEN_BODY 0x00000002
101 #define NS_DTD_FLAG_HAS_OPEN_FORM 0x00000004
102 #define NS_DTD_FLAG_HAS_EXPLICIT_HEAD 0x00000008
103 #define NS_DTD_FLAG_HAD_BODY 0x00000010
104 #define NS_DTD_FLAG_HAD_FRAMESET 0x00000020
105 #define NS_DTD_FLAG_ENABLE_RESIDUAL_STYLE 0x00000040
106 #define NS_DTD_FLAG_ALTERNATE_CONTENT 0x00000080 // NOFRAMES, NOSCRIPT
107 #define NS_DTD_FLAG_MISPLACED_CONTENT 0x00000100
108 #define NS_DTD_FLAG_IN_MISPLACED_CONTENT 0x00000200
109 #define NS_DTD_FLAG_STOP_PARSING 0x00000400
111 #define NS_DTD_FLAG_HAS_MAIN_CONTAINER (NS_DTD_FLAG_HAD_BODY | \
112 NS_DTD_FLAG_HAD_FRAMESET)
114 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CNavDTD)
115 NS_INTERFACE_MAP_ENTRY(nsIDTD)
116 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDTD)
117 NS_INTERFACE_MAP_END
119 NS_IMPL_CYCLE_COLLECTING_ADDREF(CNavDTD)
120 NS_IMPL_CYCLE_COLLECTING_RELEASE(CNavDTD)
122 NS_IMPL_CYCLE_COLLECTION_1(CNavDTD, mSink)
124 CNavDTD::CNavDTD()
125 : mMisplacedContent(0),
126 mTokenAllocator(0),
127 mBodyContext(new nsDTDContext()),
128 mTempContext(0),
129 mParser(0),
130 mTokenizer(0),
131 mDTDMode(eDTDMode_quirks),
132 mDocType(eHTML_Quirks),
133 mParserCommand(eViewNormal),
134 mLineNumber(1),
135 mOpenMapCount(0),
136 mHeadContainerPosition(-1),
137 mFlags(NS_DTD_FLAG_NONE)
141 #ifdef NS_DEBUG
143 static nsLoggingSink*
144 GetLoggingSink()
146 // This returns a content sink that is useful for following what calls the DTD
147 // makes to the content sink.
149 static PRBool checkForPath = PR_TRUE;
150 static nsLoggingSink *theSink = nsnull;
151 static const char* gLogPath = nsnull;
153 if (checkForPath) {
154 // Only check once per run.
155 gLogPath = PR_GetEnv("PARSE_LOGFILE");
156 checkForPath = PR_FALSE;
160 if (gLogPath && !theSink) {
161 static nsLoggingSink gLoggingSink;
163 PRIntn theFlags = PR_CREATE_FILE | PR_RDWR;
165 // Open the record file.
166 PRFileDesc *theLogFile = PR_Open(gLogPath, theFlags, 0);
167 gLoggingSink.SetOutputStream(theLogFile, PR_TRUE);
168 theSink = &gLoggingSink;
171 return theSink;
174 #endif
176 CNavDTD::~CNavDTD()
178 delete mBodyContext;
179 delete mTempContext;
181 #ifdef NS_DEBUG
182 if (mSink) {
183 nsLoggingSink *theLogSink = GetLoggingSink();
184 if (mSink == theLogSink) {
185 theLogSink->ReleaseProxySink();
188 #endif
191 nsresult
192 CNavDTD::WillBuildModel(const CParserContext& aParserContext,
193 nsITokenizer* aTokenizer,
194 nsIContentSink* aSink)
196 nsresult result = NS_OK;
198 mFilename = aParserContext.mScanner->GetFilename();
199 mFlags = NS_DTD_FLAG_ENABLE_RESIDUAL_STYLE;
200 mLineNumber = 1;
201 mDTDMode = aParserContext.mDTDMode;
202 mParserCommand = aParserContext.mParserCommand;
203 mMimeType = aParserContext.mMimeType;
204 mDocType = aParserContext.mDocType;
205 mTokenizer = aTokenizer;
206 mBodyContext->SetNodeAllocator(&mNodeAllocator);
208 if (!aParserContext.mPrevContext && aSink) {
209 STOP_TIMER();
210 MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::WillBuildModel(), this=%p\n", this));
212 result = aSink->WillBuildModel();
214 MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::WillBuildModel(), this=%p\n", this));
215 START_TIMER();
217 if (NS_SUCCEEDED(result) && !mSink) {
218 mSink = do_QueryInterface(aSink, &result);
219 if (NS_FAILED(result)) {
220 mFlags |= NS_DTD_FLAG_STOP_PARSING;
221 return result;
225 // Let's see if the environment is set up for us to write output to
226 // a logging sink. If so, then we'll create one, and make it the
227 // proxy for the real sink we're given from the parser.
228 #ifdef NS_DEBUG
229 nsLoggingSink *theLogSink = GetLoggingSink();
230 if (theLogSink) {
231 theLogSink->SetProxySink(mSink);
232 mSink = theLogSink;
234 #endif
236 if (mSink) {
237 PRBool enabled = PR_TRUE;
238 mSink->IsEnabled(eHTMLTag_frameset, &enabled);
239 if (enabled) {
240 mFlags |= NS_IPARSER_FLAG_FRAMES_ENABLED;
243 mSink->IsEnabled(eHTMLTag_script, &enabled);
244 if (enabled) {
245 mFlags |= NS_IPARSER_FLAG_SCRIPT_ENABLED;
250 return result;
253 nsresult
254 CNavDTD::BuildModel(nsIParser* aParser,
255 nsITokenizer* aTokenizer,
256 nsITokenObserver* anObserver,
257 nsIContentSink* aSink)
259 NS_PRECONDITION(mBodyContext != nsnull,
260 "Create a context before calling build model");
262 nsresult result = NS_OK;
264 if (!aTokenizer || !aParser) {
265 return NS_OK;
268 nsITokenizer* oldTokenizer = mTokenizer;
270 mTokenizer = aTokenizer;
271 mParser = (nsParser*)aParser;
272 mTokenAllocator = mTokenizer->GetTokenAllocator();
274 if (!mSink) {
275 return (mFlags & NS_DTD_FLAG_STOP_PARSING)
276 ? NS_ERROR_HTMLPARSER_STOPPARSING
277 : result;
280 if (mBodyContext->GetCount() == 0) {
281 CToken* tempToken;
282 if (ePlainText == mDocType) {
283 tempToken =
284 mTokenAllocator->CreateTokenOfType(eToken_start, eHTMLTag_pre);
285 if (tempToken) {
286 mTokenizer->PushTokenFront(tempToken);
290 // Always open a body if frames are disabled.
291 if (!(mFlags & NS_IPARSER_FLAG_FRAMES_ENABLED)) {
292 tempToken =
293 mTokenAllocator->CreateTokenOfType(eToken_start,
294 eHTMLTag_body,
295 NS_LITERAL_STRING("body"));
296 if (tempToken) {
297 mTokenizer->PushTokenFront(tempToken);
301 // If the content model is empty, then begin by opening <html>.
302 CStartToken* theToken = (CStartToken*)mTokenizer->GetTokenAt(0);
303 if (theToken) {
304 eHTMLTags theTag = (eHTMLTags)theToken->GetTypeID();
305 eHTMLTokenTypes theType = eHTMLTokenTypes(theToken->GetTokenType());
306 if (theTag != eHTMLTag_html || theType != eToken_start) {
307 tempToken =
308 mTokenAllocator->CreateTokenOfType(eToken_start,
309 eHTMLTag_html,
310 NS_LITERAL_STRING("html"));
311 if (tempToken) {
312 mTokenizer->PushTokenFront(tempToken);
315 } else {
316 tempToken =
317 mTokenAllocator->CreateTokenOfType(eToken_start,
318 eHTMLTag_html,
319 NS_LITERAL_STRING("html"));
320 if (tempToken) {
321 mTokenizer->PushTokenFront(tempToken);
326 while (NS_SUCCEEDED(result)) {
327 if (!(mFlags & NS_DTD_FLAG_STOP_PARSING)) {
328 CToken* theToken = mTokenizer->PopToken();
329 if (!theToken) {
330 break;
332 result = HandleToken(theToken, aParser);
333 } else {
334 result = NS_ERROR_HTMLPARSER_STOPPARSING;
335 break;
338 if (NS_ERROR_HTMLPARSER_INTERRUPTED == mSink->DidProcessAToken()) {
339 // The content sink has requested that DTD interrupt processing tokens
340 // So we need to make sure the parser is in a state where it can be
341 // interrupted.
342 // The mParser->CanInterrupt will return TRUE if BuildModel was called
343 // from a place in the parser where it prepared to handle a return value of
344 // NS_ERROR_HTMLPARSER_INTERRUPTED.
345 // If the parser is processing a script's document.write we should not
346 // allow it to be interrupted.
347 // We also need to make sure that an interruption does not override
348 // a request to block the parser.
349 if (mParser->CanInterrupt() &&
350 !IsParserInDocWrite() &&
351 NS_SUCCEEDED(result)) {
352 result = NS_ERROR_HTMLPARSER_INTERRUPTED;
353 break;
358 mTokenizer = oldTokenizer;
359 return result;
362 nsresult
363 CNavDTD::BuildNeglectedTarget(eHTMLTags aTarget,
364 eHTMLTokenTypes aType,
365 nsIParser* aParser,
366 nsIContentSink* aSink)
368 NS_ASSERTION(mTokenizer, "tokenizer is null! unable to build target.");
369 NS_ASSERTION(mTokenAllocator, "unable to create tokens without an allocator.");
370 if (!mTokenizer || !mTokenAllocator) {
371 return NS_OK;
374 CToken* target = mTokenAllocator->CreateTokenOfType(aType, aTarget);
375 NS_ENSURE_TRUE(target, NS_ERROR_OUT_OF_MEMORY);
376 mTokenizer->PushTokenFront(target);
377 return BuildModel(aParser, mTokenizer, 0, aSink);
380 nsresult
381 CNavDTD::DidBuildModel(nsresult anErrorCode,
382 PRBool aNotifySink,
383 nsIParser* aParser,
384 nsIContentSink* aSink)
386 if (!aSink) {
387 return NS_OK;
390 nsresult result = NS_OK;
391 if (aParser && aNotifySink) {
392 if (NS_OK == anErrorCode) {
393 if (!(mFlags & NS_DTD_FLAG_HAS_MAIN_CONTAINER)) {
394 // This document is not a frameset document, however, it did not contain
395 // a body tag either. So, make one!. Note: Body tag is optional per spec..
396 // Also note: We ignore the return value of BuildNeglectedTarget, we
397 // can't reasonably respond to errors (or requests to block) at this
398 // point in the parsing process.
399 BuildNeglectedTarget(eHTMLTag_body, eToken_start, aParser, aSink);
401 if (mFlags & NS_DTD_FLAG_MISPLACED_CONTENT) {
402 // Looks like the misplaced contents are not processed yet.
403 // Here is our last chance to handle the misplaced content.
405 // Keep track of the top index.
406 PRInt32 topIndex = mBodyContext->mContextTopIndex;
408 // Loop until we've really consumed all of our misplaced content.
409 do {
410 mFlags &= ~NS_DTD_FLAG_MISPLACED_CONTENT;
412 // mContextTopIndex refers to the misplaced content's legal parent index.
413 result = HandleSavedTokens(mBodyContext->mContextTopIndex);
414 if (NS_FAILED(result)) {
415 NS_ERROR("Bug in the DTD");
416 break;
419 // If we start handling misplaced content while handling misplaced
420 // content, mContextTopIndex gets modified. However, this new index
421 // necessarily points to the middle of a closed tag (since we close
422 // new tags after handling the misplaced content). So we restore the
423 // insertion point after every iteration.
424 mBodyContext->mContextTopIndex = topIndex;
425 } while (mFlags & NS_DTD_FLAG_MISPLACED_CONTENT);
427 mBodyContext->mContextTopIndex = -1;
430 // Now let's disable style handling to save time when closing remaining
431 // stack members.
432 mFlags &= ~NS_DTD_FLAG_ENABLE_RESIDUAL_STYLE;
433 while (mBodyContext->GetCount() > 0) {
434 result = CloseContainersTo(mBodyContext->Last(), PR_FALSE);
435 if (NS_FAILED(result)) {
436 //No matter what, you need to call did build model.
437 aSink->DidBuildModel();
438 return result;
441 } else {
442 // If you're here, then an error occured, but we still have nodes on the stack.
443 // At a minimum, we should grab the nodes and recycle them.
444 // Just to be correct, we'll also recycle the nodes.
445 while (mBodyContext->GetCount() > 0) {
446 nsEntryStack* theChildStyles = 0;
447 nsCParserNode* theNode = mBodyContext->Pop(theChildStyles);
448 IF_DELETE(theChildStyles, &mNodeAllocator);
449 IF_FREE(theNode, &mNodeAllocator);
453 // Now make sure the misplaced content list is empty,
454 // by forcefully recycling any tokens we might find there.
455 CToken* theToken = 0;
456 while ((theToken = (CToken*)mMisplacedContent.Pop())) {
457 IF_FREE(theToken, mTokenAllocator);
461 // No matter what, you need to call did build model.
462 return aSink->DidBuildModel();
465 NS_IMETHODIMP_(void)
466 CNavDTD::Terminate()
468 mFlags |= NS_DTD_FLAG_STOP_PARSING;
472 NS_IMETHODIMP_(PRInt32)
473 CNavDTD::GetType()
475 return NS_IPARSER_FLAG_HTML;
479 * Text and some tags require a body when they're added, this function returns
480 * true for those tags.
482 * @param aToken The current token that we care about.
483 * @param aTokenizer A tokenizer that we can get the tags attributes off of.
484 * @return PR_TRUE if aToken does indeed force the body to open.
486 static PRBool
487 DoesRequireBody(CToken* aToken, nsITokenizer* aTokenizer)
489 PRBool result = PR_FALSE;
491 if (aToken) {
492 eHTMLTags theTag = (eHTMLTags)aToken->GetTypeID();
493 if (gHTMLElements[theTag].HasSpecialProperty(kRequiresBody)) {
494 if (theTag == eHTMLTag_input) {
495 // IE & Nav4x opens up a body for type=text - Bug 66985
496 // XXXbz but we don't want to open one for <input> with no
497 // type attribute? That's pretty whack.
498 PRInt32 ac = aToken->GetAttributeCount();
499 for(PRInt32 i = 0; i < ac; ++i) {
500 CAttributeToken* attr = static_cast<CAttributeToken*>
501 (aTokenizer->GetTokenAt(i));
502 const nsSubstring& name = attr->GetKey();
503 const nsAString& value = attr->GetValue();
504 // XXXbz note that this stupid case-sensitive comparison is
505 // actually depended on by sites...
506 if ((name.EqualsLiteral("type") ||
507 name.EqualsLiteral("TYPE"))
509 !(value.EqualsLiteral("hidden") ||
510 value.EqualsLiteral("HIDDEN"))) {
511 result = PR_TRUE;
512 break;
515 } else {
516 result = PR_TRUE;
521 return result;
524 static PRBool
525 ValueIsHidden(const nsAString& aValue)
527 // Having to deal with whitespace here sucks, but we have to match
528 // what the content sink does.
529 nsAutoString str(aValue);
530 str.Trim("\n\r\t\b");
531 return str.LowerCaseEqualsLiteral("hidden");
534 // Check whether aToken corresponds to a <input type="hidden"> tag. The token
535 // must be a start tag token for an <input>. This must be called at a point
536 // when all the attributes for the input are still in the tokenizer.
537 static PRBool
538 IsHiddenInput(CToken* aToken, nsITokenizer* aTokenizer)
540 NS_PRECONDITION(eHTMLTokenTypes(aToken->GetTokenType()) == eToken_start,
541 "Must be start token");
542 NS_PRECONDITION(eHTMLTags(aToken->GetTypeID()) == eHTMLTag_input,
543 "Must be <input> tag");
545 PRInt32 ac = aToken->GetAttributeCount();
546 NS_ASSERTION(ac <= aTokenizer->GetCount(),
547 "Not enough tokens in the tokenizer");
548 // But we don't really trust ourselves to get that right
549 ac = PR_MIN(ac, aTokenizer->GetCount());
551 for (PRInt32 i = 0; i < ac; ++i) {
552 NS_ASSERTION(eHTMLTokenTypes(aTokenizer->GetTokenAt(i)->GetTokenType()) ==
553 eToken_attribute, "Unexpected token type");
554 // Again, we're not sure we actually manage to guarantee that
555 if (eHTMLTokenTypes(aTokenizer->GetTokenAt(i)->GetTokenType()) !=
556 eToken_attribute) {
557 break;
560 CAttributeToken* attrToken =
561 static_cast<CAttributeToken*>(aTokenizer->GetTokenAt(i));
562 if (!attrToken->GetKey().LowerCaseEqualsLiteral("type")) {
563 continue;
566 return ValueIsHidden(attrToken->GetValue());
569 return PR_FALSE;
573 * Returns whether or not there is a tag of type aType open on aContext.
575 static PRBool
576 HasOpenTagOfType(PRInt32 aType, const nsDTDContext& aContext)
578 PRInt32 count = aContext.GetCount();
580 while (--count >= 0) {
581 if (gHTMLElements[aContext.TagAt(count)].IsMemberOf(aType)) {
582 return PR_TRUE;
586 return PR_FALSE;
589 nsresult
590 CNavDTD::HandleToken(CToken* aToken, nsIParser* aParser)
592 if (!aToken) {
593 return NS_OK;
596 nsresult result = NS_OK;
597 CHTMLToken* theToken = static_cast<CHTMLToken*>(aToken);
598 eHTMLTokenTypes theType = eHTMLTokenTypes(theToken->GetTokenType());
599 eHTMLTags theTag = (eHTMLTags)theToken->GetTypeID();
601 aToken->SetLineNumber(mLineNumber);
603 if (!IsParserInDocWrite()) {
604 mLineNumber += aToken->GetNewlineCount();
607 if (mFlags & NS_DTD_FLAG_MISPLACED_CONTENT) {
608 // Included TD & TH to fix Bug# 20797
609 static eHTMLTags gLegalElements[] = {
610 eHTMLTag_table, eHTMLTag_thead, eHTMLTag_tbody,
611 eHTMLTag_tr, eHTMLTag_td, eHTMLTag_th, eHTMLTag_tfoot
613 // Don't even try processing misplaced tokens if we're already
614 // handling misplaced content. See bug 269095
615 if (mFlags & NS_DTD_FLAG_IN_MISPLACED_CONTENT) {
616 PushIntoMisplacedStack(theToken);
617 return NS_OK;
620 eHTMLTags theParentTag = mBodyContext->Last();
621 if (FindTagInSet(theTag, gLegalElements,
622 NS_ARRAY_LENGTH(gLegalElements)) ||
623 (gHTMLElements[theParentTag].CanContain(theTag, mDTDMode) &&
624 // Here's a problem. If theTag is legal in here, we don't move it
625 // out. So if we're moving stuff out of here, the parent of theTag
626 // gets closed at this point. But some things are legal
627 // _everywhere_ and hence would effectively close out misplaced
628 // content in tables. This is undesirable, so treat them as
629 // illegal here so they'll be shipped out with their parents and
630 // siblings. See bug 40855 for an explanation (that bug was for
631 // comments, but the same issues arise with whitespace, newlines,
632 // noscript, etc). Script is special, though. Shipping it out
633 // breaks document.write stuff. See bug 243064.
634 (!gHTMLElements[theTag].HasSpecialProperty(kLegalOpen) ||
635 theTag == eHTMLTag_script)) ||
636 (theTag == eHTMLTag_input && theType == eToken_start &&
637 FindTagInSet(theParentTag, gLegalElements,
638 NS_ARRAY_LENGTH(gLegalElements)) &&
639 IsHiddenInput(theToken, mTokenizer))) {
640 // Reset the state since all the misplaced tokens are about to get
641 // handled.
642 mFlags &= ~NS_DTD_FLAG_MISPLACED_CONTENT;
644 result = HandleSavedTokens(mBodyContext->mContextTopIndex);
645 NS_ENSURE_SUCCESS(result, result);
647 mBodyContext->mContextTopIndex = -1;
648 } else {
649 PushIntoMisplacedStack(theToken);
650 return result;
655 * This section of code is used to "move" misplaced content from one location
656 * in our document model to another. (Consider what would happen if we found a
657 * <P> tag in the head.) To move content, we throw it onto the
658 * misplacedcontent deque until we can deal with it.
660 switch(theTag) {
661 case eHTMLTag_html:
662 case eHTMLTag_noframes:
663 case eHTMLTag_script:
664 case eHTMLTag_doctypeDecl:
665 case eHTMLTag_instruction:
666 break;
668 default:
669 if (!gHTMLElements[eHTMLTag_html].SectionContains(theTag, PR_FALSE)) {
670 if (!(mFlags & (NS_DTD_FLAG_HAS_MAIN_CONTAINER |
671 NS_DTD_FLAG_ALTERNATE_CONTENT))) {
672 // For bug examples from this code, see bugs: 18928, 20989.
673 // At this point we know the body/frameset aren't open.
674 // If the child belongs in the head, then handle it (which may open
675 // the head); otherwise, push it onto the misplaced stack.
677 PRBool isExclusive = PR_FALSE;
678 PRBool theChildBelongsInHead =
679 gHTMLElements[eHTMLTag_head].IsChildOfHead(theTag, isExclusive);
680 if (theChildBelongsInHead &&
681 !isExclusive &&
682 !gHTMLElements[theTag].HasSpecialProperty(kPreferHead)) {
683 if (mMisplacedContent.GetSize() == 0 &&
684 (!gHTMLElements[theTag].HasSpecialProperty(kPreferBody) ||
685 (mFlags & NS_DTD_FLAG_HAS_EXPLICIT_HEAD))) {
686 // This tag can either be in the body or the head. Since
687 // there is no indication that the body should be open,
688 // put this token in the head.
689 break;
692 // Otherwise, we have received some indication that the body is
693 // "open", so push this token onto the misplaced content stack.
694 theChildBelongsInHead = PR_FALSE;
697 if (!theChildBelongsInHead) {
698 eHTMLTags top = mBodyContext->Last();
699 NS_ASSERTION(top != eHTMLTag_userdefined,
700 "Userdefined tags should act as leaves in the head");
701 if (top != eHTMLTag_html && top != eHTMLTag_head &&
702 gHTMLElements[top].CanContain(theTag, mDTDMode)) {
703 // Some tags (such as <object> and <script>) are opened in the
704 // head and allow other non-head content to be children.
705 // Note: Userdefined tags in the head act like leaves.
706 break;
709 // If you're here then we found a child of the body that was out of
710 // place. We're going to move it to the body by storing it
711 // temporarily on the misplaced stack. However, in quirks mode, a
712 // few tags request, ambiguosly, for a BODY. - Bugs 18928, 24204.-
713 PushIntoMisplacedStack(aToken);
715 if (IsAlternateTag(theTag)) {
716 // These tags' contents are consumed as CDATA. If we simply
717 // pushed them on the misplaced content stack, the CDATA
718 // contents would force us to open a body, which could be
719 // wrong. So we collect the whole tag as misplaced in one
720 // gulp. Note that the tokenizer guarantees that there will
721 // be an end tag.
722 CToken *current = aToken;
723 while (current->GetTokenType() != eToken_end ||
724 current->GetTypeID() != theTag) {
725 current = static_cast<CToken *>(mTokenizer->PopToken());
726 NS_ASSERTION(current, "The tokenizer is not creating good "
727 "alternate tags");
728 PushIntoMisplacedStack(current);
731 // XXX Add code to also collect incorrect attributes on the
732 // end tag.
735 if (DoesRequireBody(aToken, mTokenizer)) {
736 CToken* theBodyToken =
737 mTokenAllocator->CreateTokenOfType(eToken_start,
738 eHTMLTag_body,
739 NS_LITERAL_STRING("body"));
740 result = HandleToken(theBodyToken, aParser);
742 return result;
748 if (theToken) {
749 mParser = (nsParser*)aParser;
751 switch (theType) {
752 case eToken_text:
753 case eToken_start:
754 case eToken_whitespace:
755 case eToken_newline:
756 result = HandleStartToken(theToken);
757 break;
759 case eToken_end:
760 result = HandleEndToken(theToken);
761 break;
763 case eToken_cdatasection:
764 case eToken_comment:
765 case eToken_markupDecl:
766 result = HandleCommentToken(theToken);
767 break;
769 case eToken_entity:
770 result = HandleEntityToken(theToken);
771 break;
773 case eToken_attribute:
774 result = HandleAttributeToken(theToken);
775 break;
777 case eToken_instruction:
778 result = HandleProcessingInstructionToken(theToken);
779 break;
781 case eToken_doctypeDecl:
782 result = HandleDocTypeDeclToken(theToken);
783 break;
785 default:
786 break;
789 IF_FREE(theToken, mTokenAllocator);
790 if (result == NS_ERROR_HTMLPARSER_STOPPARSING) {
791 mFlags |= NS_DTD_FLAG_STOP_PARSING;
792 } else if (NS_FAILED(result) && result != NS_ERROR_HTMLPARSER_BLOCK) {
793 result = NS_OK;
797 return result;
800 nsresult
801 CNavDTD::DidHandleStartTag(nsIParserNode& aNode, eHTMLTags aChildTag)
803 nsresult result = NS_OK;
805 switch (aChildTag) {
806 case eHTMLTag_pre:
807 case eHTMLTag_listing:
809 // Skip the 1st newline inside PRE and LISTING unless this is a
810 // plain text doc (for which we pushed a PRE in CNavDTD::BuildModel).
812 // XXX This code is incorrect in the face of misplaced <pre> and
813 // <listing> tags (as direct children of <table>).
814 CToken* theNextToken = mTokenizer->PeekToken();
815 if (ePlainText != mDocType && theNextToken) {
816 eHTMLTokenTypes theType = eHTMLTokenTypes(theNextToken->GetTokenType());
817 if (eToken_newline == theType) {
818 if (!IsParserInDocWrite()) {
819 mLineNumber += theNextToken->GetNewlineCount();
821 theNextToken = mTokenizer->PopToken();
822 IF_FREE(theNextToken, mTokenAllocator); // fix for Bug 29379
826 break;
828 default:
829 break;
832 return result;
835 PRInt32
836 CNavDTD::LastOf(eHTMLTags aTagSet[], PRInt32 aCount) const
838 for (PRInt32 theIndex = mBodyContext->GetCount() - 1; theIndex >= 0;
839 --theIndex) {
840 if (FindTagInSet((*mBodyContext)[theIndex], aTagSet, aCount)) {
841 return theIndex;
845 return kNotFound;
848 static PRBool
849 CanBeContained(eHTMLTags aChildTag, nsDTDContext& aContext)
851 /* # Interesting test cases: Result:
852 * 1. <UL><LI>..<B>..<LI> inner <LI> closes outer <LI>
853 * 2. <CENTER><DL><DT><A><CENTER> allow nested <CENTER>
854 * 3. <TABLE><TR><TD><TABLE>... allow nested <TABLE>
855 * 4. <FRAMESET> ... <FRAMESET>
857 PRBool result = PR_TRUE;
858 PRInt32 theCount = aContext.GetCount();
860 if (0 < theCount) {
861 const TagList* theRootTags = gHTMLElements[aChildTag].GetRootTags();
862 const TagList* theSpecialParents =
863 gHTMLElements[aChildTag].GetSpecialParents();
865 if (theRootTags) {
866 PRInt32 theRootIndex = LastOf(aContext, *theRootTags);
867 PRInt32 theSPIndex = theSpecialParents
868 ? LastOf(aContext, *theSpecialParents)
869 : kNotFound;
870 PRInt32 theChildIndex =
871 nsHTMLElement::GetIndexOfChildOrSynonym(aContext, aChildTag);
872 PRInt32 theTargetIndex = (theRootIndex > theSPIndex)
873 ? theRootIndex
874 : theSPIndex;
876 if (theTargetIndex == theCount-1 ||
877 (theTargetIndex == theChildIndex &&
878 gHTMLElements[aChildTag].CanContainSelf())) {
879 result = PR_TRUE;
880 } else {
881 result = PR_FALSE;
883 static eHTMLTags gTableElements[] = { eHTMLTag_td, eHTMLTag_th };
885 PRInt32 theIndex = theCount - 1;
886 while (theChildIndex < theIndex) {
887 eHTMLTags theParentTag = aContext.TagAt(theIndex--);
888 if (gHTMLElements[theParentTag].IsMemberOf(kBlockEntity) ||
889 gHTMLElements[theParentTag].IsMemberOf(kHeading) ||
890 gHTMLElements[theParentTag].IsMemberOf(kPreformatted) ||
891 gHTMLElements[theParentTag].IsMemberOf(kFormControl) || //fixes bug 44479
892 gHTMLElements[theParentTag].IsMemberOf(kList)) {
893 if (!HasOptionalEndTag(theParentTag)) {
894 result = PR_TRUE;
895 break;
897 } else if (FindTagInSet(theParentTag, gTableElements,
898 NS_ARRAY_LENGTH(gTableElements))) {
899 // Added this to catch a case we missed; bug 57173.
900 result = PR_TRUE;
901 break;
908 return result;
911 enum eProcessRule { eNormalOp, eLetInlineContainBlock };
913 nsresult
914 CNavDTD::HandleDefaultStartToken(CToken* aToken, eHTMLTags aChildTag,
915 nsCParserNode *aNode)
917 NS_PRECONDITION(nsnull != aToken, kNullToken);
919 nsresult result = NS_OK;
920 PRBool theChildIsContainer = nsHTMLElement::IsContainer(aChildTag);
922 // Client of parser is spefically trying to parse a fragment that
923 // may lack required context. Suspend containment rules if so.
924 if (mParserCommand != eViewFragment) {
925 PRBool theChildAgrees = PR_TRUE;
926 PRInt32 theIndex = mBodyContext->GetCount();
927 PRBool theParentContains = PR_FALSE;
929 do {
930 eHTMLTags theParentTag = mBodyContext->TagAt(--theIndex);
931 if (theParentTag == eHTMLTag_userdefined) {
932 continue;
935 // Figure out whether this is a hidden input inside a
936 // table/tbody/thead/tfoot/tr
937 static eHTMLTags sTableElements[] = {
938 eHTMLTag_table, eHTMLTag_thead, eHTMLTag_tbody,
939 eHTMLTag_tr, eHTMLTag_tfoot
942 PRBool isHiddenInputInsideTableElement = PR_FALSE;
943 if (aChildTag == eHTMLTag_input &&
944 FindTagInSet(theParentTag, sTableElements,
945 NS_ARRAY_LENGTH(sTableElements))) {
946 PRInt32 attrCount = aNode->GetAttributeCount();
947 for (PRInt32 attrIndex = 0; attrIndex < attrCount; ++attrIndex) {
948 const nsAString& key = aNode->GetKeyAt(attrIndex);
949 if (key.LowerCaseEqualsLiteral("type")) {
950 isHiddenInputInsideTableElement =
951 ValueIsHidden(aNode->GetValueAt(attrIndex));
952 break;
957 // Precompute containment, and pass it to CanOmit()...
958 theParentContains =
959 isHiddenInputInsideTableElement || CanContain(theParentTag, aChildTag);
960 if (!isHiddenInputInsideTableElement &&
961 CanOmit(theParentTag, aChildTag, theParentContains)) {
962 HandleOmittedTag(aToken, aChildTag, theParentTag, aNode);
963 return NS_OK;
966 eProcessRule theRule = eNormalOp;
968 if (!theParentContains &&
969 (IsBlockElement(aChildTag, theParentTag) &&
970 IsInlineElement(theParentTag, theParentTag))) {
971 // Don't test for table to fix 57554.
972 if (eHTMLTag_li != aChildTag) {
973 nsCParserNode* theParentNode = mBodyContext->PeekNode();
974 if (theParentNode && theParentNode->mToken->IsWellFormed()) {
975 theRule = eLetInlineContainBlock;
980 switch (theRule) {
981 case eNormalOp:
982 theChildAgrees = PR_TRUE;
983 if (theParentContains) {
984 eHTMLTags theAncestor = gHTMLElements[aChildTag].mRequiredAncestor;
985 if (eHTMLTag_unknown != theAncestor) {
986 theChildAgrees = HasOpenContainer(theAncestor);
989 if (theChildAgrees && theChildIsContainer) {
990 if (theParentTag != aChildTag) {
991 // Double check the power structure
992 // Note: The bit is currently set on tags such as <A> and <LI>.
993 if (gHTMLElements[aChildTag].ShouldVerifyHierarchy()) {
994 PRInt32 theChildIndex =
995 nsHTMLElement::GetIndexOfChildOrSynonym(*mBodyContext,
996 aChildTag);
998 if (kNotFound < theChildIndex && theChildIndex < theIndex) {
1000 * 1 Here's a tricky case from bug 22596: <h5><li><h5>
1001 * How do we know that the 2nd <h5> should close the <LI>
1002 * rather than nest inside the <LI>? (Afterall, the <h5>
1003 * is a legal child of the <LI>).
1005 * The way you know is that there is no root between the
1006 * two, so the <h5> binds more tightly to the 1st <h5>
1007 * than to the <LI>.
1009 * 2 Also, bug 6148 shows this case: <SPAN><DIV><SPAN>
1010 * From this case we learned not to execute this logic if
1011 * the parent is a block.
1013 * 3 Fix for 26583:
1014 * <A href=foo.html><B>foo<A href-bar.html>bar</A></B></A>
1015 * In the above example clicking on "foo" or "bar" should
1016 * link to foo.html or bar.html respectively. That is,
1017 * the inner <A> should be informed about the presence of
1018 * an open <A> above <B>..so that the inner <A> can close
1019 * out the outer <A>. The following code does it for us.
1021 * 4 Fix for 27865 [ similer to 22596 ]. Ex:
1022 * <DL><DD><LI>one<DD><LI>two
1024 theChildAgrees = CanBeContained(aChildTag, *mBodyContext);
1031 if (!(theParentContains && theChildAgrees)) {
1032 if (!CanPropagate(theParentTag, aChildTag, theParentContains)) {
1033 if (theChildIsContainer || !theParentContains) {
1034 if (!theChildAgrees &&
1035 !gHTMLElements[aChildTag].CanAutoCloseTag(*mBodyContext,
1036 theIndex,
1037 aChildTag)) {
1038 // Closing the tags above might cause non-compatible results.
1039 // Ex. <TABLE><TR><TD><TBODY>Text</TD></TR></TABLE>.
1040 // In the example above <TBODY> is badly misplaced, but
1041 // we should not attempt to close the tags above it,
1042 // The safest thing to do is to discard this tag.
1043 // XXX We get the above example wrong anyway because of
1044 // CanBeContained.
1045 return result;
1046 } else if (mBodyContext->mContextTopIndex > 0 &&
1047 theIndex <= mBodyContext->mContextTopIndex) {
1048 // Looks like the parent tag does not want to contain the
1049 // current tag ( aChildTag ). However, we have to force the
1050 // containment, when handling misplaced content, to avoid data
1051 // loss. Ref. bug 138577.
1052 theParentContains = PR_TRUE;
1053 } else {
1054 CloseContainersTo(theIndex, aChildTag, PR_TRUE);
1056 } else {
1057 break;
1059 } else {
1060 CreateContextStackFor(theParentTag, aChildTag);
1061 theIndex = mBodyContext->GetCount();
1064 break;
1066 case eLetInlineContainBlock:
1067 // Break out of this loop and open the block.
1068 theParentContains = theChildAgrees = PR_TRUE;
1069 break;
1071 default:
1072 NS_NOTREACHED("Invalid rule detected");
1074 } while (!(theParentContains && theChildAgrees));
1077 if (theChildIsContainer) {
1078 result = OpenContainer(aNode, aChildTag);
1079 } else {
1080 result = AddLeaf(aNode);
1083 return result;
1086 nsresult
1087 CNavDTD::WillHandleStartTag(CToken* aToken, eHTMLTags aTag,
1088 nsIParserNode& aNode)
1090 nsresult result = NS_OK;
1092 PRInt32 stackDepth = mBodyContext->GetCount();
1093 if (stackDepth >= FONTSTYLE_IGNORE_DEPTH &&
1094 gHTMLElements[aTag].IsMemberOf(kFontStyle)) {
1095 // Prevent bug 58917 by tossing the new kFontStyle start tag
1096 return kHierarchyTooDeep;
1099 if (stackDepth >= PHRASE_IGNORE_DEPTH &&
1100 gHTMLElements[aTag].IsMemberOf(kPhrase)) {
1101 // Prevent bug 58917 by tossing the new kPhrase start tag
1102 return kHierarchyTooDeep;
1106 * Now a little code to deal with bug #49687 (crash when layout stack gets
1107 * too deep) I've also opened this up to any container (not just inlines):
1108 * re bug 55095 Improved to handle bug 55980 (infinite loop caused when
1109 * DEPTH is exceeded and </P> is encountered by itself (<P>) is continuously
1110 * produced.
1112 if (stackDepth > MAX_REFLOW_DEPTH) {
1113 if (nsHTMLElement::IsContainer(aTag) &&
1114 !gHTMLElements[aTag].HasSpecialProperty(kHandleStrayTag)) {
1115 // Ref. bug 98261,49678,55095,55980
1116 // Instead of throwing away the current tag close it's parent
1117 // such that the stack level does not go beyond the max_reflow_depth.
1118 // This would allow leaf tags, that follow the current tag, to find
1119 // the correct node.
1120 while (stackDepth != MAX_REFLOW_DEPTH && NS_SUCCEEDED(result)) {
1121 result = CloseContainersTo(mBodyContext->Last(), PR_FALSE);
1122 --stackDepth;
1127 STOP_TIMER()
1128 MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::WillHandleStartTag(), this=%p\n", this));
1130 if (aTag <= NS_HTML_TAG_MAX) {
1131 result = mSink->NotifyTagObservers(&aNode);
1134 MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::WillHandleStartTag(), this=%p\n", this));
1135 START_TIMER()
1137 return result;
1140 static void
1141 PushMisplacedAttributes(nsIParserNode& aNode, nsDeque& aDeque)
1143 nsCParserNode& theAttrNode = static_cast<nsCParserNode &>(aNode);
1145 for (PRInt32 count = aNode.GetAttributeCount(); count > 0; --count) {
1146 CToken* theAttrToken = theAttrNode.PopAttributeTokenFront();
1147 if (theAttrToken) {
1148 theAttrToken->SetNewlineCount(0);
1149 aDeque.Push(theAttrToken);
1154 void
1155 CNavDTD::HandleOmittedTag(CToken* aToken, eHTMLTags aChildTag,
1156 eHTMLTags aParent, nsIParserNode* aNode)
1158 NS_PRECONDITION(mBodyContext != nsnull, "need a context to work with");
1160 // The trick here is to see if the parent can contain the child, but prefers
1161 // not to. Only if the parent CANNOT contain the child should we look to see
1162 // if it's potentially a child of another section. If it is, the cache it for
1163 // later.
1164 PRInt32 theTagCount = mBodyContext->GetCount();
1165 PRBool pushToken = PR_FALSE;
1167 if (gHTMLElements[aParent].HasSpecialProperty(kBadContentWatch) &&
1168 !nsHTMLElement::IsWhitespaceTag(aChildTag)) {
1169 eHTMLTags theTag = eHTMLTag_unknown;
1171 // Don't bother saving misplaced stuff in the head. This can happen in
1172 // cases like |<head><noscript><table>foo|. See bug 401169.
1173 if (mFlags & NS_DTD_FLAG_HAS_OPEN_HEAD) {
1174 NS_ASSERTION(!(mFlags & NS_DTD_FLAG_HAS_MAIN_CONTAINER),
1175 "Bad state");
1176 return;
1179 // Determine the insertion point
1180 while (theTagCount > 0) {
1181 theTag = mBodyContext->TagAt(--theTagCount);
1182 if (!gHTMLElements[theTag].HasSpecialProperty(kBadContentWatch)) {
1183 // This is our insertion point.
1184 mBodyContext->mContextTopIndex = theTagCount;
1185 break;
1189 if (mBodyContext->mContextTopIndex > -1) {
1190 pushToken = PR_TRUE;
1192 // Remember that we've stashed some misplaced content.
1193 mFlags |= NS_DTD_FLAG_MISPLACED_CONTENT;
1197 if (aChildTag != aParent &&
1198 gHTMLElements[aParent].HasSpecialProperty(kSaveMisplaced)) {
1199 NS_ASSERTION(!pushToken, "A strange element has both kBadContentWatch "
1200 "and kSaveMisplaced");
1201 pushToken = PR_TRUE;
1204 if (pushToken) {
1205 // Hold on to this token for later use. Ref Bug. 53695
1206 IF_HOLD(aToken);
1207 PushIntoMisplacedStack(aToken);
1209 // If the token is attributed then save those attributes too.
1210 PushMisplacedAttributes(*aNode, mMisplacedContent);
1215 * This method gets called when a kegen token is found.
1217 * @update harishd 05/02/00
1218 * @param aNode -- CParserNode representing keygen
1219 * @return NS_OK if all went well; ERROR if error occured
1221 nsresult
1222 CNavDTD::HandleKeyGen(nsIParserNode* aNode)
1224 nsresult result = NS_OK;
1226 if (aNode) {
1227 nsCOMPtr<nsIFormProcessor> theFormProcessor =
1228 do_GetService(kFormProcessorCID, &result);
1230 if (NS_SUCCEEDED(result)) {
1231 PRInt32 theAttrCount = aNode->GetAttributeCount();
1232 nsStringArray theContent;
1233 nsAutoString theAttribute;
1234 nsAutoString theFormType;
1235 CToken* theToken = nsnull;
1237 theFormType.AssignLiteral("select");
1239 result = theFormProcessor->ProvideContent(theFormType, theContent,
1240 theAttribute);
1241 if (NS_SUCCEEDED(result)) {
1242 nsString* theTextValue = nsnull;
1243 PRInt32 theIndex = nsnull;
1245 if (mTokenizer && mTokenAllocator) {
1246 // Populate the tokenizer with the fabricated elements in the reverse
1247 // order such that <SELECT> is on the top fo the tokenizer followed by
1248 // <OPTION>s and </SELECT>.
1249 theToken = mTokenAllocator->CreateTokenOfType(eToken_end,
1250 eHTMLTag_select);
1251 NS_ENSURE_TRUE(theToken, NS_ERROR_OUT_OF_MEMORY);
1252 mTokenizer->PushTokenFront(theToken);
1254 for (theIndex = theContent.Count()-1; theIndex > -1; --theIndex) {
1255 theTextValue = theContent[theIndex];
1256 theToken = mTokenAllocator->CreateTokenOfType(eToken_text,
1257 eHTMLTag_text,
1258 *theTextValue);
1259 NS_ENSURE_TRUE(theToken, NS_ERROR_OUT_OF_MEMORY);
1260 mTokenizer->PushTokenFront(theToken);
1262 theToken = mTokenAllocator->CreateTokenOfType(eToken_start,
1263 eHTMLTag_option);
1264 NS_ENSURE_TRUE(theToken, NS_ERROR_OUT_OF_MEMORY);
1265 mTokenizer->PushTokenFront(theToken);
1268 // The attribute ( provided by the form processor ) should be a part
1269 // of the SELECT. Placing the attribute token on the tokenizer to get
1270 // picked up by the SELECT.
1271 theToken = mTokenAllocator->CreateTokenOfType(eToken_attribute,
1272 eHTMLTag_unknown,
1273 theAttribute);
1274 NS_ENSURE_TRUE(theToken, NS_ERROR_OUT_OF_MEMORY);
1276 ((CAttributeToken*)theToken)->SetKey(NS_LITERAL_STRING("_moz-type"));
1277 mTokenizer->PushTokenFront(theToken);
1279 // Pop out NAME and CHALLENGE attributes ( from the keygen NODE ) and
1280 // place it in the tokenizer such that the attribtues get sucked into
1281 // SELECT node.
1282 for (theIndex = theAttrCount; theIndex > 0; --theIndex) {
1283 mTokenizer->PushTokenFront(((nsCParserNode*)aNode)->PopAttributeToken());
1286 theToken = mTokenAllocator->CreateTokenOfType(eToken_start,
1287 eHTMLTag_select);
1288 NS_ENSURE_TRUE(theToken, NS_ERROR_OUT_OF_MEMORY);
1290 // Increment the count because of the additional attribute from the form processor.
1291 theToken->SetAttributeCount(theAttrCount + 1);
1292 mTokenizer->PushTokenFront(theToken);
1297 return result;
1300 PRBool
1301 CNavDTD::IsAlternateTag(eHTMLTags aTag)
1303 switch (aTag) {
1304 case eHTMLTag_noembed:
1305 return PR_TRUE;
1307 case eHTMLTag_noscript:
1308 return (mFlags & NS_IPARSER_FLAG_SCRIPT_ENABLED) != 0;
1310 case eHTMLTag_iframe:
1311 case eHTMLTag_noframes:
1312 return (mFlags & NS_IPARSER_FLAG_FRAMES_ENABLED) != 0;
1314 default:
1315 return PR_FALSE;
1319 nsresult
1320 CNavDTD::HandleStartToken(CToken* aToken)
1322 NS_PRECONDITION(nsnull != aToken, kNullToken);
1324 nsCParserNode* theNode = mNodeAllocator.CreateNode(aToken, mTokenAllocator);
1325 NS_ENSURE_TRUE(theNode, NS_ERROR_OUT_OF_MEMORY);
1327 eHTMLTags theChildTag = (eHTMLTags)aToken->GetTypeID();
1328 PRInt16 attrCount = aToken->GetAttributeCount();
1329 eHTMLTags theParent = mBodyContext->Last();
1330 nsresult result = NS_OK;
1332 if (attrCount > 0) {
1333 result = CollectAttributes(theNode, theChildTag, attrCount);
1336 if (NS_OK == result) {
1337 result = WillHandleStartTag(aToken, theChildTag, *theNode);
1338 if (NS_OK == result) {
1339 PRBool isTokenHandled = PR_FALSE;
1340 PRBool theHeadIsParent = PR_FALSE;
1342 if (nsHTMLElement::IsSectionTag(theChildTag)) {
1343 switch (theChildTag) {
1344 case eHTMLTag_html:
1345 if (mBodyContext->GetCount() > 0) {
1346 result = OpenContainer(theNode, theChildTag);
1347 isTokenHandled = PR_TRUE;
1349 break;
1351 case eHTMLTag_body:
1352 if (mFlags & NS_DTD_FLAG_HAS_OPEN_BODY) {
1353 result = OpenContainer(theNode, theChildTag);
1354 isTokenHandled=PR_TRUE;
1356 break;
1358 case eHTMLTag_head:
1359 mFlags |= NS_DTD_FLAG_HAS_EXPLICIT_HEAD;
1361 if (mFlags & NS_DTD_FLAG_HAS_MAIN_CONTAINER) {
1362 HandleOmittedTag(aToken, theChildTag, theParent, theNode);
1363 isTokenHandled = PR_TRUE;
1365 break;
1367 default:
1368 break;
1372 PRBool isExclusive = PR_FALSE;
1373 theHeadIsParent = nsHTMLElement::IsChildOfHead(theChildTag, isExclusive);
1375 switch (theChildTag) {
1376 case eHTMLTag_area:
1377 if (!mOpenMapCount) {
1378 isTokenHandled = PR_TRUE;
1381 STOP_TIMER();
1382 MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::HandleStartToken(), this=%p\n", this));
1384 if (mOpenMapCount > 0 && mSink) {
1385 result = mSink->AddLeaf(*theNode);
1386 isTokenHandled = PR_TRUE;
1389 MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::HandleStartToken(), this=%p\n", this));
1390 START_TIMER();
1391 break;
1393 case eHTMLTag_image:
1394 aToken->SetTypeID(theChildTag = eHTMLTag_img);
1395 break;
1397 case eHTMLTag_keygen:
1398 result = HandleKeyGen(theNode);
1399 isTokenHandled = PR_TRUE;
1400 break;
1402 case eHTMLTag_script:
1403 // Script isn't really exclusively in the head. However, we treat it
1404 // as such to make sure that we don't pull scripts outside the head
1405 // into the body.
1406 // XXX Where does the script go in a frameset document?
1407 isExclusive = !(mFlags & NS_DTD_FLAG_HAD_BODY);
1408 break;
1410 default:;
1413 if (!isTokenHandled) {
1414 PRBool prefersBody =
1415 gHTMLElements[theChildTag].HasSpecialProperty(kPreferBody);
1417 // If this tag prefers to be in the head (when neither the head nor the
1418 // body have been explicitly opened) then check that we haven't seen the
1419 // body (true until the <body> tag has really been seen). Otherwise,
1420 // check if the head has been explicitly opened. See bug 307122.
1421 theHeadIsParent = theHeadIsParent &&
1422 (isExclusive ||
1423 (prefersBody
1424 ? (mFlags & NS_DTD_FLAG_HAS_EXPLICIT_HEAD) &&
1425 (mFlags & NS_DTD_FLAG_HAS_OPEN_HEAD)
1426 : !(mFlags & NS_DTD_FLAG_HAS_MAIN_CONTAINER)));
1428 if (theHeadIsParent) {
1429 // These tokens prefer to be in the head.
1430 result = AddHeadContent(theNode);
1431 } else {
1432 result = HandleDefaultStartToken(aToken, theChildTag, theNode);
1436 // Now do any post processing necessary on the tag...
1437 if (NS_OK == result) {
1438 DidHandleStartTag(*theNode, theChildTag);
1443 if (kHierarchyTooDeep == result) {
1444 // Reset this error to ok; all that happens here is that given inline tag
1445 // gets dropped because the stack is too deep. Don't terminate parsing.
1446 result = NS_OK;
1449 IF_FREE(theNode, &mNodeAllocator);
1450 return result;
1454 * Call this to see if you have a closeable peer on the stack that
1455 * is ABOVE one of its root tags.
1457 * @update gess 4/11/99
1458 * @param aRootTagList -- list of root tags for aTag
1459 * @param aTag -- tag to test for containership
1460 * @return PR_TRUE if given tag can contain other tags
1462 static PRBool
1463 HasCloseablePeerAboveRoot(const TagList& aRootTagList, nsDTDContext& aContext,
1464 eHTMLTags aTag, PRBool anEndTag)
1466 PRInt32 theRootIndex = LastOf(aContext, aRootTagList);
1467 const TagList* theCloseTags = anEndTag
1468 ? gHTMLElements[aTag].GetAutoCloseEndTags()
1469 : gHTMLElements[aTag].GetAutoCloseStartTags();
1470 PRInt32 theChildIndex = -1;
1472 if (theCloseTags) {
1473 theChildIndex=LastOf(aContext, *theCloseTags);
1474 } else if (anEndTag || !gHTMLElements[aTag].CanContainSelf()) {
1475 theChildIndex = aContext.LastOf(aTag);
1478 // I changed this to theRootIndex<=theChildIndex so to handle this case:
1479 // <SELECT><OPTGROUP>...</OPTGROUP>
1480 return theRootIndex<=theChildIndex;
1485 * This method is called to determine whether or not an END tag
1486 * can be autoclosed. This means that based on the current
1487 * context, the stack should be closed to the nearest matching
1488 * tag.
1490 * @param aTag -- tag enum of child to be tested
1491 * @return PR_TRUE if autoclosure should occur
1493 static eHTMLTags
1494 FindAutoCloseTargetForEndTag(eHTMLTags aCurrentTag, nsDTDContext& aContext,
1495 nsDTDMode aMode)
1497 int theTopIndex = aContext.GetCount();
1498 eHTMLTags thePrevTag = aContext.Last();
1500 if (nsHTMLElement::IsContainer(aCurrentTag)) {
1501 PRInt32 theChildIndex =
1502 nsHTMLElement::GetIndexOfChildOrSynonym(aContext, aCurrentTag);
1504 if (kNotFound < theChildIndex) {
1505 if (thePrevTag == aContext[theChildIndex]) {
1506 return aContext[theChildIndex];
1509 if (nsHTMLElement::IsBlockCloser(aCurrentTag)) {
1511 * Here's the algorithm:
1512 * Our here is sitting at aChildIndex. There are other tags above it
1513 * on the stack. We have to try to close them out, but we may encounter
1514 * one that can block us. The way to tell is by comparing each tag on
1515 * the stack against our closeTag and rootTag list.
1517 * For each tag above our hero on the stack, ask 3 questions:
1518 * 1. Is it in the closeTag list? If so, the we can skip over it
1519 * 2. Is it in the rootTag list? If so, then we're gated by it
1520 * 3. Otherwise its non-specified and we simply presume we can close it.
1522 const TagList* theCloseTags =
1523 gHTMLElements[aCurrentTag].GetAutoCloseEndTags();
1524 const TagList* theRootTags =
1525 gHTMLElements[aCurrentTag].GetEndRootTags();
1527 if (theCloseTags) {
1528 // At a mininimum, this code is needed for H1..H6
1529 while (theChildIndex < --theTopIndex) {
1530 eHTMLTags theNextTag = aContext[theTopIndex];
1531 if (!FindTagInSet(theNextTag, theCloseTags->mTags,
1532 theCloseTags->mCount) &&
1533 FindTagInSet(theNextTag, theRootTags->mTags,
1534 theRootTags->mCount)) {
1535 // We encountered a tag in root list so fail (we're gated).
1536 return eHTMLTag_unknown;
1539 // Otherwise, presume it's something we can simply ignore and
1540 // continue searching.
1543 eHTMLTags theTarget = aContext.TagAt(theChildIndex);
1544 if (aCurrentTag != theTarget) {
1545 aCurrentTag = theTarget;
1547 // If you make it here, we're ungated and found a target!
1548 return aCurrentTag;
1549 } else if (theRootTags) {
1550 // Since we didn't find any close tags, see if there is an instance of
1551 // aCurrentTag above the stack from the roottag.
1552 if (HasCloseablePeerAboveRoot(*theRootTags, aContext, aCurrentTag,
1553 PR_TRUE)) {
1554 return aCurrentTag;
1555 } else {
1556 return eHTMLTag_unknown;
1559 } else {
1560 // Ok, a much more sensible approach for non-block closers; use the tag
1561 // group to determine closure: For example: %phrasal closes %phrasal,
1562 // %fontstyle and %special
1563 return gHTMLElements[aCurrentTag].GetCloseTargetForEndTag(aContext,
1564 theChildIndex,
1565 aMode);
1570 return eHTMLTag_unknown;
1573 static void
1574 StripWSFollowingTag(eHTMLTags aChildTag, nsITokenizer* aTokenizer,
1575 nsTokenAllocator* aTokenAllocator, PRInt32& aNewlineCount)
1577 if (!aTokenizer || !aTokenAllocator) {
1578 return;
1581 CToken* theToken = aTokenizer->PeekToken();
1583 while (theToken) {
1584 eHTMLTokenTypes theType = eHTMLTokenTypes(theToken->GetTokenType());
1586 switch(theType) {
1587 case eToken_newline:
1588 ++aNewlineCount;
1589 // Fall through...
1591 case eToken_whitespace:
1592 theToken = aTokenizer->PopToken();
1593 IF_FREE(theToken, aTokenAllocator);
1595 theToken = aTokenizer->PeekToken();
1596 break;
1598 default:
1599 theToken = nsnull;
1600 break;
1606 * This method gets called when an end token has been
1607 * encountered in the parse process. If the end tag matches
1608 * the start tag on the stack, then simply close it. Otherwise,
1609 * we have a erroneous state condition. This can be because we
1610 * have a close tag with no prior open tag (user error) or because
1611 * we screwed something up in the parse process. I'm not sure
1612 * yet how to tell the difference.
1614 * @param aToken -- next (start) token to be handled
1615 * @return PR_TRUE if all went well; PR_FALSE if error occured
1617 nsresult
1618 CNavDTD::HandleEndToken(CToken* aToken)
1620 NS_PRECONDITION(nsnull != aToken, kNullToken);
1622 nsresult result = NS_OK;
1623 eHTMLTags theChildTag = (eHTMLTags)aToken->GetTypeID();
1625 // Begin by dumping any attributes (bug 143512)
1626 CollectAttributes(nsnull, theChildTag, aToken->GetAttributeCount());
1628 switch (theChildTag) {
1629 case eHTMLTag_link:
1630 case eHTMLTag_meta:
1631 break;
1633 case eHTMLTag_head:
1634 StripWSFollowingTag(theChildTag, mTokenizer, mTokenAllocator, mLineNumber);
1635 if (mBodyContext->LastOf(eHTMLTag_head) != kNotFound) {
1636 result = CloseContainersTo(eHTMLTag_head, PR_FALSE);
1638 mFlags &= ~NS_DTD_FLAG_HAS_EXPLICIT_HEAD;
1639 break;
1641 case eHTMLTag_form:
1642 result = CloseContainer(eHTMLTag_form, PR_FALSE);
1643 break;
1645 case eHTMLTag_br:
1647 // This is special NAV-QUIRKS code that allows users to use </BR>, even
1648 // though that isn't a legitimate tag.
1649 if (eDTDMode_quirks == mDTDMode) {
1650 // Use recycler and pass the token thro' HandleToken() to fix bugs
1651 // like 32782.
1652 CToken* theToken = mTokenAllocator->CreateTokenOfType(eToken_start,
1653 theChildTag);
1654 result = HandleToken(theToken, mParser);
1657 break;
1659 case eHTMLTag_body:
1660 case eHTMLTag_html:
1661 StripWSFollowingTag(theChildTag, mTokenizer, mTokenAllocator,
1662 mLineNumber);
1663 break;
1665 case eHTMLTag_script:
1666 // Note: we don't fall through to the default case because
1667 // CloseContainersTo() has the bad habit of closing tags that are opened
1668 // by document.write(). Fortunately, the tokenizer guarantees that no
1669 // actual tags appear between <script> and </script> so we won't be
1670 // closing the wrong tag.
1671 if (mBodyContext->Last() != eHTMLTag_script) {
1672 // Except if we're here, then there's probably a stray script tag.
1673 NS_ASSERTION(mBodyContext->LastOf(eHTMLTag_script) == kNotFound,
1674 "Mishandling scripts in CNavDTD");
1675 break;
1678 mBodyContext->Pop();
1679 result = CloseContainer(eHTMLTag_script, aToken->IsInError());
1680 break;
1682 default:
1684 // Now check to see if this token should be omitted, or
1685 // if it's gated from closing by the presence of another tag.
1686 if (gHTMLElements[theChildTag].CanOmitEndTag()) {
1687 PopStyle(theChildTag);
1688 } else {
1689 eHTMLTags theParentTag = mBodyContext->Last();
1691 // First open transient styles, so that we see any autoclosed style
1692 // tags.
1693 if (nsHTMLElement::IsResidualStyleTag(theChildTag)) {
1694 result = OpenTransientStyles(theChildTag);
1695 if (NS_FAILED(result)) {
1696 return result;
1700 if (kNotFound ==
1701 nsHTMLElement::GetIndexOfChildOrSynonym(*mBodyContext,
1702 theChildTag)) {
1703 // Ref: bug 30487
1704 // Make sure that we don't cross boundaries, of certain elements,
1705 // to close stylistic information.
1706 // <font face="helvetica"><table><tr><td></font></td></tr></table> some text...
1707 // In the above ex. the orphaned FONT tag, inside TD, should cross
1708 // TD boundary to close the FONT tag above TABLE.
1709 static eHTMLTags gBarriers[] = {
1710 eHTMLTag_thead, eHTMLTag_tbody, eHTMLTag_tfoot, eHTMLTag_table
1713 if (!FindTagInSet(theParentTag, gBarriers,
1714 NS_ARRAY_LENGTH(gBarriers)) &&
1715 nsHTMLElement::IsResidualStyleTag(theChildTag)) {
1716 // Fix bug 77746
1717 mBodyContext->RemoveStyle(theChildTag);
1720 // If the bit kHandleStrayTag is set then we automatically open up a
1721 // matching start tag (compatibility). Currently this bit is set on
1722 // P tag. This also fixes Bug: 22623
1723 if (gHTMLElements[theChildTag].HasSpecialProperty(kHandleStrayTag) &&
1724 mDTDMode != eDTDMode_full_standards &&
1725 mDTDMode != eDTDMode_almost_standards) {
1726 // Oh boy!! we found a "stray" tag. Nav4.x and IE introduce line
1727 // break in such cases. So, let's simulate that effect for
1728 // compatibility.
1729 // Ex. <html><body>Hello</P>There</body></html>
1730 PRInt32 theParentContains = -1;
1731 if (!CanOmit(theParentTag, theChildTag, theParentContains)) {
1732 CToken* theStartToken =
1733 mTokenAllocator->CreateTokenOfType(eToken_start, theChildTag);
1734 NS_ENSURE_TRUE(theStartToken, NS_ERROR_OUT_OF_MEMORY);
1736 // This check for NS_DTD_FLAG_IN_MISPLACED_CONTENT was added
1737 // to fix bug 142965.
1738 if (!(mFlags & NS_DTD_FLAG_IN_MISPLACED_CONTENT)) {
1739 // We're not handling misplaced content right now, just push
1740 // these new tokens back on the stack and handle them in the
1741 // regular flow of HandleToken.
1742 IF_HOLD(aToken);
1743 mTokenizer->PushTokenFront(aToken);
1744 mTokenizer->PushTokenFront(theStartToken);
1745 } else {
1746 // Oops, we're in misplaced content. Handle these tokens
1747 // directly instead of trying to push them onto the tokenizer
1748 // stack.
1749 result = HandleToken(theStartToken, mParser);
1750 NS_ENSURE_SUCCESS(result, result);
1752 IF_HOLD(aToken);
1753 result = HandleToken(aToken, mParser);
1757 return result;
1759 if (result == NS_OK) {
1760 eHTMLTags theTarget =
1761 FindAutoCloseTargetForEndTag(theChildTag, *mBodyContext,
1762 mDTDMode);
1763 if (eHTMLTag_unknown != theTarget) {
1764 result = CloseContainersTo(theTarget, PR_FALSE);
1769 break;
1772 return result;
1776 * This method will be triggered when the end of a table is
1777 * encountered. Its primary purpose is to process all the
1778 * bad-contents pertaining a particular table. The position
1779 * of the table is the token bank ID.
1781 * @update harishd 03/24/99
1782 * @param aTag - This ought to be a table tag
1785 nsresult
1786 CNavDTD::HandleSavedTokens(PRInt32 anIndex)
1788 NS_PRECONDITION(mBodyContext != nsnull && mBodyContext->GetCount() > 0, "invalid context");
1790 nsresult result = NS_OK;
1792 if (mSink && (anIndex > kNotFound)) {
1793 PRInt32 theBadTokenCount = mMisplacedContent.GetSize();
1795 if (theBadTokenCount > 0) {
1796 mFlags |= NS_DTD_FLAG_IN_MISPLACED_CONTENT;
1798 if (!mTempContext && !(mTempContext = new nsDTDContext())) {
1799 return NS_ERROR_OUT_OF_MEMORY;
1802 CToken* theToken;
1803 eHTMLTags theTag;
1804 PRInt32 attrCount;
1805 PRInt32 theTopIndex = anIndex + 1;
1806 PRInt32 theTagCount = mBodyContext->GetCount();
1807 PRBool formWasOnStack = mSink->IsFormOnStack();
1809 if (formWasOnStack) {
1810 // Do this to synchronize dtd stack and the sink stack.
1811 // Note: FORM is never on the dtd stack because its always
1812 // considered as a leaf. However, in the sink FORM can either
1813 // be a container or a leaf. Therefore, we have to check
1814 // with the sink -- Ref: Bug 20087.
1815 ++anIndex;
1818 STOP_TIMER()
1819 MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::HandleSavedTokensAbove(), this=%p\n", this));
1820 // Pause the main context and switch to the new context.
1821 result = mSink->BeginContext(anIndex);
1822 MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::HandleSavedTokensAbove(), this=%p\n", this));
1823 START_TIMER()
1825 NS_ENSURE_SUCCESS(result, result);
1827 // The body context should contain contents only upto the marked position.
1828 mBodyContext->MoveEntries(*mTempContext, theTagCount - theTopIndex);
1830 // Now flush out all the bad contents.
1831 while (theBadTokenCount-- > 0){
1832 theToken = (CToken*)mMisplacedContent.PopFront();
1833 if (theToken) {
1834 theTag = (eHTMLTags)theToken->GetTypeID();
1835 attrCount = theToken->GetAttributeCount();
1836 // Put back attributes, which once got popped out, into the
1837 // tokenizer. Make sure we preserve their ordering, however!
1838 // XXXbz would it be faster to get the tokens out with ObjectAt and
1839 // put them in the tokenizer and then PopFront them all from
1840 // mMisplacedContent?
1841 nsDeque temp;
1842 for (PRInt32 j = 0; j < attrCount; ++j) {
1843 CToken* theAttrToken = (CToken*)mMisplacedContent.PopFront();
1844 if (theAttrToken) {
1845 temp.Push(theAttrToken);
1847 theBadTokenCount--;
1849 mTokenizer->PrependTokens(temp);
1851 if (eToken_end == theToken->GetTokenType()) {
1852 // Ref: Bug 25202
1853 // Make sure that the BeginContext() is ended only by the call to
1854 // EndContext(). Ex: <center><table><a></center>.
1855 // In the Ex. above </center> should not close <center> above table.
1856 // Doing so will cause the current context to get closed prematurely.
1857 eHTMLTags closed = FindAutoCloseTargetForEndTag(theTag, *mBodyContext,
1858 mDTDMode);
1859 PRInt32 theIndex = closed != eHTMLTag_unknown
1860 ? mBodyContext->LastOf(closed)
1861 : kNotFound;
1863 if (theIndex != kNotFound &&
1864 theIndex <= mBodyContext->mContextTopIndex) {
1865 IF_FREE(theToken, mTokenAllocator);
1866 continue;
1870 // XXX This should go away, with this call, it becomes extremely
1871 // difficult to handle misplaced style and link tags, since it's hard
1872 // to propagate the block return all the way up and then re-enter this
1873 // method.
1874 result = HandleToken(theToken, mParser);
1878 if (theTopIndex != mBodyContext->GetCount()) {
1879 // CloseContainersTo does not close any forms we might have opened while
1880 // handling saved tokens, because the parser does not track forms on its
1881 // mBodyContext stack.
1882 CloseContainersTo(theTopIndex, mBodyContext->TagAt(theTopIndex),
1883 PR_TRUE);
1886 if (!formWasOnStack && mSink->IsFormOnStack()) {
1887 // If a form has appeared on the sink context stack since the beginning of
1888 // HandleSavedTokens, have the sink close it:
1889 mSink->CloseContainer(eHTMLTag_form);
1892 // Bad-contents were successfully processed. Now, itz time to get
1893 // back to the original body context state.
1894 mTempContext->MoveEntries(*mBodyContext, theTagCount - theTopIndex);
1896 STOP_TIMER()
1897 MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::HandleSavedTokensAbove(), this=%p\n", this));
1898 // Terminate the new context and switch back to the main context
1899 mSink->EndContext(anIndex);
1900 MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::HandleSavedTokensAbove(), this=%p\n", this));
1901 START_TIMER()
1903 mFlags &= ~NS_DTD_FLAG_IN_MISPLACED_CONTENT;
1906 return result;
1911 * This method gets called when an entity token has been
1912 * encountered in the parse process.
1914 * @update gess 3/25/98
1915 * @param aToken -- next (start) token to be handled
1916 * @return PR_TRUE if all went well; PR_FALSE if error occured
1918 nsresult
1919 CNavDTD::HandleEntityToken(CToken* aToken)
1921 NS_PRECONDITION(nsnull != aToken, kNullToken);
1923 nsresult result = NS_OK;
1925 const nsSubstring& theStr = aToken->GetStringValue();
1927 if (kHashsign != theStr.First() &&
1928 -1 == nsHTMLEntities::EntityToUnicode(theStr)) {
1929 // If you're here we have a bogus entity.
1930 // Convert it into a text token.
1931 CToken *theToken;
1933 nsAutoString entityName;
1934 entityName.AssignLiteral("&");
1935 entityName.Append(theStr);
1936 theToken = mTokenAllocator->CreateTokenOfType(eToken_text, eHTMLTag_text,
1937 entityName);
1938 NS_ENSURE_TRUE(theToken, NS_ERROR_OUT_OF_MEMORY);
1940 // theToken should get recycled automagically...
1941 return HandleToken(theToken, mParser);
1944 eHTMLTags theParentTag = mBodyContext->Last();
1945 nsCParserNode* theNode = mNodeAllocator.CreateNode(aToken, mTokenAllocator);
1946 NS_ENSURE_TRUE(theNode, NS_ERROR_OUT_OF_MEMORY);
1948 PRInt32 theParentContains = -1;
1949 if (CanOmit(theParentTag, eHTMLTag_entity, theParentContains)) {
1950 eHTMLTags theCurrTag = (eHTMLTags)aToken->GetTypeID();
1951 HandleOmittedTag(aToken, theCurrTag, theParentTag, theNode);
1952 } else {
1953 result = AddLeaf(theNode);
1955 IF_FREE(theNode, &mNodeAllocator);
1956 return result;
1960 * This method gets called when a comment token has been
1961 * encountered in the parse process. After making sure
1962 * we're somewhere in the body, we handle the comment
1963 * in the same code that we use for text.
1965 * @update gess 3/25/98
1966 * @param aToken -- next (start) token to be handled
1967 * @return PR_TRUE if all went well; PR_FALSE if error occured
1969 nsresult
1970 CNavDTD::HandleCommentToken(CToken* aToken)
1972 NS_PRECONDITION(nsnull != aToken, kNullToken);
1974 nsCParserNode* theNode = mNodeAllocator.CreateNode(aToken, mTokenAllocator);
1975 NS_ENSURE_TRUE(theNode, NS_ERROR_OUT_OF_MEMORY);
1977 STOP_TIMER();
1978 MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::HandleCommentToken(), this=%p\n", this));
1980 nsresult result = mSink ? mSink->AddComment(*theNode) : NS_OK;
1982 IF_FREE(theNode, &mNodeAllocator);
1984 MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::HandleCommentToken(), this=%p\n", this));
1985 START_TIMER();
1987 return result;
1992 * This method gets called when an attribute token has been
1993 * encountered in the parse process. This is an error, since
1994 * all attributes should have been accounted for in the prior
1995 * start or end tokens
1997 * @update gess 3/25/98
1998 * @param aToken -- next (start) token to be handled
1999 * @return PR_TRUE if all went well; PR_FALSE if error occured
2001 nsresult
2002 CNavDTD::HandleAttributeToken(CToken* aToken)
2004 NS_ERROR("attribute encountered -- this shouldn't happen.");
2005 return NS_OK;
2009 * This method gets called when an "instruction" token has been
2010 * encountered in the parse process.
2012 * @update gess 3/25/98
2013 * @param aToken -- next (start) token to be handled
2014 * @return PR_TRUE if all went well; PR_FALSE if error occured
2016 nsresult
2017 CNavDTD::HandleProcessingInstructionToken(CToken* aToken)
2019 NS_PRECONDITION(nsnull != aToken, kNullToken);
2021 nsCParserNode* theNode = mNodeAllocator.CreateNode(aToken, mTokenAllocator);
2022 NS_ENSURE_TRUE(theNode, NS_ERROR_OUT_OF_MEMORY);
2024 STOP_TIMER();
2025 MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::HandleProcessingInstructionToken(), this=%p\n", this));
2027 nsresult result = mSink ? mSink->AddProcessingInstruction(*theNode) : NS_OK;
2029 IF_FREE(theNode, &mNodeAllocator);
2031 MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::HandleProcessingInstructionToken(), this=%p\n", this));
2032 START_TIMER();
2034 return result;
2038 * This method gets called when a DOCTYPE token has been
2039 * encountered in the parse process.
2041 * @update harishd 09/02/99
2042 * @param aToken -- The very first token to be handled
2043 * @return PR_TRUE if all went well; PR_FALSE if error occured
2045 nsresult
2046 CNavDTD::HandleDocTypeDeclToken(CToken* aToken)
2048 NS_PRECONDITION(nsnull != aToken, kNullToken);
2050 CDoctypeDeclToken* theToken = static_cast<CDoctypeDeclToken*>(aToken);
2051 nsAutoString docTypeStr(theToken->GetStringValue());
2052 // XXX Doesn't this count the newlines twice?
2053 if (!IsParserInDocWrite()) {
2054 mLineNumber += docTypeStr.CountChar(kNewLine);
2057 PRInt32 len = docTypeStr.Length();
2058 PRInt32 pos = docTypeStr.RFindChar(kGreaterThan);
2059 if (pos != kNotFound) {
2060 // First remove '>' from the end.
2061 docTypeStr.Cut(pos, len - pos);
2064 // Now remove "<!" from the begining
2065 docTypeStr.Cut(0, 2);
2066 theToken->SetStringValue(docTypeStr);
2068 nsCParserNode* theNode = mNodeAllocator.CreateNode(aToken, mTokenAllocator);
2069 NS_ENSURE_TRUE(theNode, NS_ERROR_OUT_OF_MEMORY);
2070 STOP_TIMER();
2071 MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::HandleDocTypeDeclToken(), this=%p\n", this));
2073 nsresult result = mSink ? mSink->AddDocTypeDecl(*theNode) : NS_OK;
2075 IF_FREE(theNode, &mNodeAllocator);
2077 MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::HandleDocTypeDeclToken(), this=%p\n", this));
2078 START_TIMER();
2080 return result;
2084 * Retrieve the attributes for this node, and add then into
2085 * the node.
2087 * @update gess4/22/98
2088 * @param aNode is the node you want to collect attributes for
2089 * @param aCount is the # of attributes you're expecting
2090 * @return error code (should be 0)
2092 nsresult
2093 CNavDTD::CollectAttributes(nsIParserNode *aNode, eHTMLTags aTag, PRInt32 aCount)
2095 int attr = 0;
2096 nsresult result = NS_OK;
2097 int theAvailTokenCount = mTokenizer->GetCount();
2099 if (aCount <= theAvailTokenCount) {
2100 CToken* theToken;
2101 for (attr = 0; attr < aCount; ++attr) {
2102 theToken = mTokenizer->PopToken();
2103 if (theToken) {
2104 eHTMLTokenTypes theType = eHTMLTokenTypes(theToken->GetTokenType());
2105 if (theType != eToken_attribute) {
2106 // If you're here then it means that the token does not
2107 // belong to this node. Put the token back into the tokenizer
2108 // and let it go thro' the regular path. Bug: 59189.
2109 mTokenizer->PushTokenFront(theToken);
2110 break;
2113 if (!IsParserInDocWrite()) {
2114 mLineNumber += theToken->GetNewlineCount();
2117 if (aNode) {
2118 // If the key is empty, the attribute is unusable, so we should not
2119 // add it to the node.
2120 if (!((CAttributeToken*)theToken)->GetKey().IsEmpty()) {
2121 aNode->AddAttribute(theToken);
2122 } else {
2123 IF_FREE(theToken, mTokenAllocator);
2125 } else {
2126 IF_FREE(theToken, mTokenAllocator);
2130 } else {
2131 result = kEOF;
2133 return result;
2136 /**
2137 * This method is called to determine whether or not a tag
2138 * of one type can contain a tag of another type.
2140 * @update gess 4/8/98
2141 * @param aParent -- tag enum of parent container
2142 * @param aChild -- tag enum of child container
2143 * @return PR_TRUE if parent can contain child
2145 PRBool
2146 CNavDTD::CanContain(PRInt32 aParent, PRInt32 aChild) const
2148 PRBool result = gHTMLElements[aParent].CanContain((eHTMLTags)aChild, mDTDMode);
2150 if (eHTMLTag_nobr == aChild &&
2151 IsInlineElement(aParent, aParent) &&
2152 HasOpenContainer(eHTMLTag_nobr)) {
2153 return PR_FALSE;
2156 return result;
2160 * This method is called to determine whether or not
2161 * the given childtag is a block element.
2163 * @update gess 6June2000
2164 * @param aChildID -- tag id of child
2165 * @param aParentID -- tag id of parent (or eHTMLTag_unknown)
2166 * @return PR_TRUE if this tag is a block tag
2168 PRBool
2169 CNavDTD::IsBlockElement(PRInt32 aTagID, PRInt32 aParentID) const
2171 eHTMLTags theTag = (eHTMLTags)aTagID;
2173 return (theTag > eHTMLTag_unknown && theTag < eHTMLTag_userdefined) &&
2174 (gHTMLElements[theTag].IsMemberOf(kBlock) ||
2175 gHTMLElements[theTag].IsMemberOf(kBlockEntity) ||
2176 gHTMLElements[theTag].IsMemberOf(kHeading) ||
2177 gHTMLElements[theTag].IsMemberOf(kPreformatted) ||
2178 gHTMLElements[theTag].IsMemberOf(kList));
2182 * This method is called to determine whether or not
2183 * the given childtag is an inline element.
2185 * @update gess 6June2000
2186 * @param aChildID -- tag id of child
2187 * @param aParentID -- tag id of parent (or eHTMLTag_unknown)
2188 * @return PR_TRUE if this tag is an inline tag
2190 PRBool
2191 CNavDTD::IsInlineElement(PRInt32 aTagID, PRInt32 aParentID) const
2193 eHTMLTags theTag = (eHTMLTags)aTagID;
2195 return (theTag > eHTMLTag_unknown && theTag < eHTMLTag_userdefined) &&
2196 (gHTMLElements[theTag].IsMemberOf(kInlineEntity) ||
2197 gHTMLElements[theTag].IsMemberOf(kFontStyle) ||
2198 gHTMLElements[theTag].IsMemberOf(kPhrase) ||
2199 gHTMLElements[theTag].IsMemberOf(kSpecial) ||
2200 gHTMLElements[theTag].IsMemberOf(kFormControl));
2204 * This method is called to determine whether or not
2205 * the necessary intermediate tags should be propagated
2206 * between the given parent and given child.
2208 * @update gess 4/8/98
2209 * @param aParent -- tag enum of parent container
2210 * @param aChild -- tag enum of child container
2211 * @return PR_TRUE if propagation should occur
2213 PRBool
2214 CNavDTD::CanPropagate(eHTMLTags aParent, eHTMLTags aChild,
2215 PRInt32 aParentContains)
2217 PRBool result = PR_FALSE;
2218 if (aParentContains == -1) {
2219 aParentContains = CanContain(aParent, aChild);
2222 if (aParent == aChild) {
2223 return result;
2226 if (nsHTMLElement::IsContainer(aChild)) {
2227 mScratch.Truncate();
2228 if (!gHTMLElements[aChild].HasSpecialProperty(kNoPropagate)) {
2229 if (nsHTMLElement::IsBlockParent(aParent) ||
2230 gHTMLElements[aParent].GetSpecialChildren()) {
2231 result = ForwardPropagate(mScratch, aParent, aChild);
2232 if (!result) {
2233 if (eHTMLTag_unknown != aParent) {
2234 result = BackwardPropagate(mScratch, aParent, aChild);
2235 } else {
2236 result = BackwardPropagate(mScratch, eHTMLTag_html, aChild);
2241 if (mScratch.Length() - 1 > gHTMLElements[aParent].mPropagateRange) {
2242 result = PR_FALSE;
2244 } else {
2245 result = !!aParentContains;
2249 return result;
2254 * This method gets called to determine whether a given
2255 * tag can be omitted from opening. Most cannot.
2257 * @param aParent
2258 * @param aChild
2259 * @param aParentContains
2260 * @return PR_TRUE if given tag can contain other tags
2262 PRBool
2263 CNavDTD::CanOmit(eHTMLTags aParent, eHTMLTags aChild, PRInt32& aParentContains)
2265 eHTMLTags theAncestor = gHTMLElements[aChild].mExcludingAncestor;
2266 if (eHTMLTag_unknown != theAncestor && HasOpenContainer(theAncestor)) {
2267 return PR_TRUE;
2270 theAncestor = gHTMLElements[aChild].mRequiredAncestor;
2271 if (eHTMLTag_unknown != theAncestor) {
2272 // If there's a required ancestor, we only omit if it isn't open and we
2273 // can't get to it through propagation.
2274 return !HasOpenContainer(theAncestor) &&
2275 !CanPropagate(aParent, aChild, aParentContains);
2278 if (gHTMLElements[aParent].CanExclude(aChild)) {
2279 return PR_TRUE;
2282 // Now the obvious test: if the parent can contain the child, don't omit.
2283 if (-1 == aParentContains) {
2284 aParentContains = CanContain(aParent, aChild);
2287 if (aParentContains || aChild == aParent) {
2288 return PR_FALSE;
2291 if (gHTMLElements[aParent].IsBlockEntity() &&
2292 nsHTMLElement::IsInlineEntity(aChild)) {
2293 // Feel free to drop inlines that a block doesn't contain.
2294 return PR_TRUE;
2297 if (gHTMLElements[aParent].HasSpecialProperty(kBadContentWatch)) {
2298 // We can only omit this child if it does not have the kBadContentWatch
2299 // special property.
2300 return !gHTMLElements[aChild].HasSpecialProperty(kBadContentWatch);
2303 if (gHTMLElements[aParent].HasSpecialProperty(kSaveMisplaced)) {
2304 return PR_TRUE;
2307 if (aParent == eHTMLTag_body) {
2308 // There are very few tags that the body does not contain. If we get here
2309 // the best thing to do is just drop them.
2310 return PR_TRUE;
2313 return PR_FALSE;
2317 * This method gets called to determine whether a given
2318 * tag is itself a container
2320 * @update gess 4/8/98
2321 * @param aTag -- tag to test as a container
2322 * @return PR_TRUE if given tag can contain other tags
2324 PRBool
2325 CNavDTD::IsContainer(PRInt32 aTag) const
2327 return nsHTMLElement::IsContainer((eHTMLTags)aTag);
2331 PRBool
2332 CNavDTD::ForwardPropagate(nsString& aSequence, eHTMLTags aParent,
2333 eHTMLTags aChild)
2335 PRBool result = PR_FALSE;
2337 switch(aParent) {
2338 case eHTMLTag_table:
2339 if (eHTMLTag_tr == aChild || eHTMLTag_td == aChild) {
2340 return BackwardPropagate(aSequence, aParent, aChild);
2342 // Otherwise, intentionally fall through...
2344 case eHTMLTag_tr:
2345 if (CanContain(eHTMLTag_td, aChild)) {
2346 aSequence.Append((PRUnichar)eHTMLTag_td);
2347 result = BackwardPropagate(aSequence, aParent, eHTMLTag_td);
2349 break;
2351 default:
2352 break;
2355 return result;
2358 PRBool
2359 CNavDTD::BackwardPropagate(nsString& aSequence, eHTMLTags aParent,
2360 eHTMLTags aChild) const
2362 eHTMLTags theParent = aParent;
2364 do {
2365 const TagList* theRootTags = gHTMLElements[aChild].GetRootTags();
2366 if (!theRootTags) {
2367 break;
2370 theParent = theRootTags->mTags[0];
2371 NS_ASSERTION(CanContain(theParent, aChild),
2372 "Children must be contained by their root tags");
2374 aChild = theParent;
2375 aSequence.Append((PRUnichar)theParent);
2376 } while (theParent != eHTMLTag_unknown && theParent != aParent);
2378 return aParent == theParent;
2381 PRBool CNavDTD::HasOpenContainer(eHTMLTags aContainer) const
2383 switch (aContainer) {
2384 case eHTMLTag_form:
2385 return !(~mFlags & NS_DTD_FLAG_HAS_OPEN_FORM);
2386 case eHTMLTag_map:
2387 return mOpenMapCount > 0;
2388 default:
2389 return mBodyContext->HasOpenContainer(aContainer);
2393 PRBool
2394 CNavDTD::HasOpenContainer(const eHTMLTags aTagSet[], PRInt32 aCount) const
2396 int theIndex;
2397 int theTopIndex = mBodyContext->GetCount() - 1;
2399 for (theIndex = theTopIndex; theIndex > 0; --theIndex) {
2400 if (FindTagInSet((*mBodyContext)[theIndex], aTagSet, aCount)) {
2401 return PR_TRUE;
2405 return PR_FALSE;
2408 eHTMLTags
2409 CNavDTD::GetTopNode() const
2411 return mBodyContext->Last();
2415 * It is with great trepidation that I offer this method (privately of course).
2416 * The gets called whenever a container gets opened. This methods job is to
2417 * take a look at the (transient) style stack, and open any style containers that
2418 * are there. Of course, we shouldn't bother to open styles that are incompatible
2419 * with our parent container.
2421 * @update gess6/4/98
2422 * @param tag of the container just opened
2423 * @return 0 (for now)
2425 nsresult
2426 CNavDTD::OpenTransientStyles(eHTMLTags aChildTag, PRBool aCloseInvalid)
2428 nsresult result = NS_OK;
2430 // No need to open transient styles in head context - Fix for 41427
2431 if ((mFlags & NS_DTD_FLAG_ENABLE_RESIDUAL_STYLE) &&
2432 eHTMLTag_newline != aChildTag &&
2433 !(mFlags & NS_DTD_FLAG_HAS_OPEN_HEAD)) {
2434 if (CanContain(eHTMLTag_font, aChildTag)) {
2435 PRUint32 theCount = mBodyContext->GetCount();
2436 PRUint32 theLevel = theCount;
2438 // This first loop is used to determine how far up the containment
2439 // hierarchy we go looking for residual styles.
2440 while (1 < theLevel) {
2441 eHTMLTags theParentTag = mBodyContext->TagAt(--theLevel);
2442 if (gHTMLElements[theParentTag].HasSpecialProperty(kNoStyleLeaksIn)) {
2443 break;
2447 mFlags &= ~NS_DTD_FLAG_ENABLE_RESIDUAL_STYLE;
2448 for (; theLevel < theCount; ++theLevel) {
2449 nsEntryStack* theStack = mBodyContext->GetStylesAt(theLevel);
2450 if (theStack) {
2451 // Don't open transient styles if it makes the stack deep, bug 58917.
2452 if (theCount + theStack->mCount >= FONTSTYLE_IGNORE_DEPTH) {
2453 break;
2456 PRInt32 sindex = 0;
2458 nsTagEntry *theEntry = theStack->mEntries;
2459 PRBool isHeadingOpen = HasOpenTagOfType(kHeading, *mBodyContext);
2460 for (sindex = 0; sindex < theStack->mCount; ++sindex) {
2461 nsCParserNode* theNode = (nsCParserNode*)theEntry->mNode;
2462 if (1 == theNode->mUseCount) {
2463 eHTMLTags theNodeTag = (eHTMLTags)theNode->GetNodeType();
2464 if (gHTMLElements[theNodeTag].CanContain(aChildTag, mDTDMode)) {
2465 // XXX The following comment is entirely incoherent.
2466 // We do this too, because this entry differs from the new one
2467 // we're pushing.
2468 theEntry->mParent = theStack;
2469 if (isHeadingOpen) {
2470 // Bug 77352
2471 // The style system needs to identify residual style tags
2472 // within heading tags so that heading tags' size can take
2473 // precedence over the residual style tags' size info..
2474 // *Note: Make sure that this attribute is transient since it
2475 // should not get carried over to cases other than heading.
2476 CAttributeToken theAttrToken(NS_LITERAL_STRING("_moz-rs-heading"),
2477 EmptyString());
2478 theNode->AddAttribute(&theAttrToken);
2479 result = OpenContainer(theNode, theNodeTag, theStack);
2480 theNode->PopAttributeToken();
2481 } else {
2482 result = OpenContainer(theNode, theNodeTag, theStack);
2484 } else if (aCloseInvalid) {
2485 // If the node tag can't contain the child tag, then remove the
2486 // child tag from the style stack
2487 nsCParserNode* node = theStack->Remove(sindex, theNodeTag);
2488 IF_FREE(node, &mNodeAllocator);
2489 --theEntry;
2492 ++theEntry;
2496 mFlags |= NS_DTD_FLAG_ENABLE_RESIDUAL_STYLE;
2500 return result;
2504 * This method gets called when an explicit style close-tag is encountered.
2505 * It results in the style tag id being popped from our internal style stack.
2507 * @update gess6/4/98
2508 * @param
2509 * @return 0 if all went well (which it always does)
2511 void
2512 CNavDTD::PopStyle(eHTMLTags aTag)
2514 if ((mFlags & NS_DTD_FLAG_ENABLE_RESIDUAL_STYLE) &&
2515 nsHTMLElement::IsResidualStyleTag(aTag)) {
2516 nsCParserNode* node = mBodyContext->PopStyle(aTag);
2517 IF_FREE(node, &mNodeAllocator);
2523 * This method does two things: 1st, help construct
2524 * our own internal model of the content-stack; and
2525 * 2nd, pass this message on to the sink.
2527 * @update gess4/22/98
2528 * @param aNode -- next node to be added to model
2530 nsresult
2531 CNavDTD::OpenHTML(const nsCParserNode *aNode)
2533 NS_PRECONDITION(mBodyContext->GetCount() >= 0, kInvalidTagStackPos);
2535 STOP_TIMER();
2536 MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::OpenHTML(), this=%p\n", this));
2538 nsresult result = mSink ? mSink->OpenContainer(*aNode) : NS_OK;
2540 MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::OpenHTML(), this=%p\n", this));
2541 START_TIMER();
2543 // Don't push more than one HTML tag into the stack.
2544 if (mBodyContext->GetCount() == 0) {
2545 mBodyContext->Push(const_cast<nsCParserNode*>(aNode), 0, PR_FALSE);
2548 return result;
2552 * This method does two things: 1st, help construct
2553 * our own internal model of the content-stack; and
2554 * 2nd, pass this message on to the sink.
2555 * @update gess4/6/98
2556 * @param aNode -- next node to be added to model
2557 * @return TRUE if ok, FALSE if error
2559 nsresult
2560 CNavDTD::OpenBody(const nsCParserNode *aNode)
2562 NS_PRECONDITION(mBodyContext->GetCount() >= 0, kInvalidTagStackPos);
2564 nsresult result = NS_OK;
2566 if (!(mFlags & NS_DTD_FLAG_HAD_FRAMESET)) {
2567 mFlags |= NS_DTD_FLAG_HAD_BODY;
2569 STOP_TIMER();
2570 MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::OpenBody(), this=%p\n", this));
2572 // Make sure the head is closed by the time the body is opened.
2573 CloseContainer(eHTMLTag_head, PR_FALSE);
2575 // Now we can open the body.
2576 result = mSink ? mSink->OpenContainer(*aNode) : NS_OK;
2578 MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::OpenBody(), this=%p\n", this));
2579 START_TIMER();
2581 if (!HasOpenContainer(eHTMLTag_body)) {
2582 mBodyContext->Push(const_cast<nsCParserNode*>(aNode), 0, PR_FALSE);
2583 mTokenizer->PrependTokens(mMisplacedContent);
2587 return result;
2591 * This method does two things: 1st, help construct
2592 * our own internal model of the content-stack; and
2593 * 2nd, pass this message on to the sink.
2594 * @update gess4/6/98
2595 * @param aNode -- next node to be added to model
2596 * @param aClosedByStartTag -- ONLY TRUE if the container is being closed by opening of another container.
2597 * @return TRUE if ok, FALSE if error
2599 nsresult
2600 CNavDTD::OpenContainer(const nsCParserNode *aNode,
2601 eHTMLTags aTag,
2602 nsEntryStack* aStyleStack)
2604 NS_PRECONDITION(mBodyContext->GetCount() >= 0, kInvalidTagStackPos);
2606 nsresult result = NS_OK;
2607 PRBool done = PR_TRUE;
2608 PRBool rs_tag = nsHTMLElement::IsResidualStyleTag(aTag);
2609 // We need to open transient styles to encompass the <li> so that the bullets
2610 // inherit the proper colors.
2611 PRBool li_tag = aTag == eHTMLTag_li;
2613 if (rs_tag || li_tag) {
2615 * Here's an interesting problem:
2617 * If there's an <a> on the RS-stack, and you're trying to open
2618 * another <a>, the one on the RS-stack should be discarded.
2620 * I'm updating OpenTransientStyles to throw old <a>'s away.
2622 OpenTransientStyles(aTag, !li_tag);
2625 switch (aTag) {
2626 case eHTMLTag_html:
2627 result = OpenHTML(aNode);
2628 break;
2630 case eHTMLTag_head:
2631 if (!(mFlags & NS_DTD_FLAG_HAS_OPEN_HEAD)) {
2632 mFlags |= NS_DTD_FLAG_HAS_OPEN_HEAD;
2633 done = PR_FALSE;
2635 break;
2637 case eHTMLTag_body:
2639 eHTMLTags theParent = mBodyContext->Last();
2640 if (!gHTMLElements[aTag].IsSpecialParent(theParent)) {
2641 mFlags |= NS_DTD_FLAG_HAS_OPEN_BODY;
2642 result = OpenBody(aNode);
2643 } else {
2644 done = PR_FALSE;
2647 break;
2649 case eHTMLTag_map:
2650 ++mOpenMapCount;
2651 done = PR_FALSE;
2652 break;
2654 case eHTMLTag_form:
2655 // Discard nested forms - bug 72639
2656 if (!(mFlags & NS_DTD_FLAG_HAS_OPEN_FORM)) {
2657 mFlags |= NS_DTD_FLAG_HAS_OPEN_FORM;
2658 result = mSink ? mSink->OpenContainer(*aNode) : NS_OK;
2660 break;
2662 case eHTMLTag_frameset:
2663 // Make sure that the head is closed before we try to open this frameset.
2664 CloseContainer(eHTMLTag_head, PR_FALSE);
2666 // Now that the head is closed, continue on with opening this frameset.
2667 mFlags |= NS_DTD_FLAG_HAD_FRAMESET;
2668 done = PR_FALSE;
2669 break;
2671 case eHTMLTag_noembed:
2672 // <noembed> is unconditionally alternate content.
2673 done = PR_FALSE;
2674 mFlags |= NS_DTD_FLAG_ALTERNATE_CONTENT;
2675 break;
2677 case eHTMLTag_noscript:
2678 // We want to make sure that OpenContainer gets called below since we're
2679 // not doing it here
2680 done = PR_FALSE;
2682 if (mFlags & NS_IPARSER_FLAG_SCRIPT_ENABLED) {
2683 // XXX This flag doesn't currently do anything (and would be
2684 // insufficient if it did).
2685 mFlags |= NS_DTD_FLAG_ALTERNATE_CONTENT;
2687 break;
2689 case eHTMLTag_iframe: // Bug 84491
2690 case eHTMLTag_noframes:
2691 done = PR_FALSE;
2692 if (mFlags & NS_IPARSER_FLAG_FRAMES_ENABLED) {
2693 mFlags |= NS_DTD_FLAG_ALTERNATE_CONTENT;
2695 break;
2697 default:
2698 done = PR_FALSE;
2699 break;
2702 if (!done) {
2703 STOP_TIMER();
2704 MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::OpenContainer(), this=%p\n", this));
2706 result = mSink ? mSink->OpenContainer(*aNode) : NS_OK;
2708 MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::OpenContainer(), this=%p\n", this));
2709 START_TIMER();
2711 // For residual style tags rs_tag will be true and hence
2712 // the body context will hold an extra reference to the node.
2713 mBodyContext->Push(const_cast<nsCParserNode*>(aNode), aStyleStack, rs_tag);
2716 return result;
2719 nsresult
2720 CNavDTD::CloseResidualStyleTags(const eHTMLTags aTag,
2721 PRBool aClosedByStartTag)
2723 const PRInt32 count = mBodyContext->GetCount();
2724 PRInt32 pos = count;
2725 while (nsHTMLElement::IsResidualStyleTag(mBodyContext->TagAt(pos - 1)))
2726 --pos;
2727 if (pos < count)
2728 return CloseContainersTo(pos, aTag, aClosedByStartTag);
2729 return NS_OK;
2733 * This method does two things: 1st, help construct
2734 * our own internal model of the content-stack; and
2735 * 2nd, pass this message on to the sink.
2736 * @update gess4/6/98
2737 * @param aTag -- id of tag to be closed
2738 * @return TRUE if ok, FALSE if error
2740 nsresult
2741 CNavDTD::CloseContainer(const eHTMLTags aTag, PRBool aMalformed)
2743 nsresult result = NS_OK;
2744 PRBool done = PR_TRUE;
2746 switch (aTag) {
2747 case eHTMLTag_head:
2748 if (mFlags & NS_DTD_FLAG_HAS_OPEN_HEAD) {
2749 mFlags &= ~NS_DTD_FLAG_HAS_OPEN_HEAD;
2750 if (mBodyContext->Last() == eHTMLTag_head) {
2751 mBodyContext->Pop();
2752 } else {
2753 // This else can happen because CloseContainer is called both directly
2754 // and from CloseContainersTo. CloseContainersTo pops the current tag
2755 // off of the stack before calling CloseContainer.
2756 NS_ASSERTION(mBodyContext->LastOf(eHTMLTag_head) == kNotFound,
2757 "Closing the wrong tag");
2759 done = PR_FALSE;
2761 break;
2763 case eHTMLTag_map:
2764 if (mOpenMapCount) {
2765 mOpenMapCount--;
2766 done = PR_FALSE;
2768 break;
2770 case eHTMLTag_form:
2771 if (mFlags & NS_DTD_FLAG_HAS_OPEN_FORM) {
2772 mFlags &= ~NS_DTD_FLAG_HAS_OPEN_FORM;
2773 done = PR_FALSE;
2774 // If we neglect to close these tags, the sink will refuse to close the
2775 // form because the form will not be on the top of the SinkContext stack.
2776 // See HTMLContentSink::CloseForm. (XXX do this in other cases?)
2777 CloseResidualStyleTags(eHTMLTag_form, PR_FALSE);
2779 break;
2781 case eHTMLTag_iframe:
2782 case eHTMLTag_noembed:
2783 case eHTMLTag_noscript:
2784 case eHTMLTag_noframes:
2785 // Switch from alternate content state to regular state.
2786 mFlags &= ~NS_DTD_FLAG_ALTERNATE_CONTENT;
2788 // falling thro' intentionally....
2789 default:
2790 done = PR_FALSE;
2793 if (!done) {
2794 STOP_TIMER();
2795 MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::CloseContainer(), this=%p\n", this));
2797 if (mSink) {
2798 result = !aMalformed
2799 ? mSink->CloseContainer(aTag)
2800 : mSink->CloseMalformedContainer(aTag);
2803 // If we were dealing with a head container in the body, make sure to
2804 // close the head context now, so that body content doesn't get sucked
2805 // into the head.
2806 if (mBodyContext->GetCount() == mHeadContainerPosition) {
2807 mHeadContainerPosition = -1;
2808 nsresult headresult = CloseContainer(eHTMLTag_head, PR_FALSE);
2810 // Note: we could be assigning NS_OK into NS_OK here, but that's ok.
2811 // This test is to avoid a successful CloseHead result stomping over a
2812 // request to block the parser.
2813 if (NS_SUCCEEDED(result)) {
2814 result = headresult;
2818 MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::CloseContainer(), this=%p\n", this));
2819 START_TIMER();
2822 return result;
2826 * This method does two things: 1st, help construct
2827 * our own internal model of the content-stack; and
2828 * 2nd, pass this message on to the sink.
2829 * @update gess4/6/98
2830 * @param anIndex
2831 * @param aTag
2832 * @param aClosedByStartTag -- if TRUE, then we're closing something because a start tag caused it
2833 * @return TRUE if ok, FALSE if error
2835 nsresult
2836 CNavDTD::CloseContainersTo(PRInt32 anIndex, eHTMLTags aTarget,
2837 PRBool aClosedByStartTag)
2839 NS_PRECONDITION(mBodyContext->GetCount() > 0, kInvalidTagStackPos);
2840 nsresult result = NS_OK;
2842 if (anIndex < mBodyContext->GetCount() && anIndex >= 0) {
2843 PRInt32 count = 0;
2844 while ((count = mBodyContext->GetCount()) > anIndex) {
2845 nsEntryStack* theChildStyleStack = 0;
2846 eHTMLTags theTag = mBodyContext->Last();
2847 nsCParserNode* theNode = mBodyContext->Pop(theChildStyleStack);
2848 result = CloseContainer(theTag, PR_FALSE);
2850 PRBool theTagIsStyle = nsHTMLElement::IsResidualStyleTag(theTag);
2851 // If the current tag cannot leak out then we shouldn't leak out of the target - Fix 40713
2852 PRBool theStyleDoesntLeakOut = gHTMLElements[theTag].HasSpecialProperty(kNoStyleLeaksOut);
2853 if (!theStyleDoesntLeakOut) {
2854 theStyleDoesntLeakOut = gHTMLElements[aTarget].HasSpecialProperty(kNoStyleLeaksOut);
2857 // Do not invoke residual style handling when dealing with
2858 // alternate content. This fixed bug 25214.
2859 if (theTagIsStyle && !(mFlags & NS_DTD_FLAG_ALTERNATE_CONTENT)) {
2860 NS_ASSERTION(theNode, "residual style node should not be null");
2861 if (!theNode) {
2862 if (theChildStyleStack) {
2863 mBodyContext->PushStyles(theChildStyleStack);
2865 return NS_OK;
2868 PRBool theTargetTagIsStyle = nsHTMLElement::IsResidualStyleTag(aTarget);
2869 if (aClosedByStartTag) {
2870 // Handle closure due to new start tag.
2871 // The cases we're handing here:
2872 // 1. <body><b><DIV> //<b> gets pushed onto <body>.mStyles.
2873 // 2. <body><a>text<a> //in this case, the target matches, so don't push style
2874 if (theNode->mUseCount == 0) {
2875 if (theTag != aTarget) {
2876 if (theChildStyleStack) {
2877 theChildStyleStack->PushFront(theNode);
2878 } else {
2879 mBodyContext->PushStyle(theNode);
2882 } else if (theTag == aTarget && !gHTMLElements[aTarget].CanContainSelf()) {
2883 //here's a case we missed: <a><div>text<a>text</a></div>
2884 //The <div> pushes the 1st <a> onto the rs-stack, then the 2nd <a>
2885 //pops the 1st <a> from the rs-stack altogether.
2886 nsCParserNode* node = mBodyContext->PopStyle(theTag);
2887 IF_FREE(node, &mNodeAllocator);
2890 if (theChildStyleStack) {
2891 mBodyContext->PushStyles(theChildStyleStack);
2893 } else {
2895 * if you're here, then we're dealing with the closure of tags
2896 * caused by a close tag (as opposed to an open tag).
2897 * At a minimum, we should consider pushing residual styles up
2898 * up the stack or popping and recycling displaced nodes.
2900 * Known cases:
2901 * 1. <body><b><div>text</DIV>
2902 * Here the <b> will leak into <div> (see case given above), and
2903 * when <div> closes the <b> is dropped since it's already residual.
2905 * 2. <body><div><b>text</div>
2906 * Here the <b> will leak out of the <div> and get pushed onto
2907 * the RS stack for the <body>, since it originated in the <div>.
2909 * 3. <body><span><b>text</span>
2910 * In this case, the the <b> get's pushed onto the style stack.
2911 * Later we deal with RS styles stored on the <span>
2913 * 4. <body><span><b>text</i>
2914 * Here we the <b>is closed by a (synonymous) style tag.
2915 * In this case, the <b> is simply closed.
2917 if (theChildStyleStack) {
2918 if (!theStyleDoesntLeakOut) {
2919 if (theTag != aTarget) {
2920 if (theNode->mUseCount == 0) {
2921 theChildStyleStack->PushFront(theNode);
2923 } else if (theNode->mUseCount == 1) {
2924 // This fixes bug 30885,29626.
2925 // Make sure that the node, which is about to
2926 // get released does not stay on the style stack...
2927 // Also be sure to remove the correct style off the
2928 // style stack. - Ref. bug 94208.
2929 // Ex <FONT><B><I></FONT><FONT></B></I></FONT>
2930 // Make sure that </B> removes B off the style stack.
2931 mBodyContext->RemoveStyle(theTag);
2933 mBodyContext->PushStyles(theChildStyleStack);
2934 } else{
2935 IF_DELETE(theChildStyleStack, &mNodeAllocator);
2937 } else if (theNode->mUseCount == 0) {
2938 // The old version of this only pushed if the targettag wasn't
2939 // style. But that misses this case: <font><b>text</font>, where
2940 // the b should leak.
2941 if (aTarget != theTag) {
2942 mBodyContext->PushStyle(theNode);
2944 } else {
2945 // Ah, at last, the final case. If you're here, then we just popped
2946 // a style tag that got onto that tag stack from a stylestack
2947 // somewhere. Pop it from the stylestack if the target is also a
2948 // style tag. Make sure to remove the matching style. In the
2949 // following example:
2950 // <FONT><B><I></FONT><FONT color=red></B></I></FONT>
2951 // make sure that </I> does not remove
2952 // <FONT color=red> off the style stack. - bug 94208
2953 if (theTargetTagIsStyle && theTag == aTarget) {
2954 mBodyContext->RemoveStyle(theTag);
2958 } else {
2959 // The tag is not a style tag.
2960 if (theChildStyleStack) {
2961 if (theStyleDoesntLeakOut) {
2962 IF_DELETE(theChildStyleStack, &mNodeAllocator);
2963 } else {
2964 mBodyContext->PushStyles(theChildStyleStack);
2968 IF_FREE(theNode, &mNodeAllocator);
2971 return result;
2975 * This method does two things: 1st, help construct
2976 * our own internal model of the content-stack; and
2977 * 2nd, pass this message on to the sink.
2978 * @update gess4/6/98
2979 * @param aTag --
2980 * @param aClosedByStartTag -- ONLY TRUE if the container is being closed by opening of another container.
2981 * @return TRUE if ok, FALSE if error
2983 nsresult
2984 CNavDTD::CloseContainersTo(eHTMLTags aTag, PRBool aClosedByStartTag)
2986 NS_PRECONDITION(mBodyContext->GetCount() > 0, kInvalidTagStackPos);
2988 PRInt32 pos = mBodyContext->LastOf(aTag);
2990 if (kNotFound != pos) {
2991 // The tag is indeed open, so close it.
2992 return CloseContainersTo(pos, aTag, aClosedByStartTag);
2995 eHTMLTags theTopTag = mBodyContext->Last();
2997 PRBool theTagIsSynonymous = (nsHTMLElement::IsResidualStyleTag(aTag) &&
2998 nsHTMLElement::IsResidualStyleTag(theTopTag)) ||
2999 (gHTMLElements[aTag].IsMemberOf(kHeading) &&
3000 gHTMLElements[theTopTag].IsMemberOf(kHeading));
3002 if (theTagIsSynonymous) {
3003 // If you're here, it's because we're trying to close one tag,
3004 // but a different (synonymous) one is actually open. Because this is NAV4x
3005 // compatibililty mode, we must close the one that's really open.
3006 aTag = theTopTag;
3007 pos = mBodyContext->LastOf(aTag);
3008 if (kNotFound != pos) {
3009 // The tag is indeed open, so close it.
3010 return CloseContainersTo(pos, aTag, aClosedByStartTag);
3014 nsresult result = NS_OK;
3015 const TagList* theRootTags = gHTMLElements[aTag].GetRootTags();
3016 // XXX Can we just bail if !theRootTags? Does that ever happen?
3017 eHTMLTags theParentTag = theRootTags ? theRootTags->mTags[0] : eHTMLTag_unknown;
3018 pos = mBodyContext->LastOf(theParentTag);
3019 if (kNotFound != pos) {
3020 // The parent container is open, so close it instead
3021 result = CloseContainersTo(pos + 1, aTag, aClosedByStartTag);
3023 return result;
3027 * This method does two things: 1st, help construct
3028 * our own internal model of the content-stack; and
3029 * 2nd, pass this message on to the sink.
3030 * @update gess4/6/98
3031 * @param aNode -- next node to be added to model
3032 * @return error code; 0 means OK
3034 nsresult
3035 CNavDTD::AddLeaf(const nsIParserNode *aNode)
3037 nsresult result = NS_OK;
3039 if (mSink) {
3040 eHTMLTags theTag = (eHTMLTags)aNode->GetNodeType();
3041 OpenTransientStyles(theTag);
3043 STOP_TIMER();
3044 MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::AddLeaf(), this=%p\n", this));
3046 result = mSink->AddLeaf(*aNode);
3048 MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::AddLeaf(), this=%p\n", this));
3049 START_TIMER();
3052 return result;
3056 * Call this method ONLY when you want to write a leaf
3057 * into the head container.
3059 * @update gess 03/14/99
3060 * @param aNode -- next node to be added to model
3061 * @return error code; 0 means OK
3063 nsresult
3064 CNavDTD::AddHeadContent(nsIParserNode *aNode)
3066 nsresult result = NS_OK;
3068 static eHTMLTags gNoXTags[] = { eHTMLTag_noembed, eHTMLTag_noframes };
3070 eHTMLTags theTag = (eHTMLTags)aNode->GetNodeType();
3072 // XXX - SCRIPT inside NOTAGS should not get executed unless the pref.
3073 // says so. Since we don't have this support yet..lets ignore the
3074 // SCRIPT inside NOTAGS. Ref Bug 25880.
3075 if (eHTMLTag_meta == theTag || eHTMLTag_script == theTag) {
3076 if (HasOpenContainer(gNoXTags, NS_ARRAY_LENGTH(gNoXTags))) {
3077 return result;
3081 if (mSink) {
3082 STOP_TIMER();
3083 MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::AddHeadContent(), this=%p\n", this));
3085 // Make sure the head is opened.
3086 if (!(mFlags & NS_DTD_FLAG_HAS_OPEN_HEAD)) {
3087 mFlags |= NS_DTD_FLAG_HAS_OPEN_HEAD;
3088 mBodyContext->PushTag(eHTMLTag_head);
3089 result = mSink->OpenHead();
3092 // Note: userdefined tags in the head are treated as leaves.
3093 if (!nsHTMLElement::IsContainer(theTag) || theTag == eHTMLTag_userdefined) {
3094 result = mSink->AddLeaf(*aNode);
3096 if (mFlags & NS_DTD_FLAG_HAS_MAIN_CONTAINER) {
3097 // Close the head now so that body content doesn't get sucked into it.
3098 CloseContainer(eHTMLTag_head, PR_FALSE);
3100 } else {
3101 if ((mFlags & NS_DTD_FLAG_HAS_MAIN_CONTAINER) &&
3102 mHeadContainerPosition == -1) {
3103 // Keep track of this so that we know when to close the head, when
3104 // this tag is done with.
3105 mHeadContainerPosition = mBodyContext->GetCount();
3108 mBodyContext->Push(static_cast<nsCParserNode*>(aNode), nsnull,
3109 PR_FALSE);
3111 // Note: The head context is already opened.
3112 result = mSink->OpenContainer(*aNode);
3115 MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::AddHeadContent(), this=%p\n", this));
3116 START_TIMER();
3119 return result;
3122 void
3123 CNavDTD::CreateContextStackFor(eHTMLTags aParent, eHTMLTags aChild)
3125 mScratch.Truncate();
3127 PRBool result = ForwardPropagate(mScratch, aParent, aChild);
3129 if (!result) {
3130 if (eHTMLTag_unknown == aParent) {
3131 result = BackwardPropagate(mScratch, eHTMLTag_html, aChild);
3132 } else if (aParent != aChild) {
3133 // Don't even bother if we're already inside a similar element...
3134 result = BackwardPropagate(mScratch, aParent, aChild);
3138 if (!result) {
3139 return;
3142 PRInt32 theLen = mScratch.Length();
3143 eHTMLTags theTag = (eHTMLTags)mScratch[--theLen];
3145 // Now, build up the stack according to the tags.
3146 while (theLen) {
3147 theTag = (eHTMLTags)mScratch[--theLen];
3149 // Note: These tokens should all wind up on contextstack, so don't recycle
3150 // them.
3151 CToken *theToken = mTokenAllocator->CreateTokenOfType(eToken_start, theTag);
3152 HandleToken(theToken, mParser);
3156 nsresult
3157 CNavDTD::WillResumeParse(nsIContentSink* aSink)
3159 STOP_TIMER();
3160 MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::WillResumeParse(), this=%p\n", this));
3162 nsresult result = aSink ? aSink->WillResume() : NS_OK;
3164 MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::WillResumeParse(), this=%p\n", this));
3165 START_TIMER();
3167 return result;
3170 nsresult
3171 CNavDTD::WillInterruptParse(nsIContentSink* aSink)
3173 STOP_TIMER();
3174 MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::WillInterruptParse(), this=%p\n", this));
3176 nsresult result = aSink ? aSink->WillInterrupt() : NS_OK;
3178 MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::WillInterruptParse(), this=%p\n", this));
3179 START_TIMER();
3181 return result;