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 ***** */
39 //#define ALLOW_TR_AS_CHILD_OF_TABLE //by setting this to true, TR is allowable directly in TABLE.
44 #include "nsHTMLTokens.h"
47 #include "nsIParser.h"
48 #include "nsIHTMLContentSink.h"
49 #include "nsScanner.h"
54 #include "nsDTDUtils.h"
55 #include "nsHTMLTokenizer.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"
65 #include "nsIServiceManager.h"
68 #include "nsLoggingSink.h"
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
);
80 static const char kNullToken
[] = "Error: Null token given";
81 static const char kInvalidTagStackPos
[] = "Error: invalid tag stack position";
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);
96 # define START_TIMER()
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
)
121 NS_IMPL_CYCLE_COLLECTING_ADDREF(CNavDTD
)
122 NS_IMPL_CYCLE_COLLECTING_RELEASE(CNavDTD
)
124 NS_IMPL_CYCLE_COLLECTION_1(CNavDTD
, mSink
)
127 : mMisplacedContent(0),
129 mBodyContext(new nsDTDContext()),
133 mDTDMode(eDTDMode_quirks
),
134 mDocType(eHTML3_Quirks
), // why not eHTML_Quirks?
135 mParserCommand(eViewNormal
),
138 mHeadContainerPosition(-1),
139 mFlags(NS_DTD_FLAG_NONE
)
145 static nsLoggingSink
*
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
;
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
;
185 nsLoggingSink
*theLogSink
= GetLoggingSink();
186 if (mSink
== theLogSink
) {
187 theLogSink
->ReleaseProxySink();
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
;
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
) {
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));
219 if (NS_SUCCEEDED(result
) && !mSink
) {
220 mSink
= do_QueryInterface(aSink
, &result
);
221 if (NS_FAILED(result
)) {
222 mFlags
|= NS_DTD_FLAG_STOP_PARSING
;
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.
231 nsLoggingSink
*theLogSink
= GetLoggingSink();
233 theLogSink
->SetProxySink(mSink
);
239 PRBool enabled
= PR_TRUE
;
240 mSink
->IsEnabled(eHTMLTag_frameset
, &enabled
);
242 mFlags
|= NS_IPARSER_FLAG_FRAMES_ENABLED
;
245 mSink
->IsEnabled(eHTMLTag_script
, &enabled
);
247 mFlags
|= NS_IPARSER_FLAG_SCRIPT_ENABLED
;
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
) {
270 nsITokenizer
* oldTokenizer
= mTokenizer
;
272 mTokenizer
= aTokenizer
;
273 mParser
= (nsParser
*)aParser
;
274 mTokenAllocator
= mTokenizer
->GetTokenAllocator();
277 return (mFlags
& NS_DTD_FLAG_STOP_PARSING
)
278 ? NS_ERROR_HTMLPARSER_STOPPARSING
282 if (mBodyContext
->GetCount() == 0) {
284 if (ePlainText
== mDocType
) {
286 mTokenAllocator
->CreateTokenOfType(eToken_start
, eHTMLTag_pre
);
288 mTokenizer
->PushTokenFront(tempToken
);
292 // Always open a body if frames are disabled.
293 if (!(mFlags
& NS_IPARSER_FLAG_FRAMES_ENABLED
)) {
295 mTokenAllocator
->CreateTokenOfType(eToken_start
,
297 NS_LITERAL_STRING("body"));
299 mTokenizer
->PushTokenFront(tempToken
);
303 // If the content model is empty, then begin by opening <html>.
304 CStartToken
* theToken
= (CStartToken
*)mTokenizer
->GetTokenAt(0);
306 eHTMLTags theTag
= (eHTMLTags
)theToken
->GetTypeID();
307 eHTMLTokenTypes theType
= eHTMLTokenTypes(theToken
->GetTokenType());
308 if (theTag
!= eHTMLTag_html
|| theType
!= eToken_start
) {
310 mTokenAllocator
->CreateTokenOfType(eToken_start
,
312 NS_LITERAL_STRING("html"));
314 mTokenizer
->PushTokenFront(tempToken
);
319 mTokenAllocator
->CreateTokenOfType(eToken_start
,
321 NS_LITERAL_STRING("html"));
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();
336 result
= HandleToken(theToken
, aParser
);
338 result
= NS_ERROR_HTMLPARSER_STOPPARSING
;
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
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
;
362 mTokenizer
= oldTokenizer
;
367 CNavDTD::BuildNeglectedTarget(eHTMLTags aTarget
,
368 eHTMLTokenTypes aType
,
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
) {
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
);
385 CNavDTD::DidBuildModel(nsresult anErrorCode
,
388 nsIContentSink
* aSink
)
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.
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");
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
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();
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();
472 mFlags
|= NS_DTD_FLAG_STOP_PARSING
;
476 NS_IMETHODIMP_(PRInt32
)
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.
491 DoesRequireBody(CToken
* aToken
, nsITokenizer
* aTokenizer
)
493 PRBool result
= PR_FALSE
;
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"))) {
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.
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()) !=
564 CAttributeToken
* attrToken
=
565 static_cast<CAttributeToken
*>(aTokenizer
->GetTokenAt(i
));
566 if (!attrToken
->GetKey().LowerCaseEqualsLiteral("type")) {
570 return ValueIsHidden(attrToken
->GetValue());
577 * Returns whether or not there is a tag of type aType open on aContext.
580 HasOpenTagOfType(PRInt32 aType
, const nsDTDContext
& aContext
)
582 PRInt32 count
= aContext
.GetCount();
584 while (--count
>= 0) {
585 if (gHTMLElements
[aContext
.TagAt(count
)].IsMemberOf(aType
)) {
594 CNavDTD::HandleToken(CToken
* aToken
, nsIParser
* aParser
)
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
);
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
646 mFlags
&= ~NS_DTD_FLAG_MISPLACED_CONTENT
;
648 result
= HandleSavedTokens(mBodyContext
->mContextTopIndex
);
649 NS_ENSURE_SUCCESS(result
, result
);
651 mBodyContext
->mContextTopIndex
= -1;
653 PushIntoMisplacedStack(theToken
);
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.
666 case eHTMLTag_noframes
:
667 case eHTMLTag_script
:
668 case eHTMLTag_doctypeDecl
:
669 case eHTMLTag_instruction
:
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
&&
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.
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.
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
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 "
732 PushIntoMisplacedStack(current
);
735 // XXX Add code to also collect incorrect attributes on the
739 if (DoesRequireBody(aToken
, mTokenizer
)) {
740 CToken
* theBodyToken
=
741 mTokenAllocator
->CreateTokenOfType(eToken_start
,
743 NS_LITERAL_STRING("body"));
744 result
= HandleToken(theBodyToken
, aParser
);
753 mParser
= (nsParser
*)aParser
;
758 case eToken_whitespace
:
760 result
= HandleStartToken(theToken
);
764 result
= HandleEndToken(theToken
);
767 case eToken_cdatasection
:
769 case eToken_markupDecl
:
770 result
= HandleCommentToken(theToken
);
774 result
= HandleEntityToken(theToken
);
777 case eToken_attribute
:
778 result
= HandleAttributeToken(theToken
);
781 case eToken_instruction
:
782 result
= HandleProcessingInstructionToken(theToken
);
785 case eToken_doctypeDecl
:
786 result
= HandleDocTypeDeclToken(theToken
);
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
) {
805 CNavDTD::DidHandleStartTag(nsIParserNode
& aNode
, eHTMLTags aChildTag
)
807 nsresult result
= NS_OK
;
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
840 CNavDTD::LastOf(eHTMLTags aTagSet
[], PRInt32 aCount
) const
842 for (PRInt32 theIndex
= mBodyContext
->GetCount() - 1; theIndex
>= 0;
844 if (FindTagInSet((*mBodyContext
)[theIndex
], aTagSet
, aCount
)) {
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();
865 const TagList
* theRootTags
= gHTMLElements
[aChildTag
].GetRootTags();
866 const TagList
* theSpecialParents
=
867 gHTMLElements
[aChildTag
].GetSpecialParents();
870 PRInt32 theRootIndex
= LastOf(aContext
, *theRootTags
);
871 PRInt32 theSPIndex
= theSpecialParents
872 ? LastOf(aContext
, *theSpecialParents
)
874 PRInt32 theChildIndex
=
875 nsHTMLElement::GetIndexOfChildOrSynonym(aContext
, aChildTag
);
876 PRInt32 theTargetIndex
= (theRootIndex
> theSPIndex
)
880 if (theTargetIndex
== theCount
-1 ||
881 (theTargetIndex
== theChildIndex
&&
882 gHTMLElements
[aChildTag
].CanContainSelf())) {
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
)) {
901 } else if (FindTagInSet(theParentTag
, gTableElements
,
902 NS_ARRAY_LENGTH(gTableElements
))) {
903 // Added this to catch a case we missed; bug 57173.
915 enum eProcessRule
{ eNormalOp
, eLetInlineContainBlock
};
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
;
934 eHTMLTags theParentTag
= mBodyContext
->TagAt(--theIndex
);
935 if (theParentTag
== eHTMLTag_userdefined
) {
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
));
961 // Precompute containment, and pass it to CanOmit()...
963 isHiddenInputInsideTableElement
|| CanContain(theParentTag
, aChildTag
);
964 if (!isHiddenInputInsideTableElement
&&
965 CanOmit(theParentTag
, aChildTag
, theParentContains
)) {
966 HandleOmittedTag(aToken
, aChildTag
, theParentTag
, aNode
);
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
;
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
,
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>
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.
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
,
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
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
;
1058 CloseContainersTo(theIndex
, aChildTag
, PR_TRUE
);
1064 CreateContextStackFor(theParentTag
, aChildTag
);
1065 theIndex
= mBodyContext
->GetCount();
1070 case eLetInlineContainBlock
:
1071 // Break out of this loop and open the block.
1072 theParentContains
= theChildAgrees
= PR_TRUE
;
1076 NS_NOTREACHED("Invalid rule detected");
1078 } while (!(theParentContains
&& theChildAgrees
));
1081 if (theChildIsContainer
) {
1082 result
= OpenContainer(aNode
, aChildTag
);
1084 result
= AddLeaf(aNode
);
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
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
);
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));
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();
1152 theAttrToken
->SetNewlineCount(0);
1153 aDeque
.Push(theAttrToken
);
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
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
),
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
;
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
;
1209 // Hold on to this token for later use. Ref Bug. 53695
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
1226 CNavDTD::HandleKeyGen(nsIParserNode
* aNode
)
1228 nsresult result
= NS_OK
;
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
,
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
,
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
,
1263 NS_ENSURE_TRUE(theToken
, NS_ERROR_OUT_OF_MEMORY
);
1264 mTokenizer
->PushTokenFront(theToken
);
1266 theToken
= mTokenAllocator
->CreateTokenOfType(eToken_start
,
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
,
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
1286 for (theIndex
= theAttrCount
; theIndex
> 0; --theIndex
) {
1287 mTokenizer
->PushTokenFront(((nsCParserNode
*)aNode
)->PopAttributeToken());
1290 theToken
= mTokenAllocator
->CreateTokenOfType(eToken_start
,
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
);
1305 CNavDTD::IsAlternateTag(eHTMLTags aTag
)
1308 case eHTMLTag_noembed
:
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;
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
) {
1349 if (mBodyContext
->GetCount() > 0) {
1350 result
= OpenContainer(theNode
, theChildTag
);
1351 isTokenHandled
= PR_TRUE
;
1356 if (mFlags
& NS_DTD_FLAG_HAS_OPEN_BODY
) {
1357 result
= OpenContainer(theNode
, theChildTag
);
1358 isTokenHandled
=PR_TRUE
;
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
;
1376 PRBool isExclusive
= PR_FALSE
;
1377 theHeadIsParent
= nsHTMLElement::IsChildOfHead(theChildTag
, isExclusive
);
1379 switch (theChildTag
) {
1381 if (!mOpenMapCount
) {
1382 isTokenHandled
= PR_TRUE
;
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));
1397 case eHTMLTag_image
:
1398 aToken
->SetTypeID(theChildTag
= eHTMLTag_img
);
1401 case eHTMLTag_keygen
:
1402 result
= HandleKeyGen(theNode
);
1403 isTokenHandled
= PR_TRUE
;
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
1410 // XXX Where does the script go in a frameset document?
1411 isExclusive
= !(mFlags
& NS_DTD_FLAG_HAD_BODY
);
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
&&
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
);
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.
1453 IF_FREE(theNode
, &mNodeAllocator
);
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
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;
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
1494 * @param aTag -- tag enum of child to be tested
1495 * @return PR_TRUE if autoclosure should occur
1498 FindAutoCloseTargetForEndTag(eHTMLTags aCurrentTag
, nsDTDContext
& aContext
,
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();
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!
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
,
1560 return eHTMLTag_unknown
;
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
,
1574 return eHTMLTag_unknown
;
1578 StripWSFollowingTag(eHTMLTags aChildTag
, nsITokenizer
* aTokenizer
,
1579 nsTokenAllocator
* aTokenAllocator
, PRInt32
& aNewlineCount
)
1581 if (!aTokenizer
|| !aTokenAllocator
) {
1585 CToken
* theToken
= aTokenizer
->PeekToken();
1588 eHTMLTokenTypes theType
= eHTMLTokenTypes(theToken
->GetTokenType());
1591 case eToken_newline
:
1595 case eToken_whitespace
:
1596 theToken
= aTokenizer
->PopToken();
1597 IF_FREE(theToken
, aTokenAllocator
);
1599 theToken
= aTokenizer
->PeekToken();
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
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
) {
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
;
1646 result
= CloseContainer(eHTMLTag_form
, PR_FALSE
);
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
1656 CToken
* theToken
= mTokenAllocator
->CreateTokenOfType(eToken_start
,
1658 result
= HandleToken(theToken
, mParser
);
1665 StripWSFollowingTag(theChildTag
, mTokenizer
, mTokenAllocator
,
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");
1682 mBodyContext
->Pop();
1683 result
= CloseContainer(eHTMLTag_script
, aToken
->IsInError());
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
);
1693 eHTMLTags theParentTag
= mBodyContext
->Last();
1695 // First open transient styles, so that we see any autoclosed style
1697 if (nsHTMLElement::IsResidualStyleTag(theChildTag
)) {
1698 result
= OpenTransientStyles(theChildTag
);
1699 if (NS_FAILED(result
)) {
1705 nsHTMLElement::GetIndexOfChildOrSynonym(*mBodyContext
,
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
)) {
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
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.
1747 mTokenizer
->PushTokenFront(aToken
);
1748 mTokenizer
->PushTokenFront(theStartToken
);
1750 // Oops, we're in misplaced content. Handle these tokens
1751 // directly instead of trying to push them onto the tokenizer
1753 result
= HandleToken(theStartToken
, mParser
);
1754 NS_ENSURE_SUCCESS(result
, result
);
1757 result
= HandleToken(aToken
, mParser
);
1763 if (result
== NS_OK
) {
1764 eHTMLTags theTarget
=
1765 FindAutoCloseTargetForEndTag(theChildTag
, *mBodyContext
,
1767 if (eHTMLTag_unknown
!= theTarget
) {
1768 result
= CloseContainersTo(theTarget
, PR_FALSE
);
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
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
;
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.
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));
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();
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?
1845 for (PRInt32 j
= 0; j
< attrCount
; ++j
) {
1846 CToken
* theAttrToken
= (CToken
*)mMisplacedContent
.PopFront();
1848 temp
.Push(theAttrToken
);
1852 mTokenizer
->PrependTokens(temp
);
1854 if (eToken_end
== theToken
->GetTokenType()) {
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
,
1862 PRInt32 theIndex
= closed
!= eHTMLTag_unknown
1863 ? mBodyContext
->LastOf(closed
)
1866 if (theIndex
!= kNotFound
&&
1867 theIndex
<= mBodyContext
->mContextTopIndex
) {
1868 IF_FREE(theToken
, mTokenAllocator
);
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
1877 result
= HandleToken(theToken
, mParser
);
1880 if (theTopIndex
!= mBodyContext
->GetCount()) {
1881 CloseContainersTo(theTopIndex
, mBodyContext
->TagAt(theTopIndex
),
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
);
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));
1896 mFlags
&= ~NS_DTD_FLAG_IN_MISPLACED_CONTENT
;
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
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.
1926 nsAutoString entityName
;
1927 entityName
.AssignLiteral("&");
1928 entityName
.Append(theStr
);
1929 theToken
= mTokenAllocator
->CreateTokenOfType(eToken_text
, eHTMLTag_text
,
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
);
1946 result
= AddLeaf(theNode
);
1948 IF_FREE(theNode
, &mNodeAllocator
);
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
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
);
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));
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
1995 CNavDTD::HandleAttributeToken(CToken
* aToken
)
1997 NS_ERROR("attribute encountered -- this shouldn't happen.");
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
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
);
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));
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
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
);
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));
2077 * Retrieve the attributes for this node, and add then into
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)
2086 CNavDTD::CollectAttributes(nsIParserNode
*aNode
, eHTMLTags aTag
, PRInt32 aCount
)
2089 nsresult result
= NS_OK
;
2090 int theAvailTokenCount
= mTokenizer
->GetCount();
2092 if (aCount
<= theAvailTokenCount
) {
2094 for (attr
= 0; attr
< aCount
; ++attr
) {
2095 theToken
= mTokenizer
->PopToken();
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
);
2106 if (!IsParserInDocWrite()) {
2107 mLineNumber
+= theToken
->GetNewlineCount();
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
);
2116 IF_FREE(theToken
, mTokenAllocator
);
2119 IF_FREE(theToken
, mTokenAllocator
);
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
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
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
)) {
2153 if (eHTMLTag_nobr
== aChild
&&
2154 IsInlineElement(aParent
, aParent
) &&
2155 HasOpenContainer(eHTMLTag_nobr
)) {
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
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
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
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
) {
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
);
2236 if (eHTMLTag_unknown
!= aParent
) {
2237 result
= BackwardPropagate(mScratch
, aParent
, aChild
);
2239 result
= BackwardPropagate(mScratch
, eHTMLTag_html
, aChild
);
2244 if (mScratch
.Length() - 1 > gHTMLElements
[aParent
].mPropagateRange
) {
2248 result
= !!aParentContains
;
2257 * This method gets called to determine whether a given
2258 * tag can be omitted from opening. Most cannot.
2262 * @param aParentContains
2263 * @return PR_TRUE if given tag can contain other tags
2266 CNavDTD::CanOmit(eHTMLTags aParent
, eHTMLTags aChild
, PRInt32
& aParentContains
)
2268 eHTMLTags theAncestor
= gHTMLElements
[aChild
].mExcludingAncestor
;
2269 if (eHTMLTag_unknown
!= theAncestor
&& HasOpenContainer(theAncestor
)) {
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
)) {
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
) {
2294 if (gHTMLElements
[aParent
].IsBlockEntity() &&
2295 nsHTMLElement::IsInlineEntity(aChild
)) {
2296 // Feel free to drop inlines that a block doesn't contain.
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
)) {
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
2322 CNavDTD::IsContainer(PRInt32 aTag
) const
2324 return nsHTMLElement::IsContainer((eHTMLTags
)aTag
);
2329 CNavDTD::ForwardPropagate(nsString
& aSequence
, eHTMLTags aParent
,
2332 PRBool result
= PR_FALSE
;
2335 case eHTMLTag_table
:
2336 if (eHTMLTag_tr
== aChild
|| eHTMLTag_td
== aChild
) {
2337 return BackwardPropagate(aSequence
, aParent
, aChild
);
2339 // Otherwise, intentionally fall through...
2342 if (CanContain(eHTMLTag_td
, aChild
)) {
2343 aSequence
.Append((PRUnichar
)eHTMLTag_td
);
2344 result
= BackwardPropagate(aSequence
, aParent
, eHTMLTag_td
);
2356 CNavDTD::BackwardPropagate(nsString
& aSequence
, eHTMLTags aParent
,
2357 eHTMLTags aChild
) const
2359 eHTMLTags theParent
= aParent
;
2362 const TagList
* theRootTags
= gHTMLElements
[aChild
].GetRootTags();
2367 theParent
= theRootTags
->mTags
[0];
2368 NS_ASSERTION(CanContain(theParent
, aChild
),
2369 "Children must be contained by their root tags");
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
) {
2382 return !(~mFlags
& NS_DTD_FLAG_HAS_OPEN_FORM
);
2384 return mOpenMapCount
> 0;
2386 return mBodyContext
->HasOpenContainer(aContainer
);
2391 CNavDTD::HasOpenContainer(const eHTMLTags aTagSet
[], PRInt32 aCount
) const
2394 int theTopIndex
= mBodyContext
->GetCount() - 1;
2396 for (theIndex
= theTopIndex
; theIndex
> 0; --theIndex
) {
2397 if (FindTagInSet((*mBodyContext
)[theIndex
], aTagSet
, aCount
)) {
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)
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
)) {
2444 mFlags
&= ~NS_DTD_FLAG_ENABLE_RESIDUAL_STYLE
;
2445 for (; theLevel
< theCount
; ++theLevel
) {
2446 nsEntryStack
* theStack
= mBodyContext
->GetStylesAt(theLevel
);
2448 // Don't open transient styles if it makes the stack deep, bug 58917.
2449 if (theCount
+ theStack
->mCount
>= FONTSTYLE_IGNORE_DEPTH
) {
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
2465 theEntry
->mParent
= theStack
;
2466 if (isHeadingOpen
) {
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"),
2475 theNode
->AddAttribute(&theAttrToken
);
2476 result
= OpenContainer(theNode
, theNodeTag
, theStack
);
2477 theNode
->PopAttributeToken();
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
);
2493 mFlags
|= NS_DTD_FLAG_ENABLE_RESIDUAL_STYLE
;
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
2506 * @return 0 if all went well (which it always does)
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
2528 CNavDTD::OpenHTML(const nsCParserNode
*aNode
)
2530 NS_PRECONDITION(mBodyContext
->GetCount() >= 0, kInvalidTagStackPos
);
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));
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
);
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
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
;
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));
2578 if (!HasOpenContainer(eHTMLTag_body
)) {
2579 mBodyContext
->Push(const_cast<nsCParserNode
*>(aNode
), 0, PR_FALSE
);
2580 mTokenizer
->PrependTokens(mMisplacedContent
);
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
2597 CNavDTD::OpenContainer(const nsCParserNode
*aNode
,
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
);
2624 result
= OpenHTML(aNode
);
2628 if (!(mFlags
& NS_DTD_FLAG_HAS_OPEN_HEAD
)) {
2629 mFlags
|= NS_DTD_FLAG_HAS_OPEN_HEAD
;
2636 eHTMLTags theParent
= mBodyContext
->Last();
2637 if (!gHTMLElements
[aTag
].IsSpecialParent(theParent
)) {
2638 mFlags
|= NS_DTD_FLAG_HAS_OPEN_BODY
;
2639 result
= OpenBody(aNode
);
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
;
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
;
2668 case eHTMLTag_noembed
:
2669 // <noembed> is unconditionally alternate content.
2671 mFlags
|= NS_DTD_FLAG_ALTERNATE_CONTENT
;
2674 case eHTMLTag_noscript
:
2675 // We want to make sure that OpenContainer gets called below since we're
2676 // not doing it here
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
;
2686 case eHTMLTag_iframe
: // Bug 84491
2687 case eHTMLTag_noframes
:
2689 if (mFlags
& NS_IPARSER_FLAG_FRAMES_ENABLED
) {
2690 mFlags
|= NS_DTD_FLAG_ALTERNATE_CONTENT
;
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));
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
);
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
2725 CNavDTD::CloseContainer(const eHTMLTags aTag
, PRBool aMalformed
)
2727 nsresult result
= NS_OK
;
2728 PRBool done
= PR_TRUE
;
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();
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");
2748 if (mOpenMapCount
) {
2755 if (mFlags
& NS_DTD_FLAG_HAS_OPEN_FORM
) {
2756 mFlags
&= ~NS_DTD_FLAG_HAS_OPEN_FORM
;
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....
2775 MOZ_TIMER_DEBUGLOG(("Stop: Parse Time: CNavDTD::CloseContainer(), this=%p\n", this));
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
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));
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
2812 * @param aClosedByStartTag -- if TRUE, then we're closing something because a start tag caused it
2813 * @return TRUE if ok, FALSE if error
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) {
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");
2842 if (theChildStyleStack
) {
2843 mBodyContext
->PushStyles(theChildStyleStack
);
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
);
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
);
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.
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
);
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
);
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
);
2939 // The tag is not a style tag.
2940 if (theChildStyleStack
) {
2941 if (theStyleDoesntLeakOut
) {
2942 IF_DELETE(theChildStyleStack
, &mNodeAllocator
);
2944 mBodyContext
->PushStyles(theChildStyleStack
);
2948 IF_FREE(theNode
, &mNodeAllocator
);
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
2960 * @param aClosedByStartTag -- ONLY TRUE if the container is being closed by opening of another container.
2961 * @return TRUE if ok, FALSE if error
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.
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
);
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
3015 CNavDTD::AddLeaf(const nsIParserNode
*aNode
)
3017 nsresult result
= NS_OK
;
3020 eHTMLTags theTag
= (eHTMLTags
)aNode
->GetNodeType();
3021 OpenTransientStyles(theTag
);
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));
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
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
))) {
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
);
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
,
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));
3103 CNavDTD::CreateContextStackFor(eHTMLTags aParent
, eHTMLTags aChild
)
3105 mScratch
.Truncate();
3107 PRBool result
= ForwardPropagate(mScratch
, aParent
, aChild
);
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
);
3122 PRInt32 theLen
= mScratch
.Length();
3123 eHTMLTags theTag
= (eHTMLTags
)mScratch
[--theLen
];
3125 // Now, build up the stack according to the tags.
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
3136 // Note: These tokens should all wind up on contextstack, so don't recycle
3138 CToken
*theToken
= mTokenAllocator
->CreateTokenOfType(eToken_start
, theTag
);
3139 HandleToken(theToken
, mParser
);
3144 CNavDTD::WillResumeParse(nsIContentSink
* aSink
)
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));
3158 CNavDTD::WillInterruptParse(nsIContentSink
* aSink
)
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));