Bug 436663. Work around ATSUI crasher caused by long Hebrew sequence. r=roc, sr=vlad
[wine-gecko.git] / parser / htmlparser / src / CNavDTD.cpp
blob65505b3bb5d7cbd495cb4f19897bc8cba9c71c4d
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 //#define ALLOW_TR_AS_CHILD_OF_TABLE //by setting this to true, TR is allowable directly in TABLE.
41 #include "nsDebug.h"
42 #include "nsIAtom.h"
43 #include "CNavDTD.h"
44 #include "nsHTMLTokens.h"
45 #include "nsCRT.h"
46 #include "nsParser.h"
47 #include "nsIParser.h"
48 #include "nsIHTMLContentSink.h"
49 #include "nsScanner.h"
50 #include "prenv.h"
51 #include "prtypes.h"
52 #include "prio.h"
53 #include "plstr.h"
54 #include "nsDTDUtils.h"
55 #include "nsHTMLTokenizer.h"
56 #include "nsTime.h"
57 #include "nsParserNode.h"
58 #include "nsHTMLEntities.h"
59 #include "nsLinebreakConverter.h"
60 #include "nsIFormProcessor.h"
61 #include "nsVoidArray.h"
62 #include "nsReadableUtils.h"
63 #include "nsUnicharUtils.h"
64 #include "prmem.h"
65 #include "nsIServiceManager.h"
67 #ifdef NS_DEBUG
68 #include "nsLoggingSink.h"
69 #endif
72 * Ignore kFontStyle and kPhrase tags when the stack is deep, bug 58917.
74 #define FONTSTYLE_IGNORE_DEPTH (MAX_REFLOW_DEPTH * 80 / 100)
75 #define PHRASE_IGNORE_DEPTH (MAX_REFLOW_DEPTH * 90 / 100)
77 static NS_DEFINE_CID(kFormProcessorCID, NS_FORMPROCESSOR_CID);
79 #ifdef DEBUG
80 static const char kNullToken[] = "Error: Null token given";
81 static const char kInvalidTagStackPos[] = "Error: invalid tag stack position";
82 #endif
84 #include "nsElementTable.h"
86 #ifdef MOZ_PERF_METRICS
87 # define START_TIMER() \
88 if (mParser) MOZ_TIMER_START(mParser->mParseTime); \
89 if (mParser) MOZ_TIMER_START(mParser->mDTDTime);
91 # define STOP_TIMER() \
92 if (mParser) MOZ_TIMER_STOP(mParser->mParseTime); \
93 if (mParser) MOZ_TIMER_STOP(mParser->mDTDTime);
94 #else
95 # define STOP_TIMER()
96 # define START_TIMER()
97 #endif
99 // Some flags for use by the DTD.
100 #define NS_DTD_FLAG_NONE 0x00000000
101 #define NS_DTD_FLAG_HAS_OPEN_HEAD 0x00000001
102 #define NS_DTD_FLAG_HAS_OPEN_BODY 0x00000002
103 #define NS_DTD_FLAG_HAS_OPEN_FORM 0x00000004
104 #define NS_DTD_FLAG_HAS_EXPLICIT_HEAD 0x00000008
105 #define NS_DTD_FLAG_HAD_BODY 0x00000010
106 #define NS_DTD_FLAG_HAD_FRAMESET 0x00000020
107 #define NS_DTD_FLAG_ENABLE_RESIDUAL_STYLE 0x00000040
108 #define NS_DTD_FLAG_ALTERNATE_CONTENT 0x00000080 // NOFRAMES, NOSCRIPT
109 #define NS_DTD_FLAG_MISPLACED_CONTENT 0x00000100
110 #define NS_DTD_FLAG_IN_MISPLACED_CONTENT 0x00000200
111 #define NS_DTD_FLAG_STOP_PARSING 0x00000400
113 #define NS_DTD_FLAG_HAS_MAIN_CONTAINER (NS_DTD_FLAG_HAD_BODY | \
114 NS_DTD_FLAG_HAD_FRAMESET)
116 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CNavDTD)
117 NS_INTERFACE_MAP_ENTRY(nsIDTD)
118 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDTD)
119 NS_INTERFACE_MAP_END
121 NS_IMPL_CYCLE_COLLECTING_ADDREF(CNavDTD)
122 NS_IMPL_CYCLE_COLLECTING_RELEASE(CNavDTD)
124 NS_IMPL_CYCLE_COLLECTION_1(CNavDTD, mSink)
126 CNavDTD::CNavDTD()
127 : mMisplacedContent(0),
128 mTokenAllocator(0),
129 mBodyContext(new nsDTDContext()),
130 mTempContext(0),
131 mParser(0),
132 mTokenizer(0),
133 mDTDMode(eDTDMode_quirks),
134 mDocType(eHTML3_Quirks), // why not eHTML_Quirks?
135 mParserCommand(eViewNormal),
136 mLineNumber(1),
137 mOpenMapCount(0),
138 mHeadContainerPosition(-1),
139 mFlags(NS_DTD_FLAG_NONE)
143 #ifdef NS_DEBUG
145 static nsLoggingSink*
146 GetLoggingSink()
148 // This returns a content sink that is useful for following what calls the DTD
149 // makes to the content sink.
151 static PRBool checkForPath = PR_TRUE;
152 static nsLoggingSink *theSink = nsnull;
153 static const char* gLogPath = nsnull;
155 if (checkForPath) {
156 // Only check once per run.
157 gLogPath = PR_GetEnv("PARSE_LOGFILE");
158 checkForPath = PR_FALSE;
162 if (gLogPath && !theSink) {
163 static nsLoggingSink gLoggingSink;
165 PRIntn theFlags = PR_CREATE_FILE | PR_RDWR;
167 // Open the record file.
168 PRFileDesc *theLogFile = PR_Open(gLogPath, theFlags, 0);
169 gLoggingSink.SetOutputStream(theLogFile, PR_TRUE);
170 theSink = &gLoggingSink;
173 return theSink;
176 #endif
178 CNavDTD::~CNavDTD()
180 delete mBodyContext;
181 delete mTempContext;
183 #ifdef NS_DEBUG
184 if (mSink) {
185 nsLoggingSink *theLogSink = GetLoggingSink();
186 if (mSink == theLogSink) {
187 theLogSink->ReleaseProxySink();
190 #endif
193 nsresult
194 CNavDTD::WillBuildModel(const CParserContext& aParserContext,
195 nsITokenizer* aTokenizer,
196 nsIContentSink* aSink)
198 nsresult result = NS_OK;
200 mFilename = aParserContext.mScanner->GetFilename();
201 mFlags = NS_DTD_FLAG_ENABLE_RESIDUAL_STYLE;
202 mLineNumber = 1;
203 mDTDMode = aParserContext.mDTDMode;
204 mParserCommand = aParserContext.mParserCommand;
205 mMimeType = aParserContext.mMimeType;
206 mDocType = aParserContext.mDocType;
207 mTokenizer = aTokenizer;
208 mBodyContext->SetNodeAllocator(&mNodeAllocator);
210 if (!aParserContext.mPrevContext && aSink) {
211 STOP_TIMER();
212 MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::WillBuildModel(), this=%p\n", this));
214 result = aSink->WillBuildModel();
216 MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::WillBuildModel(), this=%p\n", this));
217 START_TIMER();
219 if (NS_SUCCEEDED(result) && !mSink) {
220 mSink = do_QueryInterface(aSink, &result);
221 if (NS_FAILED(result)) {
222 mFlags |= NS_DTD_FLAG_STOP_PARSING;
223 return result;
227 // Let's see if the environment is set up for us to write output to
228 // a logging sink. If so, then we'll create one, and make it the
229 // proxy for the real sink we're given from the parser.
230 #ifdef NS_DEBUG
231 nsLoggingSink *theLogSink = GetLoggingSink();
232 if (theLogSink) {
233 theLogSink->SetProxySink(mSink);
234 mSink = theLogSink;
236 #endif
238 if (mSink) {
239 PRBool enabled = PR_TRUE;
240 mSink->IsEnabled(eHTMLTag_frameset, &enabled);
241 if (enabled) {
242 mFlags |= NS_IPARSER_FLAG_FRAMES_ENABLED;
245 mSink->IsEnabled(eHTMLTag_script, &enabled);
246 if (enabled) {
247 mFlags |= NS_IPARSER_FLAG_SCRIPT_ENABLED;
252 return result;
255 nsresult
256 CNavDTD::BuildModel(nsIParser* aParser,
257 nsITokenizer* aTokenizer,
258 nsITokenObserver* anObserver,
259 nsIContentSink* aSink)
261 NS_PRECONDITION(mBodyContext != nsnull,
262 "Create a context before calling build model");
264 nsresult result = NS_OK;
266 if (!aTokenizer || !aParser) {
267 return NS_OK;
270 nsITokenizer* oldTokenizer = mTokenizer;
272 mTokenizer = aTokenizer;
273 mParser = (nsParser*)aParser;
274 mTokenAllocator = mTokenizer->GetTokenAllocator();
276 if (!mSink) {
277 return (mFlags & NS_DTD_FLAG_STOP_PARSING)
278 ? NS_ERROR_HTMLPARSER_STOPPARSING
279 : result;
282 if (mBodyContext->GetCount() == 0) {
283 CToken* tempToken;
284 if (ePlainText == mDocType) {
285 tempToken =
286 mTokenAllocator->CreateTokenOfType(eToken_start, eHTMLTag_pre);
287 if (tempToken) {
288 mTokenizer->PushTokenFront(tempToken);
292 // Always open a body if frames are disabled.
293 if (!(mFlags & NS_IPARSER_FLAG_FRAMES_ENABLED)) {
294 tempToken =
295 mTokenAllocator->CreateTokenOfType(eToken_start,
296 eHTMLTag_body,
297 NS_LITERAL_STRING("body"));
298 if (tempToken) {
299 mTokenizer->PushTokenFront(tempToken);
303 // If the content model is empty, then begin by opening <html>.
304 CStartToken* theToken = (CStartToken*)mTokenizer->GetTokenAt(0);
305 if (theToken) {
306 eHTMLTags theTag = (eHTMLTags)theToken->GetTypeID();
307 eHTMLTokenTypes theType = eHTMLTokenTypes(theToken->GetTokenType());
308 if (theTag != eHTMLTag_html || theType != eToken_start) {
309 tempToken =
310 mTokenAllocator->CreateTokenOfType(eToken_start,
311 eHTMLTag_html,
312 NS_LITERAL_STRING("html"));
313 if (tempToken) {
314 mTokenizer->PushTokenFront(tempToken);
317 } else {
318 tempToken =
319 mTokenAllocator->CreateTokenOfType(eToken_start,
320 eHTMLTag_html,
321 NS_LITERAL_STRING("html"));
322 if (tempToken) {
323 mTokenizer->PushTokenFront(tempToken);
328 mSink->WillProcessTokens();
330 while (NS_SUCCEEDED(result)) {
331 if (!(mFlags & NS_DTD_FLAG_STOP_PARSING)) {
332 CToken* theToken = mTokenizer->PopToken();
333 if (!theToken) {
334 break;
336 result = HandleToken(theToken, aParser);
337 } else {
338 result = NS_ERROR_HTMLPARSER_STOPPARSING;
339 break;
342 if (NS_ERROR_HTMLPARSER_INTERRUPTED == mSink->DidProcessAToken()) {
343 // The content sink has requested that DTD interrupt processing tokens
344 // So we need to make sure the parser is in a state where it can be
345 // interrupted.
346 // The mParser->CanInterrupt will return TRUE if BuildModel was called
347 // from a place in the parser where it prepared to handle a return value of
348 // NS_ERROR_HTMLPARSER_INTERRUPTED.
349 // If the parser is processing a script's document.write we should not
350 // allow it to be interrupted.
351 // We also need to make sure that an interruption does not override
352 // a request to block the parser.
353 if (mParser->CanInterrupt() &&
354 !IsParserInDocWrite() &&
355 NS_SUCCEEDED(result)) {
356 result = NS_ERROR_HTMLPARSER_INTERRUPTED;
357 break;
362 mTokenizer = oldTokenizer;
363 return result;
366 nsresult
367 CNavDTD::BuildNeglectedTarget(eHTMLTags aTarget,
368 eHTMLTokenTypes aType,
369 nsIParser* aParser,
370 nsIContentSink* aSink)
372 NS_ASSERTION(mTokenizer, "tokenizer is null! unable to build target.");
373 NS_ASSERTION(mTokenAllocator, "unable to create tokens without an allocator.");
374 if (!mTokenizer || !mTokenAllocator) {
375 return NS_OK;
378 CToken* target = mTokenAllocator->CreateTokenOfType(aType, aTarget);
379 NS_ENSURE_TRUE(target, NS_ERROR_OUT_OF_MEMORY);
380 mTokenizer->PushTokenFront(target);
381 return BuildModel(aParser, mTokenizer, 0, aSink);
384 nsresult
385 CNavDTD::DidBuildModel(nsresult anErrorCode,
386 PRBool aNotifySink,
387 nsIParser* aParser,
388 nsIContentSink* aSink)
390 if (!aSink) {
391 return NS_OK;
394 nsresult result = NS_OK;
395 if (aParser && aNotifySink) {
396 if (NS_OK == anErrorCode) {
397 if (!(mFlags & NS_DTD_FLAG_HAS_MAIN_CONTAINER)) {
398 // This document is not a frameset document, however, it did not contain
399 // a body tag either. So, make one!. Note: Body tag is optional per spec..
400 // Also note: We ignore the return value of BuildNeglectedTarget, we
401 // can't reasonably respond to errors (or requests to block) at this
402 // point in the parsing process.
403 BuildNeglectedTarget(eHTMLTag_body, eToken_start, aParser, aSink);
405 if (mFlags & NS_DTD_FLAG_MISPLACED_CONTENT) {
406 // Looks like the misplaced contents are not processed yet.
407 // Here is our last chance to handle the misplaced content.
409 // Keep track of the top index.
410 PRInt32 topIndex = mBodyContext->mContextTopIndex;
412 // Loop until we've really consumed all of our misplaced content.
413 do {
414 mFlags &= ~NS_DTD_FLAG_MISPLACED_CONTENT;
416 // mContextTopIndex refers to the misplaced content's legal parent index.
417 result = HandleSavedTokens(mBodyContext->mContextTopIndex);
418 if (NS_FAILED(result)) {
419 NS_ERROR("Bug in the DTD");
420 break;
423 // If we start handling misplaced content while handling misplaced
424 // content, mContextTopIndex gets modified. However, this new index
425 // necessarily points to the middle of a closed tag (since we close
426 // new tags after handling the misplaced content). So we restore the
427 // insertion point after every iteration.
428 mBodyContext->mContextTopIndex = topIndex;
429 } while (mFlags & NS_DTD_FLAG_MISPLACED_CONTENT);
431 mBodyContext->mContextTopIndex = -1;
434 // Now let's disable style handling to save time when closing remaining
435 // stack members.
436 mFlags &= ~NS_DTD_FLAG_ENABLE_RESIDUAL_STYLE;
437 while (mBodyContext->GetCount() > 0) {
438 result = CloseContainersTo(mBodyContext->Last(), PR_FALSE);
439 if (NS_FAILED(result)) {
440 //No matter what, you need to call did build model.
441 aSink->DidBuildModel();
442 return result;
445 } else {
446 // If you're here, then an error occured, but we still have nodes on the stack.
447 // At a minimum, we should grab the nodes and recycle them.
448 // Just to be correct, we'll also recycle the nodes.
449 while (mBodyContext->GetCount() > 0) {
450 nsEntryStack* theChildStyles = 0;
451 nsCParserNode* theNode = mBodyContext->Pop(theChildStyles);
452 IF_DELETE(theChildStyles, &mNodeAllocator);
453 IF_FREE(theNode, &mNodeAllocator);
457 // Now make sure the misplaced content list is empty,
458 // by forcefully recycling any tokens we might find there.
459 CToken* theToken = 0;
460 while ((theToken = (CToken*)mMisplacedContent.Pop())) {
461 IF_FREE(theToken, mTokenAllocator);
465 // No matter what, you need to call did build model.
466 return aSink->DidBuildModel();
469 NS_IMETHODIMP_(void)
470 CNavDTD::Terminate()
472 mFlags |= NS_DTD_FLAG_STOP_PARSING;
476 NS_IMETHODIMP_(PRInt32)
477 CNavDTD::GetType()
479 return NS_IPARSER_FLAG_HTML;
483 * Text and some tags require a body when they're added, this function returns
484 * true for those tags.
486 * @param aToken The current token that we care about.
487 * @param aTokenizer A tokenizer that we can get the tags attributes off of.
488 * @return PR_TRUE if aToken does indeed force the body to open.
490 static PRBool
491 DoesRequireBody(CToken* aToken, nsITokenizer* aTokenizer)
493 PRBool result = PR_FALSE;
495 if (aToken) {
496 eHTMLTags theTag = (eHTMLTags)aToken->GetTypeID();
497 if (gHTMLElements[theTag].HasSpecialProperty(kRequiresBody)) {
498 if (theTag == eHTMLTag_input) {
499 // IE & Nav4x opens up a body for type=text - Bug 66985
500 // XXXbz but we don't want to open one for <input> with no
501 // type attribute? That's pretty whack.
502 PRInt32 ac = aToken->GetAttributeCount();
503 for(PRInt32 i = 0; i < ac; ++i) {
504 CAttributeToken* attr = static_cast<CAttributeToken*>
505 (aTokenizer->GetTokenAt(i));
506 const nsSubstring& name = attr->GetKey();
507 const nsAString& value = attr->GetValue();
508 // XXXbz note that this stupid case-sensitive comparison is
509 // actually depended on by sites...
510 if ((name.EqualsLiteral("type") ||
511 name.EqualsLiteral("TYPE"))
513 !(value.EqualsLiteral("hidden") ||
514 value.EqualsLiteral("HIDDEN"))) {
515 result = PR_TRUE;
516 break;
519 } else {
520 result = PR_TRUE;
525 return result;
528 static PRBool
529 ValueIsHidden(const nsAString& aValue)
531 // Having to deal with whitespace here sucks, but we have to match
532 // what the content sink does.
533 nsAutoString str(aValue);
534 str.Trim("\n\r\t\b");
535 return str.LowerCaseEqualsLiteral("hidden");
538 // Check whether aToken corresponds to a <input type="hidden"> tag. The token
539 // must be a start tag token for an <input>. This must be called at a point
540 // when all the attributes for the input are still in the tokenizer.
541 static PRBool
542 IsHiddenInput(CToken* aToken, nsITokenizer* aTokenizer)
544 NS_PRECONDITION(eHTMLTokenTypes(aToken->GetTokenType()) == eToken_start,
545 "Must be start token");
546 NS_PRECONDITION(eHTMLTags(aToken->GetTypeID()) == eHTMLTag_input,
547 "Must be <input> tag");
549 PRInt32 ac = aToken->GetAttributeCount();
550 NS_ASSERTION(ac <= aTokenizer->GetCount(),
551 "Not enough tokens in the tokenizer");
552 // But we don't really trust ourselves to get that right
553 ac = PR_MIN(ac, aTokenizer->GetCount());
555 for (PRInt32 i = 0; i < ac; ++i) {
556 NS_ASSERTION(eHTMLTokenTypes(aTokenizer->GetTokenAt(i)->GetTokenType()) ==
557 eToken_attribute, "Unexpected token type");
558 // Again, we're not sure we actually manage to guarantee that
559 if (eHTMLTokenTypes(aTokenizer->GetTokenAt(i)->GetTokenType()) !=
560 eToken_attribute) {
561 break;
564 CAttributeToken* attrToken =
565 static_cast<CAttributeToken*>(aTokenizer->GetTokenAt(i));
566 if (!attrToken->GetKey().LowerCaseEqualsLiteral("type")) {
567 continue;
570 return ValueIsHidden(attrToken->GetValue());
573 return PR_FALSE;
577 * Returns whether or not there is a tag of type aType open on aContext.
579 static PRBool
580 HasOpenTagOfType(PRInt32 aType, const nsDTDContext& aContext)
582 PRInt32 count = aContext.GetCount();
584 while (--count >= 0) {
585 if (gHTMLElements[aContext.TagAt(count)].IsMemberOf(aType)) {
586 return PR_TRUE;
590 return PR_FALSE;
593 nsresult
594 CNavDTD::HandleToken(CToken* aToken, nsIParser* aParser)
596 if (!aToken) {
597 return NS_OK;
600 nsresult result = NS_OK;
601 CHTMLToken* theToken = static_cast<CHTMLToken*>(aToken);
602 eHTMLTokenTypes theType = eHTMLTokenTypes(theToken->GetTokenType());
603 eHTMLTags theTag = (eHTMLTags)theToken->GetTypeID();
605 aToken->SetLineNumber(mLineNumber);
607 if (!IsParserInDocWrite()) {
608 mLineNumber += aToken->GetNewlineCount();
611 if (mFlags & NS_DTD_FLAG_MISPLACED_CONTENT) {
612 // Included TD & TH to fix Bug# 20797
613 static eHTMLTags gLegalElements[] = {
614 eHTMLTag_table, eHTMLTag_thead, eHTMLTag_tbody,
615 eHTMLTag_tr, eHTMLTag_td, eHTMLTag_th, eHTMLTag_tfoot
617 // Don't even try processing misplaced tokens if we're already
618 // handling misplaced content. See bug 269095
619 if (mFlags & NS_DTD_FLAG_IN_MISPLACED_CONTENT) {
620 PushIntoMisplacedStack(theToken);
621 return NS_OK;
624 eHTMLTags theParentTag = mBodyContext->Last();
625 if (FindTagInSet(theTag, gLegalElements,
626 NS_ARRAY_LENGTH(gLegalElements)) ||
627 (gHTMLElements[theParentTag].CanContain(theTag, mDTDMode) &&
628 // Here's a problem. If theTag is legal in here, we don't move it
629 // out. So if we're moving stuff out of here, the parent of theTag
630 // gets closed at this point. But some things are legal
631 // _everywhere_ and hence would effectively close out misplaced
632 // content in tables. This is undesirable, so treat them as
633 // illegal here so they'll be shipped out with their parents and
634 // siblings. See bug 40855 for an explanation (that bug was for
635 // comments, but the same issues arise with whitespace, newlines,
636 // noscript, etc). Script is special, though. Shipping it out
637 // breaks document.write stuff. See bug 243064.
638 (!gHTMLElements[theTag].HasSpecialProperty(kLegalOpen) ||
639 theTag == eHTMLTag_script)) ||
640 (theTag == eHTMLTag_input && theType == eToken_start &&
641 FindTagInSet(theParentTag, gLegalElements,
642 NS_ARRAY_LENGTH(gLegalElements)) &&
643 IsHiddenInput(theToken, mTokenizer))) {
644 // Reset the state since all the misplaced tokens are about to get
645 // handled.
646 mFlags &= ~NS_DTD_FLAG_MISPLACED_CONTENT;
648 result = HandleSavedTokens(mBodyContext->mContextTopIndex);
649 NS_ENSURE_SUCCESS(result, result);
651 mBodyContext->mContextTopIndex = -1;
652 } else {
653 PushIntoMisplacedStack(theToken);
654 return result;
659 * This section of code is used to "move" misplaced content from one location
660 * in our document model to another. (Consider what would happen if we found a
661 * <P> tag in the head.) To move content, we throw it onto the
662 * misplacedcontent deque until we can deal with it.
664 switch(theTag) {
665 case eHTMLTag_html:
666 case eHTMLTag_noframes:
667 case eHTMLTag_script:
668 case eHTMLTag_doctypeDecl:
669 case eHTMLTag_instruction:
670 break;
672 default:
673 if (!gHTMLElements[eHTMLTag_html].SectionContains(theTag, PR_FALSE)) {
674 if (!(mFlags & (NS_DTD_FLAG_HAS_MAIN_CONTAINER |
675 NS_DTD_FLAG_ALTERNATE_CONTENT))) {
676 // For bug examples from this code, see bugs: 18928, 20989.
677 // At this point we know the body/frameset aren't open.
678 // If the child belongs in the head, then handle it (which may open
679 // the head); otherwise, push it onto the misplaced stack.
681 PRBool isExclusive = PR_FALSE;
682 PRBool theChildBelongsInHead =
683 gHTMLElements[eHTMLTag_head].IsChildOfHead(theTag, isExclusive);
684 if (theChildBelongsInHead &&
685 !isExclusive &&
686 !gHTMLElements[theTag].HasSpecialProperty(kPreferHead)) {
687 if (mMisplacedContent.GetSize() == 0 &&
688 (!gHTMLElements[theTag].HasSpecialProperty(kPreferBody) ||
689 (mFlags & NS_DTD_FLAG_HAS_EXPLICIT_HEAD))) {
690 // This tag can either be in the body or the head. Since
691 // there is no indication that the body should be open,
692 // put this token in the head.
693 break;
696 // Otherwise, we have received some indication that the body is
697 // "open", so push this token onto the misplaced content stack.
698 theChildBelongsInHead = PR_FALSE;
701 if (!theChildBelongsInHead) {
702 eHTMLTags top = mBodyContext->Last();
703 NS_ASSERTION(top != eHTMLTag_userdefined,
704 "Userdefined tags should act as leaves in the head");
705 if (top != eHTMLTag_html && top != eHTMLTag_head &&
706 gHTMLElements[top].CanContain(theTag, mDTDMode)) {
707 // Some tags (such as <object> and <script>) are opened in the
708 // head and allow other non-head content to be children.
709 // Note: Userdefined tags in the head act like leaves.
710 break;
713 // If you're here then we found a child of the body that was out of
714 // place. We're going to move it to the body by storing it
715 // temporarily on the misplaced stack. However, in quirks mode, a
716 // few tags request, ambiguosly, for a BODY. - Bugs 18928, 24204.-
717 PushIntoMisplacedStack(aToken);
719 if (IsAlternateTag(theTag)) {
720 // These tags' contents are consumed as CDATA. If we simply
721 // pushed them on the misplaced content stack, the CDATA
722 // contents would force us to open a body, which could be
723 // wrong. So we collect the whole tag as misplaced in one
724 // gulp. Note that the tokenizer guarantees that there will
725 // be an end tag.
726 CToken *current = aToken;
727 while (current->GetTokenType() != eToken_end ||
728 current->GetTypeID() != theTag) {
729 current = static_cast<CToken *>(mTokenizer->PopToken());
730 NS_ASSERTION(current, "The tokenizer is not creating good "
731 "alternate tags");
732 PushIntoMisplacedStack(current);
735 // XXX Add code to also collect incorrect attributes on the
736 // end tag.
739 if (DoesRequireBody(aToken, mTokenizer)) {
740 CToken* theBodyToken =
741 mTokenAllocator->CreateTokenOfType(eToken_start,
742 eHTMLTag_body,
743 NS_LITERAL_STRING("body"));
744 result = HandleToken(theBodyToken, aParser);
746 return result;
752 if (theToken) {
753 mParser = (nsParser*)aParser;
755 switch (theType) {
756 case eToken_text:
757 case eToken_start:
758 case eToken_whitespace:
759 case eToken_newline:
760 result = HandleStartToken(theToken);
761 break;
763 case eToken_end:
764 result = HandleEndToken(theToken);
765 break;
767 case eToken_cdatasection:
768 case eToken_comment:
769 case eToken_markupDecl:
770 result = HandleCommentToken(theToken);
771 break;
773 case eToken_entity:
774 result = HandleEntityToken(theToken);
775 break;
777 case eToken_attribute:
778 result = HandleAttributeToken(theToken);
779 break;
781 case eToken_instruction:
782 result = HandleProcessingInstructionToken(theToken);
783 break;
785 case eToken_doctypeDecl:
786 result = HandleDocTypeDeclToken(theToken);
787 break;
789 default:
790 break;
793 IF_FREE(theToken, mTokenAllocator);
794 if (result == NS_ERROR_HTMLPARSER_STOPPARSING) {
795 mFlags |= NS_DTD_FLAG_STOP_PARSING;
796 } else if (NS_FAILED(result) && result != NS_ERROR_HTMLPARSER_BLOCK) {
797 result = NS_OK;
801 return result;
804 nsresult
805 CNavDTD::DidHandleStartTag(nsIParserNode& aNode, eHTMLTags aChildTag)
807 nsresult result = NS_OK;
809 switch (aChildTag) {
810 case eHTMLTag_pre:
811 case eHTMLTag_listing:
813 // Skip the 1st newline inside PRE and LISTING unless this is a
814 // plain text doc (for which we pushed a PRE in CNavDTD::BuildModel).
816 // XXX This code is incorrect in the face of misplaced <pre> and
817 // <listing> tags (as direct children of <table>).
818 CToken* theNextToken = mTokenizer->PeekToken();
819 if (ePlainText != mDocType && theNextToken) {
820 eHTMLTokenTypes theType = eHTMLTokenTypes(theNextToken->GetTokenType());
821 if (eToken_newline == theType) {
822 if (!IsParserInDocWrite()) {
823 mLineNumber += theNextToken->GetNewlineCount();
825 theNextToken = mTokenizer->PopToken();
826 IF_FREE(theNextToken, mTokenAllocator); // fix for Bug 29379
830 break;
832 default:
833 break;
836 return result;
839 PRInt32
840 CNavDTD::LastOf(eHTMLTags aTagSet[], PRInt32 aCount) const
842 for (PRInt32 theIndex = mBodyContext->GetCount() - 1; theIndex >= 0;
843 --theIndex) {
844 if (FindTagInSet((*mBodyContext)[theIndex], aTagSet, aCount)) {
845 return theIndex;
849 return kNotFound;
852 static PRBool
853 CanBeContained(eHTMLTags aChildTag, nsDTDContext& aContext)
855 /* # Interesting test cases: Result:
856 * 1. <UL><LI>..<B>..<LI> inner <LI> closes outer <LI>
857 * 2. <CENTER><DL><DT><A><CENTER> allow nested <CENTER>
858 * 3. <TABLE><TR><TD><TABLE>... allow nested <TABLE>
859 * 4. <FRAMESET> ... <FRAMESET>
861 PRBool result = PR_TRUE;
862 PRInt32 theCount = aContext.GetCount();
864 if (0 < theCount) {
865 const TagList* theRootTags = gHTMLElements[aChildTag].GetRootTags();
866 const TagList* theSpecialParents =
867 gHTMLElements[aChildTag].GetSpecialParents();
869 if (theRootTags) {
870 PRInt32 theRootIndex = LastOf(aContext, *theRootTags);
871 PRInt32 theSPIndex = theSpecialParents
872 ? LastOf(aContext, *theSpecialParents)
873 : kNotFound;
874 PRInt32 theChildIndex =
875 nsHTMLElement::GetIndexOfChildOrSynonym(aContext, aChildTag);
876 PRInt32 theTargetIndex = (theRootIndex > theSPIndex)
877 ? theRootIndex
878 : theSPIndex;
880 if (theTargetIndex == theCount-1 ||
881 (theTargetIndex == theChildIndex &&
882 gHTMLElements[aChildTag].CanContainSelf())) {
883 result = PR_TRUE;
884 } else {
885 result = PR_FALSE;
887 static eHTMLTags gTableElements[] = { eHTMLTag_td, eHTMLTag_th };
889 PRInt32 theIndex = theCount - 1;
890 while (theChildIndex < theIndex) {
891 eHTMLTags theParentTag = aContext.TagAt(theIndex--);
892 if (gHTMLElements[theParentTag].IsMemberOf(kBlockEntity) ||
893 gHTMLElements[theParentTag].IsMemberOf(kHeading) ||
894 gHTMLElements[theParentTag].IsMemberOf(kPreformatted) ||
895 gHTMLElements[theParentTag].IsMemberOf(kFormControl) || //fixes bug 44479
896 gHTMLElements[theParentTag].IsMemberOf(kList)) {
897 if (!HasOptionalEndTag(theParentTag)) {
898 result = PR_TRUE;
899 break;
901 } else if (FindTagInSet(theParentTag, gTableElements,
902 NS_ARRAY_LENGTH(gTableElements))) {
903 // Added this to catch a case we missed; bug 57173.
904 result = PR_TRUE;
905 break;
912 return result;
915 enum eProcessRule { eNormalOp, eLetInlineContainBlock };
917 nsresult
918 CNavDTD::HandleDefaultStartToken(CToken* aToken, eHTMLTags aChildTag,
919 nsCParserNode *aNode)
921 NS_PRECONDITION(nsnull != aToken, kNullToken);
923 nsresult result = NS_OK;
924 PRBool theChildIsContainer = nsHTMLElement::IsContainer(aChildTag);
926 // Client of parser is spefically trying to parse a fragment that
927 // may lack required context. Suspend containment rules if so.
928 if (mParserCommand != eViewFragment) {
929 PRBool theChildAgrees = PR_TRUE;
930 PRInt32 theIndex = mBodyContext->GetCount();
931 PRBool theParentContains = PR_FALSE;
933 do {
934 eHTMLTags theParentTag = mBodyContext->TagAt(--theIndex);
935 if (theParentTag == eHTMLTag_userdefined) {
936 continue;
939 // Figure out whether this is a hidden input inside a
940 // table/tbody/thead/tfoot/tr
941 static eHTMLTags sTableElements[] = {
942 eHTMLTag_table, eHTMLTag_thead, eHTMLTag_tbody,
943 eHTMLTag_tr, eHTMLTag_tfoot
946 PRBool isHiddenInputInsideTableElement = PR_FALSE;
947 if (aChildTag == eHTMLTag_input &&
948 FindTagInSet(theParentTag, sTableElements,
949 NS_ARRAY_LENGTH(sTableElements))) {
950 PRInt32 attrCount = aNode->GetAttributeCount();
951 for (PRInt32 attrIndex = 0; attrIndex < attrCount; ++attrIndex) {
952 const nsAString& key = aNode->GetKeyAt(attrIndex);
953 if (key.LowerCaseEqualsLiteral("type")) {
954 isHiddenInputInsideTableElement =
955 ValueIsHidden(aNode->GetValueAt(attrIndex));
956 break;
961 // Precompute containment, and pass it to CanOmit()...
962 theParentContains =
963 isHiddenInputInsideTableElement || CanContain(theParentTag, aChildTag);
964 if (!isHiddenInputInsideTableElement &&
965 CanOmit(theParentTag, aChildTag, theParentContains)) {
966 HandleOmittedTag(aToken, aChildTag, theParentTag, aNode);
967 return NS_OK;
970 eProcessRule theRule = eNormalOp;
972 if (!theParentContains &&
973 (IsBlockElement(aChildTag, theParentTag) &&
974 IsInlineElement(theParentTag, theParentTag))) {
975 // Don't test for table to fix 57554.
976 if (eHTMLTag_li != aChildTag) {
977 nsCParserNode* theParentNode = mBodyContext->PeekNode();
978 if (theParentNode && theParentNode->mToken->IsWellFormed()) {
979 theRule = eLetInlineContainBlock;
984 switch (theRule) {
985 case eNormalOp:
986 theChildAgrees = PR_TRUE;
987 if (theParentContains) {
988 eHTMLTags theAncestor = gHTMLElements[aChildTag].mRequiredAncestor;
989 if (eHTMLTag_unknown != theAncestor) {
990 theChildAgrees = HasOpenContainer(theAncestor);
993 if (theChildAgrees && theChildIsContainer) {
994 if (theParentTag != aChildTag) {
995 // Double check the power structure
996 // Note: The bit is currently set on tags such as <A> and <LI>.
997 if (gHTMLElements[aChildTag].ShouldVerifyHierarchy()) {
998 PRInt32 theChildIndex =
999 nsHTMLElement::GetIndexOfChildOrSynonym(*mBodyContext,
1000 aChildTag);
1002 if (kNotFound < theChildIndex && theChildIndex < theIndex) {
1004 * 1 Here's a tricky case from bug 22596: <h5><li><h5>
1005 * How do we know that the 2nd <h5> should close the <LI>
1006 * rather than nest inside the <LI>? (Afterall, the <h5>
1007 * is a legal child of the <LI>).
1009 * The way you know is that there is no root between the
1010 * two, so the <h5> binds more tightly to the 1st <h5>
1011 * than to the <LI>.
1013 * 2 Also, bug 6148 shows this case: <SPAN><DIV><SPAN>
1014 * From this case we learned not to execute this logic if
1015 * the parent is a block.
1017 * 3 Fix for 26583:
1018 * <A href=foo.html><B>foo<A href-bar.html>bar</A></B></A>
1019 * In the above example clicking on "foo" or "bar" should
1020 * link to foo.html or bar.html respectively. That is,
1021 * the inner <A> should be informed about the presence of
1022 * an open <A> above <B>..so that the inner <A> can close
1023 * out the outer <A>. The following code does it for us.
1025 * 4 Fix for 27865 [ similer to 22596 ]. Ex:
1026 * <DL><DD><LI>one<DD><LI>two
1028 theChildAgrees = CanBeContained(aChildTag, *mBodyContext);
1035 if (!(theParentContains && theChildAgrees)) {
1036 if (!CanPropagate(theParentTag, aChildTag, theParentContains)) {
1037 if (theChildIsContainer || !theParentContains) {
1038 if (!theChildAgrees &&
1039 !gHTMLElements[aChildTag].CanAutoCloseTag(*mBodyContext,
1040 theIndex,
1041 aChildTag)) {
1042 // Closing the tags above might cause non-compatible results.
1043 // Ex. <TABLE><TR><TD><TBODY>Text</TD></TR></TABLE>.
1044 // In the example above <TBODY> is badly misplaced, but
1045 // we should not attempt to close the tags above it,
1046 // The safest thing to do is to discard this tag.
1047 // XXX We get the above example wrong anyway because of
1048 // CanBeContained.
1049 return result;
1050 } else if (mBodyContext->mContextTopIndex > 0 &&
1051 theIndex <= mBodyContext->mContextTopIndex) {
1052 // Looks like the parent tag does not want to contain the
1053 // current tag ( aChildTag ). However, we have to force the
1054 // containment, when handling misplaced content, to avoid data
1055 // loss. Ref. bug 138577.
1056 theParentContains = PR_TRUE;
1057 } else {
1058 CloseContainersTo(theIndex, aChildTag, PR_TRUE);
1060 } else {
1061 break;
1063 } else {
1064 CreateContextStackFor(theParentTag, aChildTag);
1065 theIndex = mBodyContext->GetCount();
1068 break;
1070 case eLetInlineContainBlock:
1071 // Break out of this loop and open the block.
1072 theParentContains = theChildAgrees = PR_TRUE;
1073 break;
1075 default:
1076 NS_NOTREACHED("Invalid rule detected");
1078 } while (!(theParentContains && theChildAgrees));
1081 if (theChildIsContainer) {
1082 result = OpenContainer(aNode, aChildTag);
1083 } else {
1084 result = AddLeaf(aNode);
1087 return result;
1090 nsresult
1091 CNavDTD::WillHandleStartTag(CToken* aToken, eHTMLTags aTag,
1092 nsIParserNode& aNode)
1094 nsresult result = NS_OK;
1096 PRInt32 stackDepth = mBodyContext->GetCount();
1097 if (stackDepth >= FONTSTYLE_IGNORE_DEPTH &&
1098 gHTMLElements[aTag].IsMemberOf(kFontStyle)) {
1099 // Prevent bug 58917 by tossing the new kFontStyle start tag
1100 return kHierarchyTooDeep;
1103 if (stackDepth >= PHRASE_IGNORE_DEPTH &&
1104 gHTMLElements[aTag].IsMemberOf(kPhrase)) {
1105 // Prevent bug 58917 by tossing the new kPhrase start tag
1106 return kHierarchyTooDeep;
1110 * Now a little code to deal with bug #49687 (crash when layout stack gets
1111 * too deep) I've also opened this up to any container (not just inlines):
1112 * re bug 55095 Improved to handle bug 55980 (infinite loop caused when
1113 * DEPTH is exceeded and </P> is encountered by itself (<P>) is continuously
1114 * produced.
1116 if (stackDepth > MAX_REFLOW_DEPTH) {
1117 if (nsHTMLElement::IsContainer(aTag) &&
1118 !gHTMLElements[aTag].HasSpecialProperty(kHandleStrayTag)) {
1119 // Ref. bug 98261,49678,55095,55980
1120 // Instead of throwing away the current tag close it's parent
1121 // such that the stack level does not go beyond the max_reflow_depth.
1122 // This would allow leaf tags, that follow the current tag, to find
1123 // the correct node.
1124 while (stackDepth != MAX_REFLOW_DEPTH && NS_SUCCEEDED(result)) {
1125 result = CloseContainersTo(mBodyContext->Last(), PR_FALSE);
1126 --stackDepth;
1131 STOP_TIMER()
1132 MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::WillHandleStartTag(), this=%p\n", this));
1134 if (aTag <= NS_HTML_TAG_MAX) {
1135 result = mSink->NotifyTagObservers(&aNode);
1138 MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::WillHandleStartTag(), this=%p\n", this));
1139 START_TIMER()
1141 return result;
1144 static void
1145 PushMisplacedAttributes(nsIParserNode& aNode, nsDeque& aDeque)
1147 nsCParserNode& theAttrNode = static_cast<nsCParserNode &>(aNode);
1149 for (PRInt32 count = aNode.GetAttributeCount(); count > 0; --count) {
1150 CToken* theAttrToken = theAttrNode.PopAttributeTokenFront();
1151 if (theAttrToken) {
1152 theAttrToken->SetNewlineCount(0);
1153 aDeque.Push(theAttrToken);
1158 void
1159 CNavDTD::HandleOmittedTag(CToken* aToken, eHTMLTags aChildTag,
1160 eHTMLTags aParent, nsIParserNode* aNode)
1162 NS_PRECONDITION(mBodyContext != nsnull, "need a context to work with");
1164 // The trick here is to see if the parent can contain the child, but prefers
1165 // not to. Only if the parent CANNOT contain the child should we look to see
1166 // if it's potentially a child of another section. If it is, the cache it for
1167 // later.
1168 PRInt32 theTagCount = mBodyContext->GetCount();
1169 PRBool pushToken = PR_FALSE;
1171 if (gHTMLElements[aParent].HasSpecialProperty(kBadContentWatch) &&
1172 !nsHTMLElement::IsWhitespaceTag(aChildTag)) {
1173 eHTMLTags theTag = eHTMLTag_unknown;
1175 // Don't bother saving misplaced stuff in the head. This can happen in
1176 // cases like |<head><noscript><table>foo|. See bug 401169.
1177 if (mFlags & NS_DTD_FLAG_HAS_OPEN_HEAD) {
1178 NS_ASSERTION(!(mFlags & NS_DTD_FLAG_HAS_MAIN_CONTAINER),
1179 "Bad state");
1180 return;
1183 // Determine the insertion point
1184 while (theTagCount > 0) {
1185 theTag = mBodyContext->TagAt(--theTagCount);
1186 if (!gHTMLElements[theTag].HasSpecialProperty(kBadContentWatch)) {
1187 // This is our insertion point.
1188 mBodyContext->mContextTopIndex = theTagCount;
1189 break;
1193 if (mBodyContext->mContextTopIndex > -1) {
1194 pushToken = PR_TRUE;
1196 // Remember that we've stashed some misplaced content.
1197 mFlags |= NS_DTD_FLAG_MISPLACED_CONTENT;
1201 if (aChildTag != aParent &&
1202 gHTMLElements[aParent].HasSpecialProperty(kSaveMisplaced)) {
1203 NS_ASSERTION(!pushToken, "A strange element has both kBadContentWatch "
1204 "and kSaveMisplaced");
1205 pushToken = PR_TRUE;
1208 if (pushToken) {
1209 // Hold on to this token for later use. Ref Bug. 53695
1210 IF_HOLD(aToken);
1211 PushIntoMisplacedStack(aToken);
1213 // If the token is attributed then save those attributes too.
1214 PushMisplacedAttributes(*aNode, mMisplacedContent);
1219 * This method gets called when a kegen token is found.
1221 * @update harishd 05/02/00
1222 * @param aNode -- CParserNode representing keygen
1223 * @return NS_OK if all went well; ERROR if error occured
1225 nsresult
1226 CNavDTD::HandleKeyGen(nsIParserNode* aNode)
1228 nsresult result = NS_OK;
1230 if (aNode) {
1231 nsCOMPtr<nsIFormProcessor> theFormProcessor =
1232 do_GetService(kFormProcessorCID, &result);
1234 if (NS_SUCCEEDED(result)) {
1235 PRInt32 theAttrCount = aNode->GetAttributeCount();
1236 nsStringArray theContent;
1237 nsAutoString theAttribute;
1238 nsAutoString theFormType;
1239 CToken* theToken = nsnull;
1241 theFormType.AssignLiteral("select");
1243 result = theFormProcessor->ProvideContent(theFormType, theContent,
1244 theAttribute);
1245 if (NS_SUCCEEDED(result)) {
1246 nsString* theTextValue = nsnull;
1247 PRInt32 theIndex = nsnull;
1249 if (mTokenizer && mTokenAllocator) {
1250 // Populate the tokenizer with the fabricated elements in the reverse
1251 // order such that <SELECT> is on the top fo the tokenizer followed by
1252 // <OPTION>s and </SELECT>.
1253 theToken = mTokenAllocator->CreateTokenOfType(eToken_end,
1254 eHTMLTag_select);
1255 NS_ENSURE_TRUE(theToken, NS_ERROR_OUT_OF_MEMORY);
1256 mTokenizer->PushTokenFront(theToken);
1258 for (theIndex = theContent.Count()-1; theIndex > -1; --theIndex) {
1259 theTextValue = theContent[theIndex];
1260 theToken = mTokenAllocator->CreateTokenOfType(eToken_text,
1261 eHTMLTag_text,
1262 *theTextValue);
1263 NS_ENSURE_TRUE(theToken, NS_ERROR_OUT_OF_MEMORY);
1264 mTokenizer->PushTokenFront(theToken);
1266 theToken = mTokenAllocator->CreateTokenOfType(eToken_start,
1267 eHTMLTag_option);
1268 NS_ENSURE_TRUE(theToken, NS_ERROR_OUT_OF_MEMORY);
1269 mTokenizer->PushTokenFront(theToken);
1272 // The attribute ( provided by the form processor ) should be a part
1273 // of the SELECT. Placing the attribute token on the tokenizer to get
1274 // picked up by the SELECT.
1275 theToken = mTokenAllocator->CreateTokenOfType(eToken_attribute,
1276 eHTMLTag_unknown,
1277 theAttribute);
1278 NS_ENSURE_TRUE(theToken, NS_ERROR_OUT_OF_MEMORY);
1280 ((CAttributeToken*)theToken)->SetKey(NS_LITERAL_STRING("_moz-type"));
1281 mTokenizer->PushTokenFront(theToken);
1283 // Pop out NAME and CHALLENGE attributes ( from the keygen NODE ) and
1284 // place it in the tokenizer such that the attribtues get sucked into
1285 // SELECT node.
1286 for (theIndex = theAttrCount; theIndex > 0; --theIndex) {
1287 mTokenizer->PushTokenFront(((nsCParserNode*)aNode)->PopAttributeToken());
1290 theToken = mTokenAllocator->CreateTokenOfType(eToken_start,
1291 eHTMLTag_select);
1292 NS_ENSURE_TRUE(theToken, NS_ERROR_OUT_OF_MEMORY);
1294 // Increment the count because of the additional attribute from the form processor.
1295 theToken->SetAttributeCount(theAttrCount + 1);
1296 mTokenizer->PushTokenFront(theToken);
1301 return result;
1304 PRBool
1305 CNavDTD::IsAlternateTag(eHTMLTags aTag)
1307 switch (aTag) {
1308 case eHTMLTag_noembed:
1309 return PR_TRUE;
1311 case eHTMLTag_noscript:
1312 return (mFlags & NS_IPARSER_FLAG_SCRIPT_ENABLED) != 0;
1314 case eHTMLTag_iframe:
1315 case eHTMLTag_noframes:
1316 return (mFlags & NS_IPARSER_FLAG_FRAMES_ENABLED) != 0;
1318 default:
1319 return PR_FALSE;
1323 nsresult
1324 CNavDTD::HandleStartToken(CToken* aToken)
1326 NS_PRECONDITION(nsnull != aToken, kNullToken);
1328 nsCParserNode* theNode = mNodeAllocator.CreateNode(aToken, mTokenAllocator);
1329 NS_ENSURE_TRUE(theNode, NS_ERROR_OUT_OF_MEMORY);
1331 eHTMLTags theChildTag = (eHTMLTags)aToken->GetTypeID();
1332 PRInt16 attrCount = aToken->GetAttributeCount();
1333 eHTMLTags theParent = mBodyContext->Last();
1334 nsresult result = NS_OK;
1336 if (attrCount > 0) {
1337 result = CollectAttributes(theNode, theChildTag, attrCount);
1340 if (NS_OK == result) {
1341 result = WillHandleStartTag(aToken, theChildTag, *theNode);
1342 if (NS_OK == result) {
1343 PRBool isTokenHandled = PR_FALSE;
1344 PRBool theHeadIsParent = PR_FALSE;
1346 if (nsHTMLElement::IsSectionTag(theChildTag)) {
1347 switch (theChildTag) {
1348 case eHTMLTag_html:
1349 if (mBodyContext->GetCount() > 0) {
1350 result = OpenContainer(theNode, theChildTag);
1351 isTokenHandled = PR_TRUE;
1353 break;
1355 case eHTMLTag_body:
1356 if (mFlags & NS_DTD_FLAG_HAS_OPEN_BODY) {
1357 result = OpenContainer(theNode, theChildTag);
1358 isTokenHandled=PR_TRUE;
1360 break;
1362 case eHTMLTag_head:
1363 mFlags |= NS_DTD_FLAG_HAS_EXPLICIT_HEAD;
1365 if (mFlags & NS_DTD_FLAG_HAS_MAIN_CONTAINER) {
1366 HandleOmittedTag(aToken, theChildTag, theParent, theNode);
1367 isTokenHandled = PR_TRUE;
1369 break;
1371 default:
1372 break;
1376 PRBool isExclusive = PR_FALSE;
1377 theHeadIsParent = nsHTMLElement::IsChildOfHead(theChildTag, isExclusive);
1379 switch (theChildTag) {
1380 case eHTMLTag_area:
1381 if (!mOpenMapCount) {
1382 isTokenHandled = PR_TRUE;
1385 STOP_TIMER();
1386 MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::HandleStartToken(), this=%p\n", this));
1388 if (mOpenMapCount > 0 && mSink) {
1389 result = mSink->AddLeaf(*theNode);
1390 isTokenHandled = PR_TRUE;
1393 MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::HandleStartToken(), this=%p\n", this));
1394 START_TIMER();
1395 break;
1397 case eHTMLTag_image:
1398 aToken->SetTypeID(theChildTag = eHTMLTag_img);
1399 break;
1401 case eHTMLTag_keygen:
1402 result = HandleKeyGen(theNode);
1403 isTokenHandled = PR_TRUE;
1404 break;
1406 case eHTMLTag_script:
1407 // Script isn't really exclusively in the head. However, we treat it
1408 // as such to make sure that we don't pull scripts outside the head
1409 // into the body.
1410 // XXX Where does the script go in a frameset document?
1411 isExclusive = !(mFlags & NS_DTD_FLAG_HAD_BODY);
1412 break;
1414 default:;
1417 if (!isTokenHandled) {
1418 PRBool prefersBody =
1419 gHTMLElements[theChildTag].HasSpecialProperty(kPreferBody);
1421 // If this tag prefers to be in the head (when neither the head nor the
1422 // body have been explicitly opened) then check that we haven't seen the
1423 // body (true until the <body> tag has really been seen). Otherwise,
1424 // check if the head has been explicitly opened. See bug 307122.
1425 theHeadIsParent = theHeadIsParent &&
1426 (isExclusive ||
1427 (prefersBody
1428 ? (mFlags & NS_DTD_FLAG_HAS_EXPLICIT_HEAD) &&
1429 (mFlags & NS_DTD_FLAG_HAS_OPEN_HEAD)
1430 : !(mFlags & NS_DTD_FLAG_HAS_MAIN_CONTAINER)));
1432 if (theHeadIsParent) {
1433 // These tokens prefer to be in the head.
1434 result = AddHeadContent(theNode);
1435 } else {
1436 result = HandleDefaultStartToken(aToken, theChildTag, theNode);
1440 // Now do any post processing necessary on the tag...
1441 if (NS_OK == result) {
1442 DidHandleStartTag(*theNode, theChildTag);
1447 if (kHierarchyTooDeep == result) {
1448 // Reset this error to ok; all that happens here is that given inline tag
1449 // gets dropped because the stack is too deep. Don't terminate parsing.
1450 result = NS_OK;
1453 IF_FREE(theNode, &mNodeAllocator);
1454 return result;
1458 * Call this to see if you have a closeable peer on the stack that
1459 * is ABOVE one of its root tags.
1461 * @update gess 4/11/99
1462 * @param aRootTagList -- list of root tags for aTag
1463 * @param aTag -- tag to test for containership
1464 * @return PR_TRUE if given tag can contain other tags
1466 static PRBool
1467 HasCloseablePeerAboveRoot(const TagList& aRootTagList, nsDTDContext& aContext,
1468 eHTMLTags aTag, PRBool anEndTag)
1470 PRInt32 theRootIndex = LastOf(aContext, aRootTagList);
1471 const TagList* theCloseTags = anEndTag
1472 ? gHTMLElements[aTag].GetAutoCloseEndTags()
1473 : gHTMLElements[aTag].GetAutoCloseStartTags();
1474 PRInt32 theChildIndex = -1;
1476 if (theCloseTags) {
1477 theChildIndex=LastOf(aContext, *theCloseTags);
1478 } else if (anEndTag || !gHTMLElements[aTag].CanContainSelf()) {
1479 theChildIndex = aContext.LastOf(aTag);
1482 // I changed this to theRootIndex<=theChildIndex so to handle this case:
1483 // <SELECT><OPTGROUP>...</OPTGROUP>
1484 return theRootIndex<=theChildIndex;
1489 * This method is called to determine whether or not an END tag
1490 * can be autoclosed. This means that based on the current
1491 * context, the stack should be closed to the nearest matching
1492 * tag.
1494 * @param aTag -- tag enum of child to be tested
1495 * @return PR_TRUE if autoclosure should occur
1497 static eHTMLTags
1498 FindAutoCloseTargetForEndTag(eHTMLTags aCurrentTag, nsDTDContext& aContext,
1499 nsDTDMode aMode)
1501 int theTopIndex = aContext.GetCount();
1502 eHTMLTags thePrevTag = aContext.Last();
1504 if (nsHTMLElement::IsContainer(aCurrentTag)) {
1505 PRInt32 theChildIndex =
1506 nsHTMLElement::GetIndexOfChildOrSynonym(aContext, aCurrentTag);
1508 if (kNotFound < theChildIndex) {
1509 if (thePrevTag == aContext[theChildIndex]) {
1510 return aContext[theChildIndex];
1513 if (nsHTMLElement::IsBlockCloser(aCurrentTag)) {
1515 * Here's the algorithm:
1516 * Our here is sitting at aChildIndex. There are other tags above it
1517 * on the stack. We have to try to close them out, but we may encounter
1518 * one that can block us. The way to tell is by comparing each tag on
1519 * the stack against our closeTag and rootTag list.
1521 * For each tag above our hero on the stack, ask 3 questions:
1522 * 1. Is it in the closeTag list? If so, the we can skip over it
1523 * 2. Is it in the rootTag list? If so, then we're gated by it
1524 * 3. Otherwise its non-specified and we simply presume we can close it.
1526 const TagList* theCloseTags =
1527 gHTMLElements[aCurrentTag].GetAutoCloseEndTags();
1528 const TagList* theRootTags =
1529 gHTMLElements[aCurrentTag].GetEndRootTags();
1531 if (theCloseTags) {
1532 // At a mininimum, this code is needed for H1..H6
1533 while (theChildIndex < --theTopIndex) {
1534 eHTMLTags theNextTag = aContext[theTopIndex];
1535 if (!FindTagInSet(theNextTag, theCloseTags->mTags,
1536 theCloseTags->mCount) &&
1537 FindTagInSet(theNextTag, theRootTags->mTags,
1538 theRootTags->mCount)) {
1539 // We encountered a tag in root list so fail (we're gated).
1540 return eHTMLTag_unknown;
1543 // Otherwise, presume it's something we can simply ignore and
1544 // continue searching.
1547 eHTMLTags theTarget = aContext.TagAt(theChildIndex);
1548 if (aCurrentTag != theTarget) {
1549 aCurrentTag = theTarget;
1551 // If you make it here, we're ungated and found a target!
1552 return aCurrentTag;
1553 } else if (theRootTags) {
1554 // Since we didn't find any close tags, see if there is an instance of
1555 // aCurrentTag above the stack from the roottag.
1556 if (HasCloseablePeerAboveRoot(*theRootTags, aContext, aCurrentTag,
1557 PR_TRUE)) {
1558 return aCurrentTag;
1559 } else {
1560 return eHTMLTag_unknown;
1563 } else {
1564 // Ok, a much more sensible approach for non-block closers; use the tag
1565 // group to determine closure: For example: %phrasal closes %phrasal,
1566 // %fontstyle and %special
1567 return gHTMLElements[aCurrentTag].GetCloseTargetForEndTag(aContext,
1568 theChildIndex,
1569 aMode);
1574 return eHTMLTag_unknown;
1577 static void
1578 StripWSFollowingTag(eHTMLTags aChildTag, nsITokenizer* aTokenizer,
1579 nsTokenAllocator* aTokenAllocator, PRInt32& aNewlineCount)
1581 if (!aTokenizer || !aTokenAllocator) {
1582 return;
1585 CToken* theToken = aTokenizer->PeekToken();
1587 while (theToken) {
1588 eHTMLTokenTypes theType = eHTMLTokenTypes(theToken->GetTokenType());
1590 switch(theType) {
1591 case eToken_newline:
1592 ++aNewlineCount;
1593 // Fall through...
1595 case eToken_whitespace:
1596 theToken = aTokenizer->PopToken();
1597 IF_FREE(theToken, aTokenAllocator);
1599 theToken = aTokenizer->PeekToken();
1600 break;
1602 default:
1603 theToken = nsnull;
1604 break;
1610 * This method gets called when an end token has been
1611 * encountered in the parse process. If the end tag matches
1612 * the start tag on the stack, then simply close it. Otherwise,
1613 * we have a erroneous state condition. This can be because we
1614 * have a close tag with no prior open tag (user error) or because
1615 * we screwed something up in the parse process. I'm not sure
1616 * yet how to tell the difference.
1618 * @param aToken -- next (start) token to be handled
1619 * @return PR_TRUE if all went well; PR_FALSE if error occured
1621 nsresult
1622 CNavDTD::HandleEndToken(CToken* aToken)
1624 NS_PRECONDITION(nsnull != aToken, kNullToken);
1626 nsresult result = NS_OK;
1627 eHTMLTags theChildTag = (eHTMLTags)aToken->GetTypeID();
1629 // Begin by dumping any attributes (bug 143512)
1630 CollectAttributes(nsnull, theChildTag, aToken->GetAttributeCount());
1632 switch (theChildTag) {
1633 case eHTMLTag_link:
1634 case eHTMLTag_meta:
1635 break;
1637 case eHTMLTag_head:
1638 StripWSFollowingTag(theChildTag, mTokenizer, mTokenAllocator, mLineNumber);
1639 if (mBodyContext->LastOf(eHTMLTag_head) != kNotFound) {
1640 result = CloseContainersTo(eHTMLTag_head, PR_FALSE);
1642 mFlags &= ~NS_DTD_FLAG_HAS_EXPLICIT_HEAD;
1643 break;
1645 case eHTMLTag_form:
1646 result = CloseContainer(eHTMLTag_form, PR_FALSE);
1647 break;
1649 case eHTMLTag_br:
1651 // This is special NAV-QUIRKS code that allows users to use </BR>, even
1652 // though that isn't a legitimate tag.
1653 if (eDTDMode_quirks == mDTDMode) {
1654 // Use recycler and pass the token thro' HandleToken() to fix bugs
1655 // like 32782.
1656 CToken* theToken = mTokenAllocator->CreateTokenOfType(eToken_start,
1657 theChildTag);
1658 result = HandleToken(theToken, mParser);
1661 break;
1663 case eHTMLTag_body:
1664 case eHTMLTag_html:
1665 StripWSFollowingTag(theChildTag, mTokenizer, mTokenAllocator,
1666 mLineNumber);
1667 break;
1669 case eHTMLTag_script:
1670 // Note: we don't fall through to the default case because
1671 // CloseContainersTo() has the bad habit of closing tags that are opened
1672 // by document.write(). Fortunately, the tokenizer guarantees that no
1673 // actual tags appear between <script> and </script> so we won't be
1674 // closing the wrong tag.
1675 if (mBodyContext->Last() != eHTMLTag_script) {
1676 // Except if we're here, then there's probably a stray script tag.
1677 NS_ASSERTION(mBodyContext->LastOf(eHTMLTag_script) == kNotFound,
1678 "Mishandling scripts in CNavDTD");
1679 break;
1682 mBodyContext->Pop();
1683 result = CloseContainer(eHTMLTag_script, aToken->IsInError());
1684 break;
1686 default:
1688 // Now check to see if this token should be omitted, or
1689 // if it's gated from closing by the presence of another tag.
1690 if (gHTMLElements[theChildTag].CanOmitEndTag()) {
1691 PopStyle(theChildTag);
1692 } else {
1693 eHTMLTags theParentTag = mBodyContext->Last();
1695 // First open transient styles, so that we see any autoclosed style
1696 // tags.
1697 if (nsHTMLElement::IsResidualStyleTag(theChildTag)) {
1698 result = OpenTransientStyles(theChildTag);
1699 if (NS_FAILED(result)) {
1700 return result;
1704 if (kNotFound ==
1705 nsHTMLElement::GetIndexOfChildOrSynonym(*mBodyContext,
1706 theChildTag)) {
1707 // Ref: bug 30487
1708 // Make sure that we don't cross boundaries, of certain elements,
1709 // to close stylistic information.
1710 // <font face="helvetica"><table><tr><td></font></td></tr></table> some text...
1711 // In the above ex. the orphaned FONT tag, inside TD, should cross
1712 // TD boundary to close the FONT tag above TABLE.
1713 static eHTMLTags gBarriers[] = {
1714 eHTMLTag_thead, eHTMLTag_tbody, eHTMLTag_tfoot, eHTMLTag_table
1717 if (!FindTagInSet(theParentTag, gBarriers,
1718 NS_ARRAY_LENGTH(gBarriers)) &&
1719 nsHTMLElement::IsResidualStyleTag(theChildTag)) {
1720 // Fix bug 77746
1721 mBodyContext->RemoveStyle(theChildTag);
1724 // If the bit kHandleStrayTag is set then we automatically open up a
1725 // matching start tag (compatibility). Currently this bit is set on
1726 // P tag. This also fixes Bug: 22623
1727 if (gHTMLElements[theChildTag].HasSpecialProperty(kHandleStrayTag) &&
1728 mDTDMode != eDTDMode_full_standards &&
1729 mDTDMode != eDTDMode_almost_standards) {
1730 // Oh boy!! we found a "stray" tag. Nav4.x and IE introduce line
1731 // break in such cases. So, let's simulate that effect for
1732 // compatibility.
1733 // Ex. <html><body>Hello</P>There</body></html>
1734 PRInt32 theParentContains = -1;
1735 if (!CanOmit(theParentTag, theChildTag, theParentContains)) {
1736 CToken* theStartToken =
1737 mTokenAllocator->CreateTokenOfType(eToken_start, theChildTag);
1738 NS_ENSURE_TRUE(theStartToken, NS_ERROR_OUT_OF_MEMORY);
1740 // This check for NS_DTD_FLAG_IN_MISPLACED_CONTENT was added
1741 // to fix bug 142965.
1742 if (!(mFlags & NS_DTD_FLAG_IN_MISPLACED_CONTENT)) {
1743 // We're not handling misplaced content right now, just push
1744 // these new tokens back on the stack and handle them in the
1745 // regular flow of HandleToken.
1746 IF_HOLD(aToken);
1747 mTokenizer->PushTokenFront(aToken);
1748 mTokenizer->PushTokenFront(theStartToken);
1749 } else {
1750 // Oops, we're in misplaced content. Handle these tokens
1751 // directly instead of trying to push them onto the tokenizer
1752 // stack.
1753 result = HandleToken(theStartToken, mParser);
1754 NS_ENSURE_SUCCESS(result, result);
1756 IF_HOLD(aToken);
1757 result = HandleToken(aToken, mParser);
1761 return result;
1763 if (result == NS_OK) {
1764 eHTMLTags theTarget =
1765 FindAutoCloseTargetForEndTag(theChildTag, *mBodyContext,
1766 mDTDMode);
1767 if (eHTMLTag_unknown != theTarget) {
1768 result = CloseContainersTo(theTarget, PR_FALSE);
1773 break;
1776 return result;
1780 * This method will be triggered when the end of a table is
1781 * encountered. Its primary purpose is to process all the
1782 * bad-contents pertaining a particular table. The position
1783 * of the table is the token bank ID.
1785 * @update harishd 03/24/99
1786 * @param aTag - This ought to be a table tag
1789 nsresult
1790 CNavDTD::HandleSavedTokens(PRInt32 anIndex)
1792 NS_PRECONDITION(mBodyContext != nsnull && mBodyContext->GetCount() > 0, "invalid context");
1794 nsresult result = NS_OK;
1796 if (mSink && (anIndex > kNotFound)) {
1797 PRInt32 theBadTokenCount = mMisplacedContent.GetSize();
1799 if (theBadTokenCount > 0) {
1800 mFlags |= NS_DTD_FLAG_IN_MISPLACED_CONTENT;
1802 if (!mTempContext && !(mTempContext = new nsDTDContext())) {
1803 return NS_ERROR_OUT_OF_MEMORY;
1806 CToken* theToken;
1807 eHTMLTags theTag;
1808 PRInt32 attrCount;
1809 PRInt32 theTopIndex = anIndex + 1;
1810 PRInt32 theTagCount = mBodyContext->GetCount();
1812 if (mSink->IsFormOnStack()) {
1813 // Do this to synchronize dtd stack and the sink stack.
1814 // Note: FORM is never on the dtd stack because its always
1815 // considered as a leaf. However, in the sink FORM can either
1816 // be a container or a leaf. Therefore, we have to check
1817 // with the sink -- Ref: Bug 20087.
1818 ++anIndex;
1821 STOP_TIMER()
1822 MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::HandleSavedTokensAbove(), this=%p\n", this));
1823 // Pause the main context and switch to the new context.
1824 result = mSink->BeginContext(anIndex);
1825 MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::HandleSavedTokensAbove(), this=%p\n", this));
1826 START_TIMER()
1828 NS_ENSURE_SUCCESS(result, result);
1830 // The body context should contain contents only upto the marked position.
1831 mBodyContext->MoveEntries(*mTempContext, theTagCount - theTopIndex);
1833 // Now flush out all the bad contents.
1834 while (theBadTokenCount-- > 0){
1835 theToken = (CToken*)mMisplacedContent.PopFront();
1836 if (theToken) {
1837 theTag = (eHTMLTags)theToken->GetTypeID();
1838 attrCount = theToken->GetAttributeCount();
1839 // Put back attributes, which once got popped out, into the
1840 // tokenizer. Make sure we preserve their ordering, however!
1841 // XXXbz would it be faster to get the tokens out with ObjectAt and
1842 // put them in the tokenizer and then PopFront them all from
1843 // mMisplacedContent?
1844 nsDeque temp;
1845 for (PRInt32 j = 0; j < attrCount; ++j) {
1846 CToken* theAttrToken = (CToken*)mMisplacedContent.PopFront();
1847 if (theAttrToken) {
1848 temp.Push(theAttrToken);
1850 theBadTokenCount--;
1852 mTokenizer->PrependTokens(temp);
1854 if (eToken_end == theToken->GetTokenType()) {
1855 // Ref: Bug 25202
1856 // Make sure that the BeginContext() is ended only by the call to
1857 // EndContext(). Ex: <center><table><a></center>.
1858 // In the Ex. above </center> should not close <center> above table.
1859 // Doing so will cause the current context to get closed prematurely.
1860 eHTMLTags closed = FindAutoCloseTargetForEndTag(theTag, *mBodyContext,
1861 mDTDMode);
1862 PRInt32 theIndex = closed != eHTMLTag_unknown
1863 ? mBodyContext->LastOf(closed)
1864 : kNotFound;
1866 if (theIndex != kNotFound &&
1867 theIndex <= mBodyContext->mContextTopIndex) {
1868 IF_FREE(theToken, mTokenAllocator);
1869 continue;
1873 // XXX This should go away, with this call, it becomes extremely
1874 // difficult to handle misplaced style and link tags, since it's hard
1875 // to propagate the block return all the way up and then re-enter this
1876 // method.
1877 result = HandleToken(theToken, mParser);
1880 if (theTopIndex != mBodyContext->GetCount()) {
1881 CloseContainersTo(theTopIndex, mBodyContext->TagAt(theTopIndex),
1882 PR_TRUE);
1885 // Bad-contents were successfully processed. Now, itz time to get
1886 // back to the original body context state.
1887 mTempContext->MoveEntries(*mBodyContext, theTagCount - theTopIndex);
1889 STOP_TIMER()
1890 MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::HandleSavedTokensAbove(), this=%p\n", this));
1891 // Terminate the new context and switch back to the main context
1892 mSink->EndContext(anIndex);
1893 MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::HandleSavedTokensAbove(), this=%p\n", this));
1894 START_TIMER()
1896 mFlags &= ~NS_DTD_FLAG_IN_MISPLACED_CONTENT;
1899 return result;
1904 * This method gets called when an entity token has been
1905 * encountered in the parse process.
1907 * @update gess 3/25/98
1908 * @param aToken -- next (start) token to be handled
1909 * @return PR_TRUE if all went well; PR_FALSE if error occured
1911 nsresult
1912 CNavDTD::HandleEntityToken(CToken* aToken)
1914 NS_PRECONDITION(nsnull != aToken, kNullToken);
1916 nsresult result = NS_OK;
1918 const nsSubstring& theStr = aToken->GetStringValue();
1920 if (kHashsign != theStr.First() &&
1921 -1 == nsHTMLEntities::EntityToUnicode(theStr)) {
1922 // If you're here we have a bogus entity.
1923 // Convert it into a text token.
1924 CToken *theToken;
1926 nsAutoString entityName;
1927 entityName.AssignLiteral("&");
1928 entityName.Append(theStr);
1929 theToken = mTokenAllocator->CreateTokenOfType(eToken_text, eHTMLTag_text,
1930 entityName);
1931 NS_ENSURE_TRUE(theToken, NS_ERROR_OUT_OF_MEMORY);
1933 // theToken should get recycled automagically...
1934 return HandleToken(theToken, mParser);
1937 eHTMLTags theParentTag = mBodyContext->Last();
1938 nsCParserNode* theNode = mNodeAllocator.CreateNode(aToken, mTokenAllocator);
1939 NS_ENSURE_TRUE(theNode, NS_ERROR_OUT_OF_MEMORY);
1941 PRInt32 theParentContains = -1;
1942 if (CanOmit(theParentTag, eHTMLTag_entity, theParentContains)) {
1943 eHTMLTags theCurrTag = (eHTMLTags)aToken->GetTypeID();
1944 HandleOmittedTag(aToken, theCurrTag, theParentTag, theNode);
1945 } else {
1946 result = AddLeaf(theNode);
1948 IF_FREE(theNode, &mNodeAllocator);
1949 return result;
1953 * This method gets called when a comment token has been
1954 * encountered in the parse process. After making sure
1955 * we're somewhere in the body, we handle the comment
1956 * in the same code that we use for text.
1958 * @update gess 3/25/98
1959 * @param aToken -- next (start) token to be handled
1960 * @return PR_TRUE if all went well; PR_FALSE if error occured
1962 nsresult
1963 CNavDTD::HandleCommentToken(CToken* aToken)
1965 NS_PRECONDITION(nsnull != aToken, kNullToken);
1967 nsCParserNode* theNode = mNodeAllocator.CreateNode(aToken, mTokenAllocator);
1968 NS_ENSURE_TRUE(theNode, NS_ERROR_OUT_OF_MEMORY);
1970 STOP_TIMER();
1971 MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::HandleCommentToken(), this=%p\n", this));
1973 nsresult result = mSink ? mSink->AddComment(*theNode) : NS_OK;
1975 IF_FREE(theNode, &mNodeAllocator);
1977 MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::HandleCommentToken(), this=%p\n", this));
1978 START_TIMER();
1980 return result;
1985 * This method gets called when an attribute token has been
1986 * encountered in the parse process. This is an error, since
1987 * all attributes should have been accounted for in the prior
1988 * start or end tokens
1990 * @update gess 3/25/98
1991 * @param aToken -- next (start) token to be handled
1992 * @return PR_TRUE if all went well; PR_FALSE if error occured
1994 nsresult
1995 CNavDTD::HandleAttributeToken(CToken* aToken)
1997 NS_ERROR("attribute encountered -- this shouldn't happen.");
1998 return NS_OK;
2002 * This method gets called when an "instruction" token has been
2003 * encountered in the parse process.
2005 * @update gess 3/25/98
2006 * @param aToken -- next (start) token to be handled
2007 * @return PR_TRUE if all went well; PR_FALSE if error occured
2009 nsresult
2010 CNavDTD::HandleProcessingInstructionToken(CToken* aToken)
2012 NS_PRECONDITION(nsnull != aToken, kNullToken);
2014 nsCParserNode* theNode = mNodeAllocator.CreateNode(aToken, mTokenAllocator);
2015 NS_ENSURE_TRUE(theNode, NS_ERROR_OUT_OF_MEMORY);
2017 STOP_TIMER();
2018 MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::HandleProcessingInstructionToken(), this=%p\n", this));
2020 nsresult result = mSink ? mSink->AddProcessingInstruction(*theNode) : NS_OK;
2022 IF_FREE(theNode, &mNodeAllocator);
2024 MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::HandleProcessingInstructionToken(), this=%p\n", this));
2025 START_TIMER();
2027 return result;
2031 * This method gets called when a DOCTYPE token has been
2032 * encountered in the parse process.
2034 * @update harishd 09/02/99
2035 * @param aToken -- The very first token to be handled
2036 * @return PR_TRUE if all went well; PR_FALSE if error occured
2038 nsresult
2039 CNavDTD::HandleDocTypeDeclToken(CToken* aToken)
2041 NS_PRECONDITION(nsnull != aToken, kNullToken);
2043 CDoctypeDeclToken* theToken = static_cast<CDoctypeDeclToken*>(aToken);
2044 nsAutoString docTypeStr(theToken->GetStringValue());
2045 // XXX Doesn't this count the newlines twice?
2046 if (!IsParserInDocWrite()) {
2047 mLineNumber += docTypeStr.CountChar(kNewLine);
2050 PRInt32 len = docTypeStr.Length();
2051 PRInt32 pos = docTypeStr.RFindChar(kGreaterThan);
2052 if (pos != kNotFound) {
2053 // First remove '>' from the end.
2054 docTypeStr.Cut(pos, len - pos);
2057 // Now remove "<!" from the begining
2058 docTypeStr.Cut(0, 2);
2059 theToken->SetStringValue(docTypeStr);
2061 nsCParserNode* theNode = mNodeAllocator.CreateNode(aToken, mTokenAllocator);
2062 NS_ENSURE_TRUE(theNode, NS_ERROR_OUT_OF_MEMORY);
2063 STOP_TIMER();
2064 MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::HandleDocTypeDeclToken(), this=%p\n", this));
2066 nsresult result = mSink ? mSink->AddDocTypeDecl(*theNode) : NS_OK;
2068 IF_FREE(theNode, &mNodeAllocator);
2070 MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::HandleDocTypeDeclToken(), this=%p\n", this));
2071 START_TIMER();
2073 return result;
2077 * Retrieve the attributes for this node, and add then into
2078 * the node.
2080 * @update gess4/22/98
2081 * @param aNode is the node you want to collect attributes for
2082 * @param aCount is the # of attributes you're expecting
2083 * @return error code (should be 0)
2085 nsresult
2086 CNavDTD::CollectAttributes(nsIParserNode *aNode, eHTMLTags aTag, PRInt32 aCount)
2088 int attr = 0;
2089 nsresult result = NS_OK;
2090 int theAvailTokenCount = mTokenizer->GetCount();
2092 if (aCount <= theAvailTokenCount) {
2093 CToken* theToken;
2094 for (attr = 0; attr < aCount; ++attr) {
2095 theToken = mTokenizer->PopToken();
2096 if (theToken) {
2097 eHTMLTokenTypes theType = eHTMLTokenTypes(theToken->GetTokenType());
2098 if (theType != eToken_attribute) {
2099 // If you're here then it means that the token does not
2100 // belong to this node. Put the token back into the tokenizer
2101 // and let it go thro' the regular path. Bug: 59189.
2102 mTokenizer->PushTokenFront(theToken);
2103 break;
2106 if (!IsParserInDocWrite()) {
2107 mLineNumber += theToken->GetNewlineCount();
2110 if (aNode) {
2111 // If the key is empty, the attribute is unusable, so we should not
2112 // add it to the node.
2113 if (!((CAttributeToken*)theToken)->GetKey().IsEmpty()) {
2114 aNode->AddAttribute(theToken);
2115 } else {
2116 IF_FREE(theToken, mTokenAllocator);
2118 } else {
2119 IF_FREE(theToken, mTokenAllocator);
2123 } else {
2124 result = kEOF;
2126 return result;
2129 /**
2130 * This method is called to determine whether or not a tag
2131 * of one type can contain a tag of another type.
2133 * @update gess 4/8/98
2134 * @param aParent -- tag enum of parent container
2135 * @param aChild -- tag enum of child container
2136 * @return PR_TRUE if parent can contain child
2138 PRBool
2139 CNavDTD::CanContain(PRInt32 aParent, PRInt32 aChild) const
2141 PRBool result = gHTMLElements[aParent].CanContain((eHTMLTags)aChild, mDTDMode);
2143 #ifdef ALLOW_TR_AS_CHILD_OF_TABLE
2144 if (!result) {
2145 //XXX This vile hack is here to support bug 30378, which allows
2146 //table to contain tr directly in an html32 document.
2147 if ((eHTMLTag_tr == aChild) && (eHTMLTag_table == aParent)) {
2148 result = PR_TRUE;
2151 #endif
2153 if (eHTMLTag_nobr == aChild &&
2154 IsInlineElement(aParent, aParent) &&
2155 HasOpenContainer(eHTMLTag_nobr)) {
2156 return PR_FALSE;
2159 return result;
2163 * This method is called to determine whether or not
2164 * the given childtag is a block element.
2166 * @update gess 6June2000
2167 * @param aChildID -- tag id of child
2168 * @param aParentID -- tag id of parent (or eHTMLTag_unknown)
2169 * @return PR_TRUE if this tag is a block tag
2171 PRBool
2172 CNavDTD::IsBlockElement(PRInt32 aTagID, PRInt32 aParentID) const
2174 eHTMLTags theTag = (eHTMLTags)aTagID;
2176 return (theTag > eHTMLTag_unknown && theTag < eHTMLTag_userdefined) &&
2177 (gHTMLElements[theTag].IsMemberOf(kBlock) ||
2178 gHTMLElements[theTag].IsMemberOf(kBlockEntity) ||
2179 gHTMLElements[theTag].IsMemberOf(kHeading) ||
2180 gHTMLElements[theTag].IsMemberOf(kPreformatted) ||
2181 gHTMLElements[theTag].IsMemberOf(kList));
2185 * This method is called to determine whether or not
2186 * the given childtag is an inline element.
2188 * @update gess 6June2000
2189 * @param aChildID -- tag id of child
2190 * @param aParentID -- tag id of parent (or eHTMLTag_unknown)
2191 * @return PR_TRUE if this tag is an inline tag
2193 PRBool
2194 CNavDTD::IsInlineElement(PRInt32 aTagID, PRInt32 aParentID) const
2196 eHTMLTags theTag = (eHTMLTags)aTagID;
2198 return (theTag > eHTMLTag_unknown && theTag < eHTMLTag_userdefined) &&
2199 (gHTMLElements[theTag].IsMemberOf(kInlineEntity) ||
2200 gHTMLElements[theTag].IsMemberOf(kFontStyle) ||
2201 gHTMLElements[theTag].IsMemberOf(kPhrase) ||
2202 gHTMLElements[theTag].IsMemberOf(kSpecial) ||
2203 gHTMLElements[theTag].IsMemberOf(kFormControl));
2207 * This method is called to determine whether or not
2208 * the necessary intermediate tags should be propagated
2209 * between the given parent and given child.
2211 * @update gess 4/8/98
2212 * @param aParent -- tag enum of parent container
2213 * @param aChild -- tag enum of child container
2214 * @return PR_TRUE if propagation should occur
2216 PRBool
2217 CNavDTD::CanPropagate(eHTMLTags aParent, eHTMLTags aChild,
2218 PRInt32 aParentContains)
2220 PRBool result = PR_FALSE;
2221 if (aParentContains == -1) {
2222 aParentContains = CanContain(aParent, aChild);
2225 if (aParent == aChild) {
2226 return result;
2229 if (nsHTMLElement::IsContainer(aChild)) {
2230 mScratch.Truncate();
2231 if (!gHTMLElements[aChild].HasSpecialProperty(kNoPropagate)) {
2232 if (nsHTMLElement::IsBlockParent(aParent) ||
2233 gHTMLElements[aParent].GetSpecialChildren()) {
2234 result = ForwardPropagate(mScratch, aParent, aChild);
2235 if (!result) {
2236 if (eHTMLTag_unknown != aParent) {
2237 result = BackwardPropagate(mScratch, aParent, aChild);
2238 } else {
2239 result = BackwardPropagate(mScratch, eHTMLTag_html, aChild);
2244 if (mScratch.Length() - 1 > gHTMLElements[aParent].mPropagateRange) {
2245 result = PR_FALSE;
2247 } else {
2248 result = !!aParentContains;
2252 return result;
2257 * This method gets called to determine whether a given
2258 * tag can be omitted from opening. Most cannot.
2260 * @param aParent
2261 * @param aChild
2262 * @param aParentContains
2263 * @return PR_TRUE if given tag can contain other tags
2265 PRBool
2266 CNavDTD::CanOmit(eHTMLTags aParent, eHTMLTags aChild, PRInt32& aParentContains)
2268 eHTMLTags theAncestor = gHTMLElements[aChild].mExcludingAncestor;
2269 if (eHTMLTag_unknown != theAncestor && HasOpenContainer(theAncestor)) {
2270 return PR_TRUE;
2273 theAncestor = gHTMLElements[aChild].mRequiredAncestor;
2274 if (eHTMLTag_unknown != theAncestor) {
2275 // If there's a required ancestor, we only omit if it isn't open and we
2276 // can't get to it through propagation.
2277 return !HasOpenContainer(theAncestor) &&
2278 !CanPropagate(aParent, aChild, aParentContains);
2281 if (gHTMLElements[aParent].CanExclude(aChild)) {
2282 return PR_TRUE;
2285 // Now the obvious test: if the parent can contain the child, don't omit.
2286 if (-1 == aParentContains) {
2287 aParentContains = CanContain(aParent, aChild);
2290 if (aParentContains || aChild == aParent) {
2291 return PR_FALSE;
2294 if (gHTMLElements[aParent].IsBlockEntity() &&
2295 nsHTMLElement::IsInlineEntity(aChild)) {
2296 // Feel free to drop inlines that a block doesn't contain.
2297 return PR_TRUE;
2300 if (gHTMLElements[aParent].HasSpecialProperty(kBadContentWatch)) {
2301 // We can only omit this child if it does not have the kBadContentWatch
2302 // special property.
2303 return !gHTMLElements[aChild].HasSpecialProperty(kBadContentWatch);
2306 if (gHTMLElements[aParent].HasSpecialProperty(kSaveMisplaced)) {
2307 return PR_TRUE;
2310 return PR_FALSE;
2314 * This method gets called to determine whether a given
2315 * tag is itself a container
2317 * @update gess 4/8/98
2318 * @param aTag -- tag to test as a container
2319 * @return PR_TRUE if given tag can contain other tags
2321 PRBool
2322 CNavDTD::IsContainer(PRInt32 aTag) const
2324 return nsHTMLElement::IsContainer((eHTMLTags)aTag);
2328 PRBool
2329 CNavDTD::ForwardPropagate(nsString& aSequence, eHTMLTags aParent,
2330 eHTMLTags aChild)
2332 PRBool result = PR_FALSE;
2334 switch(aParent) {
2335 case eHTMLTag_table:
2336 if (eHTMLTag_tr == aChild || eHTMLTag_td == aChild) {
2337 return BackwardPropagate(aSequence, aParent, aChild);
2339 // Otherwise, intentionally fall through...
2341 case eHTMLTag_tr:
2342 if (CanContain(eHTMLTag_td, aChild)) {
2343 aSequence.Append((PRUnichar)eHTMLTag_td);
2344 result = BackwardPropagate(aSequence, aParent, eHTMLTag_td);
2346 break;
2348 default:
2349 break;
2352 return result;
2355 PRBool
2356 CNavDTD::BackwardPropagate(nsString& aSequence, eHTMLTags aParent,
2357 eHTMLTags aChild) const
2359 eHTMLTags theParent = aParent;
2361 do {
2362 const TagList* theRootTags = gHTMLElements[aChild].GetRootTags();
2363 if (!theRootTags) {
2364 break;
2367 theParent = theRootTags->mTags[0];
2368 NS_ASSERTION(CanContain(theParent, aChild),
2369 "Children must be contained by their root tags");
2371 aChild = theParent;
2372 aSequence.Append((PRUnichar)theParent);
2373 } while (theParent != eHTMLTag_unknown && theParent != aParent);
2375 return aParent == theParent;
2378 PRBool CNavDTD::HasOpenContainer(eHTMLTags aContainer) const
2380 switch (aContainer) {
2381 case eHTMLTag_form:
2382 return !(~mFlags & NS_DTD_FLAG_HAS_OPEN_FORM);
2383 case eHTMLTag_map:
2384 return mOpenMapCount > 0;
2385 default:
2386 return mBodyContext->HasOpenContainer(aContainer);
2390 PRBool
2391 CNavDTD::HasOpenContainer(const eHTMLTags aTagSet[], PRInt32 aCount) const
2393 int theIndex;
2394 int theTopIndex = mBodyContext->GetCount() - 1;
2396 for (theIndex = theTopIndex; theIndex > 0; --theIndex) {
2397 if (FindTagInSet((*mBodyContext)[theIndex], aTagSet, aCount)) {
2398 return PR_TRUE;
2402 return PR_FALSE;
2405 eHTMLTags
2406 CNavDTD::GetTopNode() const
2408 return mBodyContext->Last();
2412 * It is with great trepidation that I offer this method (privately of course).
2413 * The gets called whenever a container gets opened. This methods job is to
2414 * take a look at the (transient) style stack, and open any style containers that
2415 * are there. Of course, we shouldn't bother to open styles that are incompatible
2416 * with our parent container.
2418 * @update gess6/4/98
2419 * @param tag of the container just opened
2420 * @return 0 (for now)
2422 nsresult
2423 CNavDTD::OpenTransientStyles(eHTMLTags aChildTag, PRBool aCloseInvalid)
2425 nsresult result = NS_OK;
2427 // No need to open transient styles in head context - Fix for 41427
2428 if ((mFlags & NS_DTD_FLAG_ENABLE_RESIDUAL_STYLE) &&
2429 eHTMLTag_newline != aChildTag &&
2430 !(mFlags & NS_DTD_FLAG_HAS_OPEN_HEAD)) {
2431 if (CanContain(eHTMLTag_font, aChildTag)) {
2432 PRUint32 theCount = mBodyContext->GetCount();
2433 PRUint32 theLevel = theCount;
2435 // This first loop is used to determine how far up the containment
2436 // hierarchy we go looking for residual styles.
2437 while (1 < theLevel) {
2438 eHTMLTags theParentTag = mBodyContext->TagAt(--theLevel);
2439 if (gHTMLElements[theParentTag].HasSpecialProperty(kNoStyleLeaksIn)) {
2440 break;
2444 mFlags &= ~NS_DTD_FLAG_ENABLE_RESIDUAL_STYLE;
2445 for (; theLevel < theCount; ++theLevel) {
2446 nsEntryStack* theStack = mBodyContext->GetStylesAt(theLevel);
2447 if (theStack) {
2448 // Don't open transient styles if it makes the stack deep, bug 58917.
2449 if (theCount + theStack->mCount >= FONTSTYLE_IGNORE_DEPTH) {
2450 break;
2453 PRInt32 sindex = 0;
2455 nsTagEntry *theEntry = theStack->mEntries;
2456 PRBool isHeadingOpen = HasOpenTagOfType(kHeading, *mBodyContext);
2457 for (sindex = 0; sindex < theStack->mCount; ++sindex) {
2458 nsCParserNode* theNode = (nsCParserNode*)theEntry->mNode;
2459 if (1 == theNode->mUseCount) {
2460 eHTMLTags theNodeTag = (eHTMLTags)theNode->GetNodeType();
2461 if (gHTMLElements[theNodeTag].CanContain(aChildTag, mDTDMode)) {
2462 // XXX The following comment is entirely incoherent.
2463 // We do this too, because this entry differs from the new one
2464 // we're pushing.
2465 theEntry->mParent = theStack;
2466 if (isHeadingOpen) {
2467 // Bug 77352
2468 // The style system needs to identify residual style tags
2469 // within heading tags so that heading tags' size can take
2470 // precedence over the residual style tags' size info..
2471 // *Note: Make sure that this attribute is transient since it
2472 // should not get carried over to cases other than heading.
2473 CAttributeToken theAttrToken(NS_LITERAL_STRING("_moz-rs-heading"),
2474 EmptyString());
2475 theNode->AddAttribute(&theAttrToken);
2476 result = OpenContainer(theNode, theNodeTag, theStack);
2477 theNode->PopAttributeToken();
2478 } else {
2479 result = OpenContainer(theNode, theNodeTag, theStack);
2481 } else if (aCloseInvalid) {
2482 // If the node tag can't contain the child tag, then remove the
2483 // child tag from the style stack
2484 nsCParserNode* node = theStack->Remove(sindex, theNodeTag);
2485 IF_FREE(node, &mNodeAllocator);
2486 --theEntry;
2489 ++theEntry;
2493 mFlags |= NS_DTD_FLAG_ENABLE_RESIDUAL_STYLE;
2497 return result;
2501 * This method gets called when an explicit style close-tag is encountered.
2502 * It results in the style tag id being popped from our internal style stack.
2504 * @update gess6/4/98
2505 * @param
2506 * @return 0 if all went well (which it always does)
2508 void
2509 CNavDTD::PopStyle(eHTMLTags aTag)
2511 if ((mFlags & NS_DTD_FLAG_ENABLE_RESIDUAL_STYLE) &&
2512 nsHTMLElement::IsResidualStyleTag(aTag)) {
2513 nsCParserNode* node = mBodyContext->PopStyle(aTag);
2514 IF_FREE(node, &mNodeAllocator);
2520 * This method does two things: 1st, help construct
2521 * our own internal model of the content-stack; and
2522 * 2nd, pass this message on to the sink.
2524 * @update gess4/22/98
2525 * @param aNode -- next node to be added to model
2527 nsresult
2528 CNavDTD::OpenHTML(const nsCParserNode *aNode)
2530 NS_PRECONDITION(mBodyContext->GetCount() >= 0, kInvalidTagStackPos);
2532 STOP_TIMER();
2533 MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::OpenHTML(), this=%p\n", this));
2535 nsresult result = mSink ? mSink->OpenContainer(*aNode) : NS_OK;
2537 MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::OpenHTML(), this=%p\n", this));
2538 START_TIMER();
2540 // Don't push more than one HTML tag into the stack.
2541 if (mBodyContext->GetCount() == 0) {
2542 mBodyContext->Push(const_cast<nsCParserNode*>(aNode), 0, PR_FALSE);
2545 return result;
2549 * This method does two things: 1st, help construct
2550 * our own internal model of the content-stack; and
2551 * 2nd, pass this message on to the sink.
2552 * @update gess4/6/98
2553 * @param aNode -- next node to be added to model
2554 * @return TRUE if ok, FALSE if error
2556 nsresult
2557 CNavDTD::OpenBody(const nsCParserNode *aNode)
2559 NS_PRECONDITION(mBodyContext->GetCount() >= 0, kInvalidTagStackPos);
2561 nsresult result = NS_OK;
2563 if (!(mFlags & NS_DTD_FLAG_HAD_FRAMESET)) {
2564 mFlags |= NS_DTD_FLAG_HAD_BODY;
2566 STOP_TIMER();
2567 MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::OpenBody(), this=%p\n", this));
2569 // Make sure the head is closed by the time the body is opened.
2570 CloseContainer(eHTMLTag_head, PR_FALSE);
2572 // Now we can open the body.
2573 result = mSink ? mSink->OpenContainer(*aNode) : NS_OK;
2575 MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::OpenBody(), this=%p\n", this));
2576 START_TIMER();
2578 if (!HasOpenContainer(eHTMLTag_body)) {
2579 mBodyContext->Push(const_cast<nsCParserNode*>(aNode), 0, PR_FALSE);
2580 mTokenizer->PrependTokens(mMisplacedContent);
2584 return result;
2588 * This method does two things: 1st, help construct
2589 * our own internal model of the content-stack; and
2590 * 2nd, pass this message on to the sink.
2591 * @update gess4/6/98
2592 * @param aNode -- next node to be added to model
2593 * @param aClosedByStartTag -- ONLY TRUE if the container is being closed by opening of another container.
2594 * @return TRUE if ok, FALSE if error
2596 nsresult
2597 CNavDTD::OpenContainer(const nsCParserNode *aNode,
2598 eHTMLTags aTag,
2599 nsEntryStack* aStyleStack)
2601 NS_PRECONDITION(mBodyContext->GetCount() >= 0, kInvalidTagStackPos);
2603 nsresult result = NS_OK;
2604 PRBool done = PR_TRUE;
2605 PRBool rs_tag = nsHTMLElement::IsResidualStyleTag(aTag);
2606 // We need to open transient styles to encompass the <li> so that the bullets
2607 // inherit the proper colors.
2608 PRBool li_tag = aTag == eHTMLTag_li;
2610 if (rs_tag || li_tag) {
2612 * Here's an interesting problem:
2614 * If there's an <a> on the RS-stack, and you're trying to open
2615 * another <a>, the one on the RS-stack should be discarded.
2617 * I'm updating OpenTransientStyles to throw old <a>'s away.
2619 OpenTransientStyles(aTag, !li_tag);
2622 switch (aTag) {
2623 case eHTMLTag_html:
2624 result = OpenHTML(aNode);
2625 break;
2627 case eHTMLTag_head:
2628 if (!(mFlags & NS_DTD_FLAG_HAS_OPEN_HEAD)) {
2629 mFlags |= NS_DTD_FLAG_HAS_OPEN_HEAD;
2630 done = PR_FALSE;
2632 break;
2634 case eHTMLTag_body:
2636 eHTMLTags theParent = mBodyContext->Last();
2637 if (!gHTMLElements[aTag].IsSpecialParent(theParent)) {
2638 mFlags |= NS_DTD_FLAG_HAS_OPEN_BODY;
2639 result = OpenBody(aNode);
2640 } else {
2641 done = PR_FALSE;
2644 break;
2646 case eHTMLTag_map:
2647 ++mOpenMapCount;
2648 done = PR_FALSE;
2649 break;
2651 case eHTMLTag_form:
2652 // Discard nested forms - bug 72639
2653 if (!(mFlags & NS_DTD_FLAG_HAS_OPEN_FORM)) {
2654 mFlags |= NS_DTD_FLAG_HAS_OPEN_FORM;
2655 result = mSink ? mSink->OpenContainer(*aNode) : NS_OK;
2657 break;
2659 case eHTMLTag_frameset:
2660 // Make sure that the head is closed before we try to open this frameset.
2661 CloseContainer(eHTMLTag_head, PR_FALSE);
2663 // Now that the head is closed, continue on with opening this frameset.
2664 mFlags |= NS_DTD_FLAG_HAD_FRAMESET;
2665 done = PR_FALSE;
2666 break;
2668 case eHTMLTag_noembed:
2669 // <noembed> is unconditionally alternate content.
2670 done = PR_FALSE;
2671 mFlags |= NS_DTD_FLAG_ALTERNATE_CONTENT;
2672 break;
2674 case eHTMLTag_noscript:
2675 // We want to make sure that OpenContainer gets called below since we're
2676 // not doing it here
2677 done = PR_FALSE;
2679 if (mFlags & NS_IPARSER_FLAG_SCRIPT_ENABLED) {
2680 // XXX This flag doesn't currently do anything (and would be
2681 // insufficient if it did).
2682 mFlags |= NS_DTD_FLAG_ALTERNATE_CONTENT;
2684 break;
2686 case eHTMLTag_iframe: // Bug 84491
2687 case eHTMLTag_noframes:
2688 done = PR_FALSE;
2689 if (mFlags & NS_IPARSER_FLAG_FRAMES_ENABLED) {
2690 mFlags |= NS_DTD_FLAG_ALTERNATE_CONTENT;
2692 break;
2694 default:
2695 done = PR_FALSE;
2696 break;
2699 if (!done) {
2700 STOP_TIMER();
2701 MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::OpenContainer(), this=%p\n", this));
2703 result = mSink ? mSink->OpenContainer(*aNode) : NS_OK;
2705 MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::OpenContainer(), this=%p\n", this));
2706 START_TIMER();
2708 // For residual style tags rs_tag will be true and hence
2709 // the body context will hold an extra reference to the node.
2710 mBodyContext->Push(const_cast<nsCParserNode*>(aNode), aStyleStack, rs_tag);
2713 return result;
2717 * This method does two things: 1st, help construct
2718 * our own internal model of the content-stack; and
2719 * 2nd, pass this message on to the sink.
2720 * @update gess4/6/98
2721 * @param aTag -- id of tag to be closed
2722 * @return TRUE if ok, FALSE if error
2724 nsresult
2725 CNavDTD::CloseContainer(const eHTMLTags aTag, PRBool aMalformed)
2727 nsresult result = NS_OK;
2728 PRBool done = PR_TRUE;
2730 switch (aTag) {
2731 case eHTMLTag_head:
2732 if (mFlags & NS_DTD_FLAG_HAS_OPEN_HEAD) {
2733 mFlags &= ~NS_DTD_FLAG_HAS_OPEN_HEAD;
2734 if (mBodyContext->Last() == eHTMLTag_head) {
2735 mBodyContext->Pop();
2736 } else {
2737 // This else can happen because CloseContainer is called both directly
2738 // and from CloseContainersTo. CloseContainersTo pops the current tag
2739 // off of the stack before calling CloseContainer.
2740 NS_ASSERTION(mBodyContext->LastOf(eHTMLTag_head) == kNotFound,
2741 "Closing the wrong tag");
2743 done = PR_FALSE;
2745 break;
2747 case eHTMLTag_map:
2748 if (mOpenMapCount) {
2749 mOpenMapCount--;
2750 done = PR_FALSE;
2752 break;
2754 case eHTMLTag_form:
2755 if (mFlags & NS_DTD_FLAG_HAS_OPEN_FORM) {
2756 mFlags &= ~NS_DTD_FLAG_HAS_OPEN_FORM;
2757 done = PR_FALSE;
2759 break;
2761 case eHTMLTag_iframe:
2762 case eHTMLTag_noembed:
2763 case eHTMLTag_noscript:
2764 case eHTMLTag_noframes:
2765 // Switch from alternate content state to regular state.
2766 mFlags &= ~NS_DTD_FLAG_ALTERNATE_CONTENT;
2768 // falling thro' intentionally....
2769 default:
2770 done = PR_FALSE;
2773 if (!done) {
2774 STOP_TIMER();
2775 MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::CloseContainer(), this=%p\n", this));
2777 if (mSink) {
2778 result = !aMalformed
2779 ? mSink->CloseContainer(aTag)
2780 : mSink->CloseMalformedContainer(aTag);
2783 // If we were dealing with a head container in the body, make sure to
2784 // close the head context now, so that body content doesn't get sucked
2785 // into the head.
2786 if (mBodyContext->GetCount() == mHeadContainerPosition) {
2787 mHeadContainerPosition = -1;
2788 nsresult headresult = CloseContainer(eHTMLTag_head, PR_FALSE);
2790 // Note: we could be assigning NS_OK into NS_OK here, but that's ok.
2791 // This test is to avoid a successful CloseHead result stomping over a
2792 // request to block the parser.
2793 if (NS_SUCCEEDED(result)) {
2794 result = headresult;
2798 MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::CloseContainer(), this=%p\n", this));
2799 START_TIMER();
2802 return result;
2806 * This method does two things: 1st, help construct
2807 * our own internal model of the content-stack; and
2808 * 2nd, pass this message on to the sink.
2809 * @update gess4/6/98
2810 * @param anIndex
2811 * @param aTag
2812 * @param aClosedByStartTag -- if TRUE, then we're closing something because a start tag caused it
2813 * @return TRUE if ok, FALSE if error
2815 nsresult
2816 CNavDTD::CloseContainersTo(PRInt32 anIndex, eHTMLTags aTarget,
2817 PRBool aClosedByStartTag)
2819 NS_PRECONDITION(mBodyContext->GetCount() > 0, kInvalidTagStackPos);
2820 nsresult result = NS_OK;
2822 if (anIndex < mBodyContext->GetCount() && anIndex >= 0) {
2823 PRInt32 count = 0;
2824 while ((count = mBodyContext->GetCount()) > anIndex) {
2825 nsEntryStack* theChildStyleStack = 0;
2826 eHTMLTags theTag = mBodyContext->Last();
2827 nsCParserNode* theNode = mBodyContext->Pop(theChildStyleStack);
2828 result = CloseContainer(theTag, PR_FALSE);
2830 PRBool theTagIsStyle = nsHTMLElement::IsResidualStyleTag(theTag);
2831 // If the current tag cannot leak out then we shouldn't leak out of the target - Fix 40713
2832 PRBool theStyleDoesntLeakOut = gHTMLElements[theTag].HasSpecialProperty(kNoStyleLeaksOut);
2833 if (!theStyleDoesntLeakOut) {
2834 theStyleDoesntLeakOut = gHTMLElements[aTarget].HasSpecialProperty(kNoStyleLeaksOut);
2837 // Do not invoke residual style handling when dealing with
2838 // alternate content. This fixed bug 25214.
2839 if (theTagIsStyle && !(mFlags & NS_DTD_FLAG_ALTERNATE_CONTENT)) {
2840 NS_ASSERTION(theNode, "residual style node should not be null");
2841 if (!theNode) {
2842 if (theChildStyleStack) {
2843 mBodyContext->PushStyles(theChildStyleStack);
2845 return NS_OK;
2848 PRBool theTargetTagIsStyle = nsHTMLElement::IsResidualStyleTag(aTarget);
2849 if (aClosedByStartTag) {
2850 // Handle closure due to new start tag.
2851 // The cases we're handing here:
2852 // 1. <body><b><DIV> //<b> gets pushed onto <body>.mStyles.
2853 // 2. <body><a>text<a> //in this case, the target matches, so don't push style
2854 if (theNode->mUseCount == 0) {
2855 if (theTag != aTarget) {
2856 if (theChildStyleStack) {
2857 theChildStyleStack->PushFront(theNode);
2858 } else {
2859 mBodyContext->PushStyle(theNode);
2862 } else if (theTag == aTarget && !gHTMLElements[aTarget].CanContainSelf()) {
2863 //here's a case we missed: <a><div>text<a>text</a></div>
2864 //The <div> pushes the 1st <a> onto the rs-stack, then the 2nd <a>
2865 //pops the 1st <a> from the rs-stack altogether.
2866 nsCParserNode* node = mBodyContext->PopStyle(theTag);
2867 IF_FREE(node, &mNodeAllocator);
2870 if (theChildStyleStack) {
2871 mBodyContext->PushStyles(theChildStyleStack);
2873 } else {
2875 * if you're here, then we're dealing with the closure of tags
2876 * caused by a close tag (as opposed to an open tag).
2877 * At a minimum, we should consider pushing residual styles up
2878 * up the stack or popping and recycling displaced nodes.
2880 * Known cases:
2881 * 1. <body><b><div>text</DIV>
2882 * Here the <b> will leak into <div> (see case given above), and
2883 * when <div> closes the <b> is dropped since it's already residual.
2885 * 2. <body><div><b>text</div>
2886 * Here the <b> will leak out of the <div> and get pushed onto
2887 * the RS stack for the <body>, since it originated in the <div>.
2889 * 3. <body><span><b>text</span>
2890 * In this case, the the <b> get's pushed onto the style stack.
2891 * Later we deal with RS styles stored on the <span>
2893 * 4. <body><span><b>text</i>
2894 * Here we the <b>is closed by a (synonymous) style tag.
2895 * In this case, the <b> is simply closed.
2897 if (theChildStyleStack) {
2898 if (!theStyleDoesntLeakOut) {
2899 if (theTag != aTarget) {
2900 if (theNode->mUseCount == 0) {
2901 theChildStyleStack->PushFront(theNode);
2903 } else if (theNode->mUseCount == 1) {
2904 // This fixes bug 30885,29626.
2905 // Make sure that the node, which is about to
2906 // get released does not stay on the style stack...
2907 // Also be sure to remove the correct style off the
2908 // style stack. - Ref. bug 94208.
2909 // Ex <FONT><B><I></FONT><FONT></B></I></FONT>
2910 // Make sure that </B> removes B off the style stack.
2911 mBodyContext->RemoveStyle(theTag);
2913 mBodyContext->PushStyles(theChildStyleStack);
2914 } else{
2915 IF_DELETE(theChildStyleStack, &mNodeAllocator);
2917 } else if (theNode->mUseCount == 0) {
2918 // The old version of this only pushed if the targettag wasn't
2919 // style. But that misses this case: <font><b>text</font>, where
2920 // the b should leak.
2921 if (aTarget != theTag) {
2922 mBodyContext->PushStyle(theNode);
2924 } else {
2925 // Ah, at last, the final case. If you're here, then we just popped
2926 // a style tag that got onto that tag stack from a stylestack
2927 // somewhere. Pop it from the stylestack if the target is also a
2928 // style tag. Make sure to remove the matching style. In the
2929 // following example:
2930 // <FONT><B><I></FONT><FONT color=red></B></I></FONT>
2931 // make sure that </I> does not remove
2932 // <FONT color=red> off the style stack. - bug 94208
2933 if (theTargetTagIsStyle && theTag == aTarget) {
2934 mBodyContext->RemoveStyle(theTag);
2938 } else {
2939 // The tag is not a style tag.
2940 if (theChildStyleStack) {
2941 if (theStyleDoesntLeakOut) {
2942 IF_DELETE(theChildStyleStack, &mNodeAllocator);
2943 } else {
2944 mBodyContext->PushStyles(theChildStyleStack);
2948 IF_FREE(theNode, &mNodeAllocator);
2951 return result;
2955 * This method does two things: 1st, help construct
2956 * our own internal model of the content-stack; and
2957 * 2nd, pass this message on to the sink.
2958 * @update gess4/6/98
2959 * @param aTag --
2960 * @param aClosedByStartTag -- ONLY TRUE if the container is being closed by opening of another container.
2961 * @return TRUE if ok, FALSE if error
2963 nsresult
2964 CNavDTD::CloseContainersTo(eHTMLTags aTag, PRBool aClosedByStartTag)
2966 NS_PRECONDITION(mBodyContext->GetCount() > 0, kInvalidTagStackPos);
2968 PRInt32 pos = mBodyContext->LastOf(aTag);
2970 if (kNotFound != pos) {
2971 // The tag is indeed open, so close it.
2972 return CloseContainersTo(pos, aTag, aClosedByStartTag);
2975 eHTMLTags theTopTag = mBodyContext->Last();
2977 PRBool theTagIsSynonymous = (nsHTMLElement::IsResidualStyleTag(aTag) &&
2978 nsHTMLElement::IsResidualStyleTag(theTopTag)) ||
2979 (gHTMLElements[aTag].IsMemberOf(kHeading) &&
2980 gHTMLElements[theTopTag].IsMemberOf(kHeading));
2982 if (theTagIsSynonymous) {
2983 // If you're here, it's because we're trying to close one tag,
2984 // but a different (synonymous) one is actually open. Because this is NAV4x
2985 // compatibililty mode, we must close the one that's really open.
2986 aTag = theTopTag;
2987 pos = mBodyContext->LastOf(aTag);
2988 if (kNotFound != pos) {
2989 // The tag is indeed open, so close it.
2990 return CloseContainersTo(pos, aTag, aClosedByStartTag);
2994 nsresult result = NS_OK;
2995 const TagList* theRootTags = gHTMLElements[aTag].GetRootTags();
2996 // XXX Can we just bail if !theRootTags? Does that ever happen?
2997 eHTMLTags theParentTag = theRootTags ? theRootTags->mTags[0] : eHTMLTag_unknown;
2998 pos = mBodyContext->LastOf(theParentTag);
2999 if (kNotFound != pos) {
3000 // The parent container is open, so close it instead
3001 result = CloseContainersTo(pos + 1, aTag, aClosedByStartTag);
3003 return result;
3007 * This method does two things: 1st, help construct
3008 * our own internal model of the content-stack; and
3009 * 2nd, pass this message on to the sink.
3010 * @update gess4/6/98
3011 * @param aNode -- next node to be added to model
3012 * @return error code; 0 means OK
3014 nsresult
3015 CNavDTD::AddLeaf(const nsIParserNode *aNode)
3017 nsresult result = NS_OK;
3019 if (mSink) {
3020 eHTMLTags theTag = (eHTMLTags)aNode->GetNodeType();
3021 OpenTransientStyles(theTag);
3023 STOP_TIMER();
3024 MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::AddLeaf(), this=%p\n", this));
3026 result = mSink->AddLeaf(*aNode);
3028 MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::AddLeaf(), this=%p\n", this));
3029 START_TIMER();
3032 return result;
3036 * Call this method ONLY when you want to write a leaf
3037 * into the head container.
3039 * @update gess 03/14/99
3040 * @param aNode -- next node to be added to model
3041 * @return error code; 0 means OK
3043 nsresult
3044 CNavDTD::AddHeadContent(nsIParserNode *aNode)
3046 nsresult result = NS_OK;
3048 static eHTMLTags gNoXTags[] = { eHTMLTag_noembed, eHTMLTag_noframes };
3050 eHTMLTags theTag = (eHTMLTags)aNode->GetNodeType();
3052 // XXX - SCRIPT inside NOTAGS should not get executed unless the pref.
3053 // says so. Since we don't have this support yet..lets ignore the
3054 // SCRIPT inside NOTAGS. Ref Bug 25880.
3055 if (eHTMLTag_meta == theTag || eHTMLTag_script == theTag) {
3056 if (HasOpenContainer(gNoXTags, NS_ARRAY_LENGTH(gNoXTags))) {
3057 return result;
3061 if (mSink) {
3062 STOP_TIMER();
3063 MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::AddHeadContent(), this=%p\n", this));
3065 // Make sure the head is opened.
3066 if (!(mFlags & NS_DTD_FLAG_HAS_OPEN_HEAD)) {
3067 mFlags |= NS_DTD_FLAG_HAS_OPEN_HEAD;
3068 mBodyContext->PushTag(eHTMLTag_head);
3069 result = mSink->OpenHead();
3072 // Note: userdefined tags in the head are treated as leaves.
3073 if (!nsHTMLElement::IsContainer(theTag) || theTag == eHTMLTag_userdefined) {
3074 result = mSink->AddLeaf(*aNode);
3076 if (mFlags & NS_DTD_FLAG_HAS_MAIN_CONTAINER) {
3077 // Close the head now so that body content doesn't get sucked into it.
3078 CloseContainer(eHTMLTag_head, PR_FALSE);
3080 } else {
3081 if ((mFlags & NS_DTD_FLAG_HAS_MAIN_CONTAINER) &&
3082 mHeadContainerPosition == -1) {
3083 // Keep track of this so that we know when to close the head, when
3084 // this tag is done with.
3085 mHeadContainerPosition = mBodyContext->GetCount();
3088 mBodyContext->Push(static_cast<nsCParserNode*>(aNode), nsnull,
3089 PR_FALSE);
3091 // Note: The head context is already opened.
3092 result = mSink->OpenContainer(*aNode);
3095 MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::AddHeadContent(), this=%p\n", this));
3096 START_TIMER();
3099 return result;
3102 void
3103 CNavDTD::CreateContextStackFor(eHTMLTags aParent, eHTMLTags aChild)
3105 mScratch.Truncate();
3107 PRBool result = ForwardPropagate(mScratch, aParent, aChild);
3109 if (!result) {
3110 if (eHTMLTag_unknown == aParent) {
3111 result = BackwardPropagate(mScratch, eHTMLTag_html, aChild);
3112 } else if (aParent != aChild) {
3113 // Don't even bother if we're already inside a similar element...
3114 result = BackwardPropagate(mScratch, aParent, aChild);
3118 if (!result) {
3119 return;
3122 PRInt32 theLen = mScratch.Length();
3123 eHTMLTags theTag = (eHTMLTags)mScratch[--theLen];
3125 // Now, build up the stack according to the tags.
3126 while (theLen) {
3127 theTag = (eHTMLTags)mScratch[--theLen];
3129 #ifdef ALLOW_TR_AS_CHILD_OF_TABLE
3130 if (eHTML3_Quirks == mDocType && eHTMLTag_tbody == theTag) {
3131 // The prev. condition prevents us from emitting tbody in html3.2 docs; fix bug 30378
3132 continue;
3134 #endif
3136 // Note: These tokens should all wind up on contextstack, so don't recycle
3137 // them.
3138 CToken *theToken = mTokenAllocator->CreateTokenOfType(eToken_start, theTag);
3139 HandleToken(theToken, mParser);
3143 nsresult
3144 CNavDTD::WillResumeParse(nsIContentSink* aSink)
3146 STOP_TIMER();
3147 MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::WillResumeParse(), this=%p\n", this));
3149 nsresult result = aSink ? aSink->WillResume() : NS_OK;
3151 MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::WillResumeParse(), this=%p\n", this));
3152 START_TIMER();
3154 return result;
3157 nsresult
3158 CNavDTD::WillInterruptParse(nsIContentSink* aSink)
3160 STOP_TIMER();
3161 MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::WillInterruptParse(), this=%p\n", this));
3163 nsresult result = aSink ? aSink->WillInterrupt() : NS_OK;
3165 MOZ_TIMER_DEBUGLOG(("Start: Parse Time: CNavDTD::WillInterruptParse(), this=%p\n", this));
3166 START_TIMER();
3168 return result;