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