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
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is mozilla.org code.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Netscape Communications Corporation.
20 : * Portions created by the Initial Developer are Copyright (C) 1998
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either of the GNU General Public License Version 2 or later (the "GPL"),
27 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "mozilla/Util.h"
40 :
41 : #include "nsDebug.h"
42 : #include "nsIAtom.h"
43 : #include "CNavDTD.h"
44 : #include "nsHTMLTokens.h"
45 : #include "nsCRT.h"
46 : #include "nsParser.h"
47 : #include "nsIHTMLContentSink.h"
48 : #include "nsScanner.h"
49 : #include "prenv.h"
50 : #include "prtypes.h"
51 : #include "prio.h"
52 : #include "plstr.h"
53 : #include "nsDTDUtils.h"
54 : #include "nsHTMLTokenizer.h"
55 : #include "nsParserNode.h"
56 : #include "nsHTMLEntities.h"
57 : #include "nsLinebreakConverter.h"
58 : #include "nsIFormProcessor.h"
59 : #include "nsTArray.h"
60 : #include "nsReadableUtils.h"
61 : #include "nsUnicharUtils.h"
62 : #include "prmem.h"
63 : #include "nsIServiceManager.h"
64 : #include "nsParserConstants.h"
65 :
66 : using namespace mozilla;
67 :
68 : /*
69 : * Ignore kFontStyle and kPhrase tags when the stack is deep, bug 58917.
70 : */
71 : #define FONTSTYLE_IGNORE_DEPTH (MAX_REFLOW_DEPTH * 80 / 100)
72 : #define PHRASE_IGNORE_DEPTH (MAX_REFLOW_DEPTH * 90 / 100)
73 :
74 : static NS_DEFINE_CID(kFormProcessorCID, NS_FORMPROCESSOR_CID);
75 :
76 : #ifdef DEBUG
77 : static const char kNullToken[] = "Error: Null token given";
78 : static const char kInvalidTagStackPos[] = "Error: invalid tag stack position";
79 : #endif
80 :
81 : #include "nsElementTable.h"
82 :
83 : // Some flags for use by the DTD.
84 : #define NS_DTD_FLAG_NONE 0x00000000
85 : #define NS_DTD_FLAG_HAS_OPEN_HEAD 0x00000001
86 : #define NS_DTD_FLAG_HAS_OPEN_BODY 0x00000002
87 : #define NS_DTD_FLAG_HAS_OPEN_FORM 0x00000004
88 : #define NS_DTD_FLAG_HAS_EXPLICIT_HEAD 0x00000008
89 : #define NS_DTD_FLAG_HAD_BODY 0x00000010
90 : #define NS_DTD_FLAG_HAD_FRAMESET 0x00000020
91 : #define NS_DTD_FLAG_ENABLE_RESIDUAL_STYLE 0x00000040
92 : #define NS_DTD_FLAG_ALTERNATE_CONTENT 0x00000080 // NOFRAMES, NOSCRIPT
93 : #define NS_DTD_FLAG_MISPLACED_CONTENT 0x00000100
94 : #define NS_DTD_FLAG_IN_MISPLACED_CONTENT 0x00000200
95 : #define NS_DTD_FLAG_STOP_PARSING 0x00000400
96 :
97 : #define NS_DTD_FLAG_HAS_MAIN_CONTAINER (NS_DTD_FLAG_HAD_BODY | \
98 : NS_DTD_FLAG_HAD_FRAMESET)
99 :
100 56 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CNavDTD)
101 28 : NS_INTERFACE_MAP_ENTRY(nsIDTD)
102 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDTD)
103 0 : NS_INTERFACE_MAP_END
104 :
105 56 : NS_IMPL_CYCLE_COLLECTING_ADDREF(CNavDTD)
106 56 : NS_IMPL_CYCLE_COLLECTING_RELEASE(CNavDTD)
107 :
108 1464 : NS_IMPL_CYCLE_COLLECTION_1(CNavDTD, mSink)
109 :
110 28 : CNavDTD::CNavDTD()
111 : : mMisplacedContent(0),
112 : mTokenAllocator(0),
113 : mBodyContext(new nsDTDContext()),
114 : mTempContext(0),
115 : mCountLines(true),
116 : mTokenizer(0),
117 : mDTDMode(eDTDMode_quirks),
118 : mDocType(eHTML_Quirks),
119 : mParserCommand(eViewNormal),
120 : mLineNumber(1),
121 : mOpenMapCount(0),
122 : mHeadContainerPosition(-1),
123 56 : mFlags(NS_DTD_FLAG_NONE)
124 : {
125 28 : }
126 :
127 84 : CNavDTD::~CNavDTD()
128 : {
129 28 : delete mBodyContext;
130 28 : delete mTempContext;
131 112 : }
132 :
133 : NS_IMETHODIMP
134 28 : CNavDTD::WillBuildModel(const CParserContext& aParserContext,
135 : nsITokenizer* aTokenizer,
136 : nsIContentSink* aSink)
137 : {
138 28 : nsresult result = NS_OK;
139 :
140 28 : mFilename = aParserContext.mScanner->GetFilename();
141 28 : mFlags = NS_DTD_FLAG_ENABLE_RESIDUAL_STYLE;
142 28 : mLineNumber = 1;
143 28 : mDTDMode = aParserContext.mDTDMode;
144 28 : mParserCommand = aParserContext.mParserCommand;
145 28 : mMimeType = aParserContext.mMimeType;
146 28 : mDocType = aParserContext.mDocType;
147 28 : mTokenizer = aTokenizer;
148 28 : mBodyContext->SetNodeAllocator(&mNodeAllocator);
149 :
150 28 : if (!aParserContext.mPrevContext && aSink) {
151 :
152 28 : if (!mSink) {
153 28 : mSink = do_QueryInterface(aSink, &result);
154 28 : if (NS_FAILED(result)) {
155 0 : mFlags |= NS_DTD_FLAG_STOP_PARSING;
156 0 : return result;
157 : }
158 : }
159 :
160 28 : mFlags |= nsHTMLTokenizer::GetFlags(aSink);
161 :
162 : }
163 :
164 28 : return result;
165 : }
166 :
167 : NS_IMETHODIMP
168 56 : CNavDTD::BuildModel(nsITokenizer* aTokenizer,
169 : bool aCountLines,
170 : const nsCString*)
171 : {
172 56 : NS_PRECONDITION(mBodyContext != nsnull,
173 : "Create a context before calling build model");
174 :
175 56 : nsresult result = NS_OK;
176 :
177 56 : if (!aTokenizer) {
178 0 : return NS_OK;
179 : }
180 :
181 56 : nsITokenizer* const oldTokenizer = mTokenizer;
182 :
183 56 : mCountLines = aCountLines;
184 56 : mTokenizer = aTokenizer;
185 56 : mTokenAllocator = mTokenizer->GetTokenAllocator();
186 :
187 56 : if (!mSink) {
188 : return (mFlags & NS_DTD_FLAG_STOP_PARSING)
189 : ? NS_ERROR_HTMLPARSER_STOPPARSING
190 0 : : result;
191 : }
192 :
193 56 : if (mBodyContext->GetCount() == 0) {
194 : CToken* tempToken;
195 28 : if (ePlainText == mDocType) {
196 : tempToken =
197 0 : mTokenAllocator->CreateTokenOfType(eToken_start, eHTMLTag_pre);
198 0 : if (tempToken) {
199 0 : mTokenizer->PushTokenFront(tempToken);
200 : }
201 : }
202 :
203 : // Always open a body if frames are disabled.
204 28 : if (!(mFlags & NS_IPARSER_FLAG_FRAMES_ENABLED)) {
205 : tempToken =
206 : mTokenAllocator->CreateTokenOfType(eToken_start,
207 : eHTMLTag_body,
208 0 : NS_LITERAL_STRING("body"));
209 0 : if (tempToken) {
210 0 : mTokenizer->PushTokenFront(tempToken);
211 : }
212 : }
213 :
214 : // If the content model is empty, then begin by opening <html>.
215 28 : CStartToken* theToken = (CStartToken*)mTokenizer->GetTokenAt(0);
216 28 : if (theToken) {
217 25 : eHTMLTags theTag = (eHTMLTags)theToken->GetTypeID();
218 25 : eHTMLTokenTypes theType = eHTMLTokenTypes(theToken->GetTokenType());
219 25 : if (theTag != eHTMLTag_html || theType != eToken_start) {
220 : tempToken =
221 : mTokenAllocator->CreateTokenOfType(eToken_start,
222 : eHTMLTag_html,
223 25 : NS_LITERAL_STRING("html"));
224 25 : if (tempToken) {
225 25 : mTokenizer->PushTokenFront(tempToken);
226 : }
227 : }
228 : } else {
229 : tempToken =
230 : mTokenAllocator->CreateTokenOfType(eToken_start,
231 : eHTMLTag_html,
232 3 : NS_LITERAL_STRING("html"));
233 3 : if (tempToken) {
234 3 : mTokenizer->PushTokenFront(tempToken);
235 : }
236 : }
237 : }
238 :
239 2502 : while (NS_SUCCEEDED(result)) {
240 2446 : if (!(mFlags & NS_DTD_FLAG_STOP_PARSING)) {
241 2446 : CToken* theToken = mTokenizer->PopToken();
242 2446 : if (!theToken) {
243 56 : break;
244 : }
245 2390 : result = HandleToken(theToken);
246 : } else {
247 0 : result = NS_ERROR_HTMLPARSER_STOPPARSING;
248 0 : break;
249 : }
250 :
251 2390 : if (NS_ERROR_HTMLPARSER_INTERRUPTED == mSink->DidProcessAToken()) {
252 : // The content sink has requested that DTD interrupt processing tokens
253 : // We need to make sure that an interruption does not override
254 : // a request to block the parser.
255 0 : if (NS_SUCCEEDED(result)) {
256 0 : result = NS_ERROR_HTMLPARSER_INTERRUPTED;
257 0 : break;
258 : }
259 : }
260 : }
261 :
262 56 : mTokenizer = oldTokenizer;
263 56 : return result;
264 : }
265 :
266 : nsresult
267 3 : CNavDTD::BuildNeglectedTarget(eHTMLTags aTarget,
268 : eHTMLTokenTypes aType)
269 : {
270 3 : NS_ASSERTION(mTokenizer, "tokenizer is null! unable to build target.");
271 3 : NS_ASSERTION(mTokenAllocator, "unable to create tokens without an allocator.");
272 3 : if (!mTokenizer || !mTokenAllocator) {
273 0 : return NS_OK;
274 : }
275 :
276 3 : CToken* target = mTokenAllocator->CreateTokenOfType(aType, aTarget);
277 3 : NS_ENSURE_TRUE(target, NS_ERROR_OUT_OF_MEMORY);
278 3 : mTokenizer->PushTokenFront(target);
279 : // Also, BuildModel
280 : // doesn't seem to care about the charset, and at this point we have no idea
281 : // what the charset was, so 0 can and must suffice.
282 3 : return BuildModel(mTokenizer, mCountLines, 0);
283 : }
284 :
285 : NS_IMETHODIMP
286 28 : CNavDTD::DidBuildModel(nsresult anErrorCode)
287 : {
288 28 : nsresult result = NS_OK;
289 :
290 28 : if (mSink) {
291 28 : if (NS_OK == anErrorCode) {
292 28 : if (!(mFlags & NS_DTD_FLAG_HAS_MAIN_CONTAINER)) {
293 : // This document is not a frameset document, however, it did not contain
294 : // a body tag either. So, make one!. Note: Body tag is optional per spec..
295 : // Also note: We ignore the return value of BuildNeglectedTarget, we
296 : // can't reasonably respond to errors (or requests to block) at this
297 : // point in the parsing process.
298 3 : BuildNeglectedTarget(eHTMLTag_body, eToken_start);
299 : }
300 28 : if (mFlags & NS_DTD_FLAG_MISPLACED_CONTENT) {
301 : // Looks like the misplaced contents are not processed yet.
302 : // Here is our last chance to handle the misplaced content.
303 :
304 : // Keep track of the top index.
305 0 : PRInt32 topIndex = mBodyContext->mContextTopIndex;
306 :
307 : // Loop until we've really consumed all of our misplaced content.
308 0 : do {
309 0 : mFlags &= ~NS_DTD_FLAG_MISPLACED_CONTENT;
310 :
311 : // mContextTopIndex refers to the misplaced content's legal parent index.
312 0 : result = HandleSavedTokens(mBodyContext->mContextTopIndex);
313 0 : if (NS_FAILED(result)) {
314 0 : NS_ERROR("Bug in the DTD");
315 0 : break;
316 : }
317 :
318 : // If we start handling misplaced content while handling misplaced
319 : // content, mContextTopIndex gets modified. However, this new index
320 : // necessarily points to the middle of a closed tag (since we close
321 : // new tags after handling the misplaced content). So we restore the
322 : // insertion point after every iteration.
323 0 : mBodyContext->mContextTopIndex = topIndex;
324 : } while (mFlags & NS_DTD_FLAG_MISPLACED_CONTENT);
325 :
326 0 : mBodyContext->mContextTopIndex = -1;
327 : }
328 :
329 : // Now let's disable style handling to save time when closing remaining
330 : // stack members.
331 28 : mFlags &= ~NS_DTD_FLAG_ENABLE_RESIDUAL_STYLE;
332 137 : while (mBodyContext->GetCount() > 0) {
333 81 : result = CloseContainersTo(mBodyContext->Last(), false);
334 81 : NS_ENSURE_SUCCESS(result, result);
335 : }
336 : } else {
337 : // If you're here, then an error occurred, but we still have nodes on the stack.
338 : // At a minimum, we should grab the nodes and recycle them.
339 : // Just to be correct, we'll also recycle the nodes.
340 0 : while (mBodyContext->GetCount() > 0) {
341 0 : nsEntryStack* theChildStyles = 0;
342 0 : nsCParserNode* theNode = mBodyContext->Pop(theChildStyles);
343 0 : IF_DELETE(theChildStyles, &mNodeAllocator);
344 0 : IF_FREE(theNode, &mNodeAllocator);
345 : }
346 : }
347 :
348 : // Now make sure the misplaced content list is empty,
349 : // by forcefully recycling any tokens we might find there.
350 28 : CToken* theToken = 0;
351 56 : while ((theToken = (CToken*)mMisplacedContent.Pop())) {
352 0 : IF_FREE(theToken, mTokenAllocator);
353 : }
354 : }
355 :
356 28 : return result;
357 : }
358 :
359 : NS_IMETHODIMP_(void)
360 0 : CNavDTD::Terminate()
361 : {
362 0 : mFlags |= NS_DTD_FLAG_STOP_PARSING;
363 0 : }
364 :
365 :
366 : NS_IMETHODIMP_(PRInt32)
367 240 : CNavDTD::GetType()
368 : {
369 240 : return NS_IPARSER_FLAG_HTML;
370 : }
371 :
372 : NS_IMETHODIMP_(nsDTDMode)
373 28 : CNavDTD::GetMode() const
374 : {
375 28 : return mDTDMode;
376 : }
377 :
378 : /**
379 : * Text and some tags require a body when they're added, this function returns
380 : * true for those tags.
381 : *
382 : * @param aToken The current token that we care about.
383 : * @param aTokenizer A tokenizer that we can get the tags attributes off of.
384 : * @return true if aToken does indeed force the body to open.
385 : */
386 : static bool
387 54 : DoesRequireBody(CToken* aToken, nsITokenizer* aTokenizer)
388 : {
389 54 : bool result = false;
390 :
391 54 : if (aToken) {
392 54 : eHTMLTags theTag = (eHTMLTags)aToken->GetTypeID();
393 54 : if (gHTMLElements[theTag].HasSpecialProperty(kRequiresBody)) {
394 25 : if (theTag == eHTMLTag_input) {
395 : // IE & Nav4x opens up a body for type=text - Bug 66985
396 : // XXXbz but we don't want to open one for <input> with no
397 : // type attribute? That's pretty whack.
398 0 : PRInt32 ac = aToken->GetAttributeCount();
399 0 : for(PRInt32 i = 0; i < ac; ++i) {
400 : CAttributeToken* attr = static_cast<CAttributeToken*>
401 0 : (aTokenizer->GetTokenAt(i));
402 0 : const nsSubstring& name = attr->GetKey();
403 0 : const nsAString& value = attr->GetValue();
404 : // XXXbz note that this stupid case-sensitive comparison is
405 : // actually depended on by sites...
406 0 : if ((name.EqualsLiteral("type") ||
407 0 : name.EqualsLiteral("TYPE"))
408 : &&
409 0 : !(value.EqualsLiteral("hidden") ||
410 0 : value.EqualsLiteral("HIDDEN"))) {
411 0 : result = true;
412 0 : break;
413 : }
414 : }
415 : } else {
416 25 : result = true;
417 : }
418 : }
419 : }
420 :
421 54 : return result;
422 : }
423 :
424 : static bool
425 0 : ValueIsHidden(const nsAString& aValue)
426 : {
427 : // Having to deal with whitespace here sucks, but we have to match
428 : // what the content sink does.
429 0 : nsAutoString str(aValue);
430 0 : str.Trim("\n\r\t\b");
431 0 : return str.LowerCaseEqualsLiteral("hidden");
432 : }
433 :
434 : // Check whether aToken corresponds to a <input type="hidden"> tag. The token
435 : // must be a start tag token for an <input>. This must be called at a point
436 : // when all the attributes for the input are still in the tokenizer.
437 : static bool
438 0 : IsHiddenInput(CToken* aToken, nsITokenizer* aTokenizer)
439 : {
440 0 : NS_PRECONDITION(eHTMLTokenTypes(aToken->GetTokenType()) == eToken_start,
441 : "Must be start token");
442 0 : NS_PRECONDITION(eHTMLTags(aToken->GetTypeID()) == eHTMLTag_input,
443 : "Must be <input> tag");
444 :
445 0 : PRInt32 ac = aToken->GetAttributeCount();
446 0 : NS_ASSERTION(ac <= aTokenizer->GetCount(),
447 : "Not enough tokens in the tokenizer");
448 : // But we don't really trust ourselves to get that right
449 0 : ac = NS_MIN(ac, aTokenizer->GetCount());
450 :
451 0 : for (PRInt32 i = 0; i < ac; ++i) {
452 0 : NS_ASSERTION(eHTMLTokenTypes(aTokenizer->GetTokenAt(i)->GetTokenType()) ==
453 : eToken_attribute, "Unexpected token type");
454 : // Again, we're not sure we actually manage to guarantee that
455 0 : if (eHTMLTokenTypes(aTokenizer->GetTokenAt(i)->GetTokenType()) !=
456 : eToken_attribute) {
457 0 : break;
458 : }
459 :
460 : CAttributeToken* attrToken =
461 0 : static_cast<CAttributeToken*>(aTokenizer->GetTokenAt(i));
462 0 : if (!attrToken->GetKey().LowerCaseEqualsLiteral("type")) {
463 0 : continue;
464 : }
465 :
466 0 : return ValueIsHidden(attrToken->GetValue());
467 : }
468 :
469 0 : return false;
470 : }
471 :
472 : /**
473 : * Returns whether or not there is a tag of type aType open on aContext.
474 : */
475 : static bool
476 0 : HasOpenTagOfType(PRInt32 aType, const nsDTDContext& aContext)
477 : {
478 0 : PRInt32 count = aContext.GetCount();
479 :
480 0 : while (--count >= 0) {
481 0 : if (gHTMLElements[aContext.TagAt(count)].IsMemberOf(aType)) {
482 0 : return true;
483 : }
484 : }
485 :
486 0 : return false;
487 : }
488 :
489 : nsresult
490 2415 : CNavDTD::HandleToken(CToken* aToken)
491 : {
492 2415 : if (!aToken) {
493 0 : return NS_OK;
494 : }
495 :
496 2415 : nsresult result = NS_OK;
497 2415 : CHTMLToken* theToken = static_cast<CHTMLToken*>(aToken);
498 2415 : eHTMLTokenTypes theType = eHTMLTokenTypes(theToken->GetTokenType());
499 2415 : eHTMLTags theTag = (eHTMLTags)theToken->GetTypeID();
500 :
501 2415 : aToken->SetLineNumber(mLineNumber);
502 :
503 2415 : if (mCountLines) {
504 2415 : mLineNumber += aToken->GetNewlineCount();
505 : }
506 :
507 2415 : if (mFlags & NS_DTD_FLAG_MISPLACED_CONTENT) {
508 : // Included TD & TH to fix Bug# 20797
509 : static eHTMLTags gLegalElements[] = {
510 : eHTMLTag_table, eHTMLTag_thead, eHTMLTag_tbody,
511 : eHTMLTag_tr, eHTMLTag_td, eHTMLTag_th, eHTMLTag_tfoot
512 : };
513 : // Don't even try processing misplaced tokens if we're already
514 : // handling misplaced content. See bug 269095
515 0 : if (mFlags & NS_DTD_FLAG_IN_MISPLACED_CONTENT) {
516 0 : PushIntoMisplacedStack(theToken);
517 0 : return NS_OK;
518 : }
519 :
520 0 : eHTMLTags theParentTag = mBodyContext->Last();
521 0 : if (FindTagInSet(theTag, gLegalElements,
522 0 : ArrayLength(gLegalElements)) ||
523 0 : (gHTMLElements[theParentTag].CanContain(theTag, mDTDMode) &&
524 : // Here's a problem. If theTag is legal in here, we don't move it
525 : // out. So if we're moving stuff out of here, the parent of theTag
526 : // gets closed at this point. But some things are legal
527 : // _everywhere_ and hence would effectively close out misplaced
528 : // content in tables. This is undesirable, so treat them as
529 : // illegal here so they'll be shipped out with their parents and
530 : // siblings. See bug 40855 for an explanation (that bug was for
531 : // comments, but the same issues arise with whitespace, newlines,
532 : // noscript, etc). Script is special, though. Shipping it out
533 : // breaks document.write stuff. See bug 243064.
534 0 : (!gHTMLElements[theTag].HasSpecialProperty(kLegalOpen) ||
535 : theTag == eHTMLTag_script)) ||
536 : (theTag == eHTMLTag_input && theType == eToken_start &&
537 : FindTagInSet(theParentTag, gLegalElements,
538 0 : ArrayLength(gLegalElements)) &&
539 0 : IsHiddenInput(theToken, mTokenizer))) {
540 : // Reset the state since all the misplaced tokens are about to get
541 : // handled.
542 0 : mFlags &= ~NS_DTD_FLAG_MISPLACED_CONTENT;
543 :
544 0 : result = HandleSavedTokens(mBodyContext->mContextTopIndex);
545 0 : NS_ENSURE_SUCCESS(result, result);
546 :
547 0 : mBodyContext->mContextTopIndex = -1;
548 : } else {
549 0 : PushIntoMisplacedStack(theToken);
550 0 : return result;
551 : }
552 : }
553 :
554 : /*
555 : * This section of code is used to "move" misplaced content from one location
556 : * in our document model to another. (Consider what would happen if we found a
557 : * <P> tag in the head.) To move content, we throw it onto the
558 : * misplacedcontent deque until we can deal with it.
559 : */
560 2415 : switch(theTag) {
561 : case eHTMLTag_html:
562 : case eHTMLTag_noframes:
563 : case eHTMLTag_script:
564 : case eHTMLTag_doctypeDecl:
565 : case eHTMLTag_instruction:
566 53 : break;
567 :
568 : default:
569 2362 : if (!gHTMLElements[eHTMLTag_html].SectionContains(theTag, false)) {
570 2334 : if (!(mFlags & (NS_DTD_FLAG_HAS_MAIN_CONTAINER |
571 2334 : NS_DTD_FLAG_ALTERNATE_CONTENT))) {
572 : // For bug examples from this code, see bugs: 18928, 20989.
573 : // At this point we know the body/frameset aren't open.
574 : // If the child belongs in the head, then handle it (which may open
575 : // the head); otherwise, push it onto the misplaced stack.
576 :
577 279 : bool isExclusive = false;
578 : bool theChildBelongsInHead =
579 279 : gHTMLElements[eHTMLTag_head].IsChildOfHead(theTag, isExclusive);
580 604 : if (theChildBelongsInHead &&
581 200 : !isExclusive &&
582 125 : !gHTMLElements[theTag].HasSpecialProperty(kPreferHead)) {
583 250 : if (mMisplacedContent.GetSize() == 0 &&
584 125 : (!gHTMLElements[theTag].HasSpecialProperty(kPreferBody) ||
585 : (mFlags & NS_DTD_FLAG_HAS_EXPLICIT_HEAD))) {
586 : // This tag can either be in the body or the head. Since
587 : // there is no indication that the body should be open,
588 : // put this token in the head.
589 125 : break;
590 : }
591 :
592 : // Otherwise, we have received some indication that the body is
593 : // "open", so push this token onto the misplaced content stack.
594 0 : theChildBelongsInHead = false;
595 : }
596 :
597 154 : if (!theChildBelongsInHead) {
598 79 : eHTMLTags top = mBodyContext->Last();
599 79 : NS_ASSERTION(top != eHTMLTag_userdefined,
600 : "Userdefined tags should act as leaves in the head");
601 104 : if (top != eHTMLTag_html && top != eHTMLTag_head &&
602 25 : gHTMLElements[top].CanContain(theTag, mDTDMode)) {
603 : // Some tags (such as <object> and <script>) are opened in the
604 : // head and allow other non-head content to be children.
605 : // Note: Userdefined tags in the head act like leaves.
606 25 : break;
607 : }
608 :
609 : // If you're here then we found a child of the body that was out of
610 : // place. We're going to move it to the body by storing it
611 : // temporarily on the misplaced stack. However, in quirks mode, a
612 : // few tags request, ambiguosly, for a BODY. - Bugs 18928, 24204.-
613 54 : PushIntoMisplacedStack(aToken);
614 :
615 54 : if (IsAlternateTag(theTag)) {
616 : // These tags' contents are consumed as CDATA. If we simply
617 : // pushed them on the misplaced content stack, the CDATA
618 : // contents would force us to open a body, which could be
619 : // wrong. So we collect the whole tag as misplaced in one
620 : // gulp. Note that the tokenizer guarantees that there will
621 : // be an end tag.
622 0 : CToken *current = aToken;
623 0 : while (current->GetTokenType() != eToken_end ||
624 0 : current->GetTypeID() != theTag) {
625 0 : current = static_cast<CToken *>(mTokenizer->PopToken());
626 0 : NS_ASSERTION(current, "The tokenizer is not creating good "
627 : "alternate tags");
628 0 : PushIntoMisplacedStack(current);
629 : }
630 :
631 : // XXX Add code to also collect incorrect attributes on the
632 : // end tag.
633 : }
634 :
635 54 : if (DoesRequireBody(aToken, mTokenizer)) {
636 : CToken* theBodyToken =
637 : mTokenAllocator->CreateTokenOfType(eToken_start,
638 : eHTMLTag_body,
639 25 : NS_LITERAL_STRING("body"));
640 25 : result = HandleToken(theBodyToken);
641 : }
642 54 : return result;
643 : }
644 : }
645 : }
646 : }
647 :
648 2361 : if (theToken) {
649 2361 : switch (theType) {
650 : case eToken_text:
651 : case eToken_start:
652 : case eToken_whitespace:
653 : case eToken_newline:
654 1967 : result = HandleStartToken(theToken);
655 1967 : break;
656 :
657 : case eToken_end:
658 344 : result = HandleEndToken(theToken);
659 344 : break;
660 :
661 : case eToken_cdatasection:
662 : case eToken_comment:
663 : case eToken_markupDecl:
664 25 : result = HandleCommentToken(theToken);
665 25 : break;
666 :
667 : case eToken_entity:
668 0 : result = HandleEntityToken(theToken);
669 0 : break;
670 :
671 : case eToken_attribute:
672 0 : result = HandleAttributeToken(theToken);
673 0 : break;
674 :
675 : case eToken_instruction:
676 0 : result = HandleProcessingInstructionToken(theToken);
677 0 : break;
678 :
679 : case eToken_doctypeDecl:
680 25 : result = HandleDocTypeDeclToken(theToken);
681 25 : break;
682 :
683 : default:
684 0 : break;
685 : }
686 :
687 2361 : IF_FREE(theToken, mTokenAllocator);
688 2361 : if (result == NS_ERROR_HTMLPARSER_STOPPARSING) {
689 0 : mFlags |= NS_DTD_FLAG_STOP_PARSING;
690 2361 : } else if (NS_FAILED(result) && result != NS_ERROR_HTMLPARSER_BLOCK) {
691 0 : result = NS_OK;
692 : }
693 : }
694 :
695 2361 : return result;
696 : }
697 :
698 : nsresult
699 1967 : CNavDTD::DidHandleStartTag(nsIParserNode& aNode, eHTMLTags aChildTag)
700 : {
701 1967 : nsresult result = NS_OK;
702 :
703 1967 : switch (aChildTag) {
704 : case eHTMLTag_pre:
705 : case eHTMLTag_listing:
706 : {
707 : // Skip the 1st newline inside PRE and LISTING unless this is a
708 : // plain text doc (for which we pushed a PRE in CNavDTD::BuildModel).
709 :
710 : // XXX This code is incorrect in the face of misplaced <pre> and
711 : // <listing> tags (as direct children of <table>).
712 0 : CToken* theNextToken = mTokenizer->PeekToken();
713 0 : if (ePlainText != mDocType && theNextToken) {
714 0 : eHTMLTokenTypes theType = eHTMLTokenTypes(theNextToken->GetTokenType());
715 0 : if (eToken_newline == theType) {
716 0 : if (mCountLines) {
717 0 : mLineNumber += theNextToken->GetNewlineCount();
718 : }
719 0 : theNextToken = mTokenizer->PopToken();
720 0 : IF_FREE(theNextToken, mTokenAllocator); // fix for Bug 29379
721 : }
722 : }
723 : }
724 0 : break;
725 :
726 : default:
727 1967 : break;
728 : }
729 :
730 1967 : return result;
731 : }
732 :
733 : PRInt32
734 0 : CNavDTD::LastOf(eHTMLTags aTagSet[], PRInt32 aCount) const
735 : {
736 0 : for (PRInt32 theIndex = mBodyContext->GetCount() - 1; theIndex >= 0;
737 : --theIndex) {
738 0 : if (FindTagInSet((*mBodyContext)[theIndex], aTagSet, aCount)) {
739 0 : return theIndex;
740 : }
741 : }
742 :
743 0 : return kNotFound;
744 : }
745 :
746 : static bool
747 62 : CanBeContained(eHTMLTags aChildTag, nsDTDContext& aContext)
748 : {
749 : /* # Interesting test cases: Result:
750 : * 1. <UL><LI>..<B>..<LI> inner <LI> closes outer <LI>
751 : * 2. <CENTER><DL><DT><A><CENTER> allow nested <CENTER>
752 : * 3. <TABLE><TR><TD><TABLE>... allow nested <TABLE>
753 : * 4. <FRAMESET> ... <FRAMESET>
754 : */
755 62 : bool result = true;
756 62 : PRInt32 theCount = aContext.GetCount();
757 :
758 62 : if (0 < theCount) {
759 62 : const TagList* theRootTags = gHTMLElements[aChildTag].GetRootTags();
760 : const TagList* theSpecialParents =
761 62 : gHTMLElements[aChildTag].GetSpecialParents();
762 :
763 62 : if (theRootTags) {
764 62 : PRInt32 theRootIndex = LastOf(aContext, *theRootTags);
765 : PRInt32 theSPIndex = theSpecialParents
766 : ? LastOf(aContext, *theSpecialParents)
767 62 : : kNotFound;
768 : PRInt32 theChildIndex =
769 62 : nsHTMLElement::GetIndexOfChildOrSynonym(aContext, aChildTag);
770 : PRInt32 theTargetIndex = (theRootIndex > theSPIndex)
771 : ? theRootIndex
772 62 : : theSPIndex;
773 :
774 62 : if (theTargetIndex == theCount-1 ||
775 : (theTargetIndex == theChildIndex &&
776 0 : gHTMLElements[aChildTag].CanContainSelf())) {
777 62 : result = true;
778 : } else {
779 0 : result = false;
780 :
781 : static eHTMLTags gTableElements[] = { eHTMLTag_td, eHTMLTag_th };
782 :
783 0 : PRInt32 theIndex = theCount - 1;
784 0 : while (theChildIndex < theIndex) {
785 0 : eHTMLTags theParentTag = aContext.TagAt(theIndex--);
786 0 : if (gHTMLElements[theParentTag].IsMemberOf(kBlockEntity) ||
787 0 : gHTMLElements[theParentTag].IsMemberOf(kHeading) ||
788 0 : gHTMLElements[theParentTag].IsMemberOf(kPreformatted) ||
789 0 : gHTMLElements[theParentTag].IsMemberOf(kFormControl) || //fixes bug 44479
790 0 : gHTMLElements[theParentTag].IsMemberOf(kList)) {
791 0 : if (!HasOptionalEndTag(theParentTag)) {
792 0 : result = true;
793 0 : break;
794 : }
795 0 : } else if (FindTagInSet(theParentTag, gTableElements,
796 0 : ArrayLength(gTableElements))) {
797 : // Added this to catch a case we missed; bug 57173.
798 0 : result = true;
799 0 : break;
800 : }
801 : }
802 : }
803 : }
804 : }
805 :
806 62 : return result;
807 : }
808 :
809 : enum eProcessRule { eNormalOp, eLetInlineContainBlock };
810 :
811 : nsresult
812 1817 : CNavDTD::HandleDefaultStartToken(CToken* aToken, eHTMLTags aChildTag,
813 : nsCParserNode *aNode)
814 : {
815 1817 : NS_PRECONDITION(nsnull != aToken, kNullToken);
816 :
817 1817 : nsresult result = NS_OK;
818 1817 : bool theChildIsContainer = nsHTMLElement::IsContainer(aChildTag);
819 :
820 : // Client of parser is spefically trying to parse a fragment that
821 : // may lack required context. Suspend containment rules if so.
822 1817 : if (mParserCommand != eViewFragment) {
823 1817 : bool theChildAgrees = true;
824 1817 : PRInt32 theIndex = mBodyContext->GetCount();
825 1817 : PRInt32 theParentContains = 0;
826 :
827 2477 : do {
828 2147 : eHTMLTags theParentTag = mBodyContext->TagAt(--theIndex);
829 2147 : if (theParentTag == eHTMLTag_userdefined) {
830 0 : continue;
831 : }
832 :
833 : // Figure out whether this is a hidden input inside a
834 : // table/tbody/thead/tfoot/tr
835 : static eHTMLTags sTableElements[] = {
836 : eHTMLTag_table, eHTMLTag_thead, eHTMLTag_tbody,
837 : eHTMLTag_tr, eHTMLTag_tfoot
838 : };
839 :
840 2147 : bool isHiddenInputInsideTableElement = false;
841 2147 : if (aChildTag == eHTMLTag_input &&
842 : FindTagInSet(theParentTag, sTableElements,
843 0 : ArrayLength(sTableElements))) {
844 0 : PRInt32 attrCount = aNode->GetAttributeCount();
845 0 : for (PRInt32 attrIndex = 0; attrIndex < attrCount; ++attrIndex) {
846 0 : const nsAString& key = aNode->GetKeyAt(attrIndex);
847 0 : if (key.LowerCaseEqualsLiteral("type")) {
848 : isHiddenInputInsideTableElement =
849 0 : ValueIsHidden(aNode->GetValueAt(attrIndex));
850 0 : break;
851 : }
852 : }
853 : }
854 :
855 : // Precompute containment, and pass it to CanOmit()...
856 : theParentContains =
857 2147 : isHiddenInputInsideTableElement || CanContain(theParentTag, aChildTag);
858 4294 : if (!isHiddenInputInsideTableElement &&
859 2147 : CanOmit(theParentTag, aChildTag, theParentContains)) {
860 0 : HandleOmittedTag(aToken, aChildTag, theParentTag, aNode);
861 0 : return NS_OK;
862 : }
863 :
864 2147 : eProcessRule theRule = eNormalOp;
865 :
866 2538 : if (!theParentContains &&
867 330 : (IsBlockElement(aChildTag, theParentTag) &&
868 61 : IsInlineElement(theParentTag, theParentTag))) {
869 : // Don't test for table to fix 57554.
870 61 : if (eHTMLTag_li != aChildTag) {
871 61 : nsCParserNode* theParentNode = mBodyContext->PeekNode();
872 61 : if (theParentNode && theParentNode->mToken->IsWellFormed()) {
873 0 : theRule = eLetInlineContainBlock;
874 : }
875 : }
876 : }
877 :
878 2147 : switch (theRule) {
879 : case eNormalOp:
880 2147 : theChildAgrees = true;
881 2147 : if (theParentContains) {
882 1817 : eHTMLTags theAncestor = gHTMLElements[aChildTag].mRequiredAncestor;
883 1817 : if (eHTMLTag_unknown != theAncestor) {
884 0 : theChildAgrees = HasOpenContainer(theAncestor);
885 : }
886 :
887 1817 : if (theChildAgrees && theChildIsContainer) {
888 800 : if (theParentTag != aChildTag) {
889 : // Double check the power structure
890 : // Note: The bit is currently set on tags such as <A> and <LI>.
891 772 : if (gHTMLElements[aChildTag].ShouldVerifyHierarchy()) {
892 : PRInt32 theChildIndex =
893 : nsHTMLElement::GetIndexOfChildOrSynonym(*mBodyContext,
894 490 : aChildTag);
895 :
896 490 : if (kNotFound < theChildIndex && theChildIndex < theIndex) {
897 : /*
898 : * 1 Here's a tricky case from bug 22596: <h5><li><h5>
899 : * How do we know that the 2nd <h5> should close the <LI>
900 : * rather than nest inside the <LI>? (Afterall, the <h5>
901 : * is a legal child of the <LI>).
902 : *
903 : * The way you know is that there is no root between the
904 : * two, so the <h5> binds more tightly to the 1st <h5>
905 : * than to the <LI>.
906 : *
907 : * 2 Also, bug 6148 shows this case: <SPAN><DIV><SPAN>
908 : * From this case we learned not to execute this logic if
909 : * the parent is a block.
910 : *
911 : * 3 Fix for 26583:
912 : * <A href=foo.html><B>foo<A href-bar.html>bar</A></B></A>
913 : * In the above example clicking on "foo" or "bar" should
914 : * link to foo.html or bar.html respectively. That is,
915 : * the inner <A> should be informed about the presence of
916 : * an open <A> above <B>..so that the inner <A> can close
917 : * out the outer <A>. The following code does it for us.
918 : *
919 : * 4 Fix for 27865 [ similer to 22596 ]. Ex:
920 : * <DL><DD><LI>one<DD><LI>two
921 : */
922 62 : theChildAgrees = CanBeContained(aChildTag, *mBodyContext);
923 : }
924 : }
925 : }
926 : }
927 : }
928 :
929 2147 : if (!(theParentContains && theChildAgrees)) {
930 330 : if (!CanPropagate(theParentTag, aChildTag, theParentContains)) {
931 330 : if (theChildIsContainer || !theParentContains) {
932 330 : if (!theChildAgrees &&
933 0 : !gHTMLElements[aChildTag].CanAutoCloseTag(*mBodyContext,
934 : theIndex,
935 0 : aChildTag)) {
936 : // Closing the tags above might cause non-compatible results.
937 : // Ex. <TABLE><TR><TD><TBODY>Text</TD></TR></TABLE>.
938 : // In the example above <TBODY> is badly misplaced, but
939 : // we should not attempt to close the tags above it,
940 : // The safest thing to do is to discard this tag.
941 : // XXX We get the above example wrong anyway because of
942 : // CanBeContained.
943 0 : return result;
944 330 : } else if (mBodyContext->mContextTopIndex > 0 &&
945 : theIndex <= mBodyContext->mContextTopIndex) {
946 : // Looks like the parent tag does not want to contain the
947 : // current tag ( aChildTag ). However, we have to force the
948 : // containment, when handling misplaced content, to avoid data
949 : // loss. Ref. bug 138577.
950 0 : theParentContains = true;
951 : } else {
952 330 : CloseContainersTo(theIndex, aChildTag, true);
953 : }
954 : } else {
955 0 : break;
956 : }
957 : } else {
958 0 : CreateContextStackFor(theParentTag, aChildTag);
959 0 : theIndex = mBodyContext->GetCount();
960 : }
961 : }
962 2147 : break;
963 :
964 : case eLetInlineContainBlock:
965 : // Break out of this loop and open the block.
966 0 : theParentContains = theChildAgrees = true;
967 0 : break;
968 :
969 : default:
970 0 : NS_NOTREACHED("Invalid rule detected");
971 : }
972 3964 : } while (!(theParentContains && theChildAgrees));
973 : }
974 :
975 1817 : if (theChildIsContainer) {
976 800 : result = OpenContainer(aNode, aChildTag);
977 : } else {
978 1017 : result = AddLeaf(aNode);
979 : }
980 :
981 1817 : return result;
982 : }
983 :
984 : nsresult
985 1967 : CNavDTD::WillHandleStartTag(CToken* aToken, eHTMLTags aTag,
986 : nsIParserNode& aNode)
987 : {
988 1967 : nsresult result = NS_OK;
989 :
990 1967 : PRInt32 stackDepth = mBodyContext->GetCount();
991 1967 : if (stackDepth >= FONTSTYLE_IGNORE_DEPTH &&
992 0 : gHTMLElements[aTag].IsMemberOf(kFontStyle)) {
993 : // Prevent bug 58917 by tossing the new kFontStyle start tag
994 0 : return kHierarchyTooDeep;
995 : }
996 :
997 1967 : if (stackDepth >= PHRASE_IGNORE_DEPTH &&
998 0 : gHTMLElements[aTag].IsMemberOf(kPhrase)) {
999 : // Prevent bug 58917 by tossing the new kPhrase start tag
1000 0 : return kHierarchyTooDeep;
1001 : }
1002 :
1003 : /*
1004 : * Now a little code to deal with bug #49687 (crash when layout stack gets
1005 : * too deep) I've also opened this up to any container (not just inlines):
1006 : * re bug 55095 Improved to handle bug 55980 (infinite loop caused when
1007 : * DEPTH is exceeded and </P> is encountered by itself (<P>) is continuously
1008 : * produced.
1009 : */
1010 1967 : if (stackDepth > MAX_REFLOW_DEPTH) {
1011 0 : if (nsHTMLElement::IsContainer(aTag) &&
1012 0 : !gHTMLElements[aTag].HasSpecialProperty(kHandleStrayTag)) {
1013 : // Ref. bug 98261,49678,55095,55980
1014 : // Instead of throwing away the current tag close it's parent
1015 : // such that the stack level does not go beyond the max_reflow_depth.
1016 : // This would allow leaf tags, that follow the current tag, to find
1017 : // the correct node.
1018 0 : while (stackDepth != MAX_REFLOW_DEPTH && NS_SUCCEEDED(result)) {
1019 0 : result = CloseContainersTo(mBodyContext->Last(), false);
1020 0 : --stackDepth;
1021 : }
1022 : }
1023 : }
1024 :
1025 1967 : return result;
1026 : }
1027 :
1028 : static void
1029 0 : PushMisplacedAttributes(nsIParserNode& aNode, nsDeque& aDeque)
1030 : {
1031 0 : nsCParserNode& theAttrNode = static_cast<nsCParserNode &>(aNode);
1032 :
1033 0 : for (PRInt32 count = aNode.GetAttributeCount(); count > 0; --count) {
1034 0 : CToken* theAttrToken = theAttrNode.PopAttributeTokenFront();
1035 0 : if (theAttrToken) {
1036 0 : theAttrToken->SetNewlineCount(0);
1037 0 : aDeque.Push(theAttrToken);
1038 : }
1039 : }
1040 0 : }
1041 :
1042 : void
1043 0 : CNavDTD::HandleOmittedTag(CToken* aToken, eHTMLTags aChildTag,
1044 : eHTMLTags aParent, nsIParserNode* aNode)
1045 : {
1046 0 : NS_PRECONDITION(mBodyContext != nsnull, "need a context to work with");
1047 :
1048 : // The trick here is to see if the parent can contain the child, but prefers
1049 : // not to. Only if the parent CANNOT contain the child should we look to see
1050 : // if it's potentially a child of another section. If it is, the cache it for
1051 : // later.
1052 0 : PRInt32 theTagCount = mBodyContext->GetCount();
1053 0 : bool pushToken = false;
1054 :
1055 0 : if (gHTMLElements[aParent].HasSpecialProperty(kBadContentWatch) &&
1056 0 : !nsHTMLElement::IsWhitespaceTag(aChildTag)) {
1057 0 : eHTMLTags theTag = eHTMLTag_unknown;
1058 :
1059 : // Don't bother saving misplaced stuff in the head. This can happen in
1060 : // cases like |<head><noscript><table>foo|. See bug 401169.
1061 0 : if (mFlags & NS_DTD_FLAG_HAS_OPEN_HEAD) {
1062 0 : NS_ASSERTION(!(mFlags & NS_DTD_FLAG_HAS_MAIN_CONTAINER),
1063 : "Bad state");
1064 0 : return;
1065 : }
1066 :
1067 : // Determine the insertion point
1068 0 : while (theTagCount > 0) {
1069 0 : theTag = mBodyContext->TagAt(--theTagCount);
1070 0 : if (!gHTMLElements[theTag].HasSpecialProperty(kBadContentWatch)) {
1071 : // This is our insertion point.
1072 0 : mBodyContext->mContextTopIndex = theTagCount;
1073 0 : break;
1074 : }
1075 : }
1076 :
1077 0 : if (mBodyContext->mContextTopIndex > -1) {
1078 0 : pushToken = true;
1079 :
1080 : // Remember that we've stashed some misplaced content.
1081 0 : mFlags |= NS_DTD_FLAG_MISPLACED_CONTENT;
1082 : }
1083 : }
1084 :
1085 0 : if (aChildTag != aParent &&
1086 0 : gHTMLElements[aParent].HasSpecialProperty(kSaveMisplaced)) {
1087 0 : NS_ASSERTION(!pushToken, "A strange element has both kBadContentWatch "
1088 : "and kSaveMisplaced");
1089 0 : pushToken = true;
1090 : }
1091 :
1092 0 : if (pushToken) {
1093 : // Hold on to this token for later use. Ref Bug. 53695
1094 0 : IF_HOLD(aToken);
1095 0 : PushIntoMisplacedStack(aToken);
1096 :
1097 : // If the token is attributed then save those attributes too.
1098 0 : PushMisplacedAttributes(*aNode, mMisplacedContent);
1099 : }
1100 : }
1101 :
1102 : /**
1103 : * This method gets called when a kegen token is found.
1104 : *
1105 : * @update harishd 05/02/00
1106 : * @param aNode -- CParserNode representing keygen
1107 : * @return NS_OK if all went well; ERROR if error occurred
1108 : */
1109 : nsresult
1110 0 : CNavDTD::HandleKeyGen(nsIParserNode* aNode)
1111 : {
1112 0 : nsresult result = NS_OK;
1113 :
1114 : nsCOMPtr<nsIFormProcessor> theFormProcessor =
1115 0 : do_GetService(kFormProcessorCID, &result);
1116 0 : if (NS_FAILED(result)) {
1117 0 : return result;
1118 : }
1119 :
1120 0 : PRInt32 theAttrCount = aNode->GetAttributeCount();
1121 0 : nsTArray<nsString> theContent;
1122 0 : nsAutoString theAttribute;
1123 0 : nsAutoString theFormType;
1124 0 : CToken* theToken = nsnull;
1125 :
1126 0 : theFormType.AssignLiteral("select");
1127 :
1128 0 : result = theFormProcessor->ProvideContent(theFormType, theContent,
1129 0 : theAttribute);
1130 0 : if (NS_FAILED(result)) {
1131 0 : return result;
1132 : }
1133 0 : PRInt32 theIndex = nsnull;
1134 :
1135 : // Populate the tokenizer with the fabricated elements in the reverse
1136 : // order such that <SELECT> is on the top fo the tokenizer followed by
1137 : // <OPTION>s and </SELECT>.
1138 : theToken = mTokenAllocator->CreateTokenOfType(eToken_end,
1139 0 : eHTMLTag_select);
1140 0 : NS_ENSURE_TRUE(theToken, NS_ERROR_OUT_OF_MEMORY);
1141 0 : mTokenizer->PushTokenFront(theToken);
1142 :
1143 0 : for (theIndex = theContent.Length()-1; theIndex > -1; --theIndex) {
1144 : theToken = mTokenAllocator->CreateTokenOfType(eToken_text,
1145 : eHTMLTag_text,
1146 0 : theContent[theIndex]);
1147 0 : NS_ENSURE_TRUE(theToken, NS_ERROR_OUT_OF_MEMORY);
1148 0 : mTokenizer->PushTokenFront(theToken);
1149 :
1150 : theToken = mTokenAllocator->CreateTokenOfType(eToken_start,
1151 0 : eHTMLTag_option);
1152 0 : NS_ENSURE_TRUE(theToken, NS_ERROR_OUT_OF_MEMORY);
1153 0 : mTokenizer->PushTokenFront(theToken);
1154 : }
1155 :
1156 : // The attribute ( provided by the form processor ) should be a part
1157 : // of the SELECT. Placing the attribute token on the tokenizer to get
1158 : // picked up by the SELECT.
1159 : theToken = mTokenAllocator->CreateTokenOfType(eToken_attribute,
1160 : eHTMLTag_unknown,
1161 0 : theAttribute);
1162 0 : NS_ENSURE_TRUE(theToken, NS_ERROR_OUT_OF_MEMORY);
1163 :
1164 0 : ((CAttributeToken*)theToken)->SetKey(NS_LITERAL_STRING("_moz-type"));
1165 0 : mTokenizer->PushTokenFront(theToken);
1166 :
1167 : // Pop out NAME and CHALLENGE attributes ( from the keygen NODE ) and
1168 : // place it in the tokenizer such that the attribtues get sucked into
1169 : // SELECT node.
1170 0 : for (theIndex = theAttrCount; theIndex > 0; --theIndex) {
1171 0 : mTokenizer->PushTokenFront(((nsCParserNode*)aNode)->PopAttributeToken());
1172 : }
1173 :
1174 : theToken = mTokenAllocator->CreateTokenOfType(eToken_start,
1175 0 : eHTMLTag_select);
1176 0 : NS_ENSURE_TRUE(theToken, NS_ERROR_OUT_OF_MEMORY);
1177 :
1178 : // Increment the count because of the additional attribute from the form processor.
1179 0 : theToken->SetAttributeCount(theAttrCount + 1);
1180 0 : mTokenizer->PushTokenFront(theToken);
1181 :
1182 0 : return result;
1183 : }
1184 :
1185 : bool
1186 54 : CNavDTD::IsAlternateTag(eHTMLTags aTag)
1187 : {
1188 54 : switch (aTag) {
1189 : case eHTMLTag_noembed:
1190 0 : return true;
1191 :
1192 : case eHTMLTag_noscript:
1193 0 : return (mFlags & NS_IPARSER_FLAG_SCRIPT_ENABLED) != 0;
1194 :
1195 : case eHTMLTag_iframe:
1196 : case eHTMLTag_noframes:
1197 0 : return (mFlags & NS_IPARSER_FLAG_FRAMES_ENABLED) != 0;
1198 :
1199 : default:
1200 54 : return false;
1201 : }
1202 : }
1203 :
1204 : nsresult
1205 1967 : CNavDTD::HandleStartToken(CToken* aToken)
1206 : {
1207 1967 : NS_PRECONDITION(nsnull != aToken, kNullToken);
1208 :
1209 1967 : nsCParserNode* theNode = mNodeAllocator.CreateNode(aToken, mTokenAllocator);
1210 1967 : NS_ENSURE_TRUE(theNode, NS_ERROR_OUT_OF_MEMORY);
1211 :
1212 1967 : eHTMLTags theChildTag = (eHTMLTags)aToken->GetTypeID();
1213 1967 : PRInt16 attrCount = aToken->GetAttributeCount();
1214 1967 : eHTMLTags theParent = mBodyContext->Last();
1215 1967 : nsresult result = NS_OK;
1216 :
1217 1967 : if (attrCount > 0) {
1218 237 : result = CollectAttributes(theNode, theChildTag, attrCount);
1219 : }
1220 :
1221 1967 : if (NS_OK == result) {
1222 1967 : result = WillHandleStartTag(aToken, theChildTag, *theNode);
1223 1967 : if (NS_OK == result) {
1224 1967 : bool isTokenHandled = false;
1225 1967 : bool theHeadIsParent = false;
1226 :
1227 1967 : if (nsHTMLElement::IsSectionTag(theChildTag)) {
1228 56 : switch (theChildTag) {
1229 : case eHTMLTag_html:
1230 28 : if (mBodyContext->GetCount() > 0) {
1231 0 : result = OpenContainer(theNode, theChildTag);
1232 0 : isTokenHandled = true;
1233 : }
1234 28 : break;
1235 :
1236 : case eHTMLTag_body:
1237 28 : if (mFlags & NS_DTD_FLAG_HAS_OPEN_BODY) {
1238 0 : result = OpenContainer(theNode, theChildTag);
1239 0 : isTokenHandled=true;
1240 : }
1241 28 : break;
1242 :
1243 : case eHTMLTag_head:
1244 0 : mFlags |= NS_DTD_FLAG_HAS_EXPLICIT_HEAD;
1245 :
1246 0 : if (mFlags & NS_DTD_FLAG_HAS_MAIN_CONTAINER) {
1247 0 : HandleOmittedTag(aToken, theChildTag, theParent, theNode);
1248 0 : isTokenHandled = true;
1249 : }
1250 0 : break;
1251 :
1252 : default:
1253 0 : break;
1254 : }
1255 : }
1256 :
1257 1967 : bool isExclusive = false;
1258 1967 : theHeadIsParent = nsHTMLElement::IsChildOfHead(theChildTag, isExclusive);
1259 :
1260 1967 : switch (theChildTag) {
1261 : case eHTMLTag_area:
1262 0 : if (!mOpenMapCount) {
1263 0 : isTokenHandled = true;
1264 : }
1265 :
1266 0 : if (mOpenMapCount > 0 && mSink) {
1267 0 : result = mSink->AddLeaf(*theNode);
1268 0 : isTokenHandled = true;
1269 : }
1270 :
1271 0 : break;
1272 :
1273 : case eHTMLTag_image:
1274 0 : aToken->SetTypeID(theChildTag = eHTMLTag_img);
1275 0 : break;
1276 :
1277 : case eHTMLTag_keygen:
1278 0 : result = HandleKeyGen(theNode);
1279 0 : isTokenHandled = true;
1280 0 : break;
1281 :
1282 : case eHTMLTag_script:
1283 : // Script isn't really exclusively in the head. However, we treat it
1284 : // as such to make sure that we don't pull scripts outside the head
1285 : // into the body.
1286 : // XXX Where does the script go in a frameset document?
1287 0 : isExclusive = !(mFlags & NS_DTD_FLAG_HAD_BODY);
1288 0 : break;
1289 :
1290 : default:;
1291 : }
1292 :
1293 1967 : if (!isTokenHandled) {
1294 : bool prefersBody =
1295 1967 : gHTMLElements[theChildTag].HasSpecialProperty(kPreferBody);
1296 :
1297 : // If this tag prefers to be in the head (when neither the head nor the
1298 : // body have been explicitly opened) then check that we haven't seen the
1299 : // body (true until the <body> tag has really been seen). Otherwise,
1300 : // check if the head has been explicitly opened. See bug 307122.
1301 : theHeadIsParent = theHeadIsParent &&
1302 : (isExclusive ||
1303 : (prefersBody
1304 : ? (mFlags & NS_DTD_FLAG_HAS_EXPLICIT_HEAD) &&
1305 : (mFlags & NS_DTD_FLAG_HAS_OPEN_HEAD)
1306 1967 : : !(mFlags & NS_DTD_FLAG_HAS_MAIN_CONTAINER)));
1307 :
1308 1967 : if (theHeadIsParent) {
1309 : // These tokens prefer to be in the head.
1310 150 : result = AddHeadContent(theNode);
1311 : } else {
1312 1817 : result = HandleDefaultStartToken(aToken, theChildTag, theNode);
1313 : }
1314 : }
1315 :
1316 : // Now do any post processing necessary on the tag...
1317 1967 : if (NS_OK == result) {
1318 1967 : DidHandleStartTag(*theNode, theChildTag);
1319 : }
1320 : }
1321 : }
1322 :
1323 1967 : if (kHierarchyTooDeep == result) {
1324 : // Reset this error to ok; all that happens here is that given inline tag
1325 : // gets dropped because the stack is too deep. Don't terminate parsing.
1326 0 : result = NS_OK;
1327 : }
1328 :
1329 1967 : IF_FREE(theNode, &mNodeAllocator);
1330 1967 : return result;
1331 : }
1332 :
1333 : /**
1334 : * Call this to see if you have a closeable peer on the stack that
1335 : * is ABOVE one of its root tags.
1336 : *
1337 : * @update gess 4/11/99
1338 : * @param aRootTagList -- list of root tags for aTag
1339 : * @param aTag -- tag to test for containership
1340 : * @return true if given tag can contain other tags
1341 : */
1342 : static bool
1343 86 : HasCloseablePeerAboveRoot(const TagList& aRootTagList, nsDTDContext& aContext,
1344 : eHTMLTags aTag, bool anEndTag)
1345 : {
1346 86 : PRInt32 theRootIndex = LastOf(aContext, aRootTagList);
1347 : const TagList* theCloseTags = anEndTag
1348 86 : ? gHTMLElements[aTag].GetAutoCloseEndTags()
1349 172 : : gHTMLElements[aTag].GetAutoCloseStartTags();
1350 86 : PRInt32 theChildIndex = -1;
1351 :
1352 86 : if (theCloseTags) {
1353 0 : theChildIndex=LastOf(aContext, *theCloseTags);
1354 86 : } else if (anEndTag || !gHTMLElements[aTag].CanContainSelf()) {
1355 86 : theChildIndex = aContext.LastOf(aTag);
1356 : }
1357 :
1358 : // I changed this to theRootIndex<=theChildIndex so to handle this case:
1359 : // <SELECT><OPTGROUP>...</OPTGROUP>
1360 86 : return theRootIndex<=theChildIndex;
1361 : }
1362 :
1363 :
1364 : /**
1365 : * This method is called to determine whether or not an END tag
1366 : * can be autoclosed. This means that based on the current
1367 : * context, the stack should be closed to the nearest matching
1368 : * tag.
1369 : *
1370 : * @param aTag -- tag enum of child to be tested
1371 : * @return true if autoclosure should occur
1372 : */
1373 : static eHTMLTags
1374 344 : FindAutoCloseTargetForEndTag(eHTMLTags aCurrentTag, nsDTDContext& aContext,
1375 : nsDTDMode aMode)
1376 : {
1377 344 : int theTopIndex = aContext.GetCount();
1378 344 : eHTMLTags thePrevTag = aContext.Last();
1379 :
1380 344 : if (nsHTMLElement::IsContainer(aCurrentTag)) {
1381 : PRInt32 theChildIndex =
1382 344 : nsHTMLElement::GetIndexOfChildOrSynonym(aContext, aCurrentTag);
1383 :
1384 344 : if (kNotFound < theChildIndex) {
1385 344 : if (thePrevTag == aContext[theChildIndex]) {
1386 258 : return aContext[theChildIndex];
1387 : }
1388 :
1389 86 : if (nsHTMLElement::IsBlockCloser(aCurrentTag)) {
1390 : /*
1391 : * Here's the algorithm:
1392 : * Our here is sitting at aChildIndex. There are other tags above it
1393 : * on the stack. We have to try to close them out, but we may encounter
1394 : * one that can block us. The way to tell is by comparing each tag on
1395 : * the stack against our closeTag and rootTag list.
1396 : *
1397 : * For each tag above our hero on the stack, ask 3 questions:
1398 : * 1. Is it in the closeTag list? If so, the we can skip over it
1399 : * 2. Is it in the rootTag list? If so, then we're gated by it
1400 : * 3. Otherwise its non-specified and we simply presume we can close it.
1401 : */
1402 : const TagList* theCloseTags =
1403 86 : gHTMLElements[aCurrentTag].GetAutoCloseEndTags();
1404 : const TagList* theRootTags =
1405 86 : gHTMLElements[aCurrentTag].GetEndRootTags();
1406 :
1407 86 : if (theCloseTags) {
1408 : // At a mininimum, this code is needed for H1..H6
1409 0 : while (theChildIndex < --theTopIndex) {
1410 0 : eHTMLTags theNextTag = aContext[theTopIndex];
1411 0 : if (!FindTagInSet(theNextTag, theCloseTags->mTags,
1412 0 : theCloseTags->mCount) &&
1413 : FindTagInSet(theNextTag, theRootTags->mTags,
1414 0 : theRootTags->mCount)) {
1415 : // We encountered a tag in root list so fail (we're gated).
1416 0 : return eHTMLTag_unknown;
1417 : }
1418 :
1419 : // Otherwise, presume it's something we can simply ignore and
1420 : // continue searching.
1421 : }
1422 :
1423 0 : eHTMLTags theTarget = aContext.TagAt(theChildIndex);
1424 0 : if (aCurrentTag != theTarget) {
1425 0 : aCurrentTag = theTarget;
1426 : }
1427 : // If you make it here, we're ungated and found a target!
1428 0 : return aCurrentTag;
1429 86 : } else if (theRootTags) {
1430 : // Since we didn't find any close tags, see if there is an instance of
1431 : // aCurrentTag above the stack from the roottag.
1432 86 : if (HasCloseablePeerAboveRoot(*theRootTags, aContext, aCurrentTag,
1433 : true)) {
1434 86 : return aCurrentTag;
1435 : } else {
1436 0 : return eHTMLTag_unknown;
1437 : }
1438 : }
1439 : } else {
1440 : // Ok, a much more sensible approach for non-block closers; use the tag
1441 : // group to determine closure: For example: %phrasal closes %phrasal,
1442 : // %fontstyle and %special
1443 0 : return gHTMLElements[aCurrentTag].GetCloseTargetForEndTag(aContext,
1444 : theChildIndex,
1445 0 : aMode);
1446 : }
1447 : }
1448 : }
1449 :
1450 0 : return eHTMLTag_unknown;
1451 : }
1452 :
1453 : static void
1454 0 : StripWSFollowingTag(eHTMLTags aChildTag, nsITokenizer* aTokenizer,
1455 : nsTokenAllocator* aTokenAllocator, PRInt32* aNewlineCount)
1456 : {
1457 0 : if (!aTokenizer || !aTokenAllocator) {
1458 0 : return;
1459 : }
1460 :
1461 0 : CToken* theToken = aTokenizer->PeekToken();
1462 :
1463 0 : PRInt32 newlineCount = 0;
1464 0 : while (theToken) {
1465 0 : eHTMLTokenTypes theType = eHTMLTokenTypes(theToken->GetTokenType());
1466 :
1467 0 : switch(theType) {
1468 : case eToken_newline:
1469 : case eToken_whitespace:
1470 0 : theToken = aTokenizer->PopToken();
1471 0 : newlineCount += theToken->GetNewlineCount();
1472 0 : IF_FREE(theToken, aTokenAllocator);
1473 :
1474 0 : theToken = aTokenizer->PeekToken();
1475 0 : break;
1476 :
1477 : default:
1478 0 : theToken = nsnull;
1479 0 : break;
1480 : }
1481 : }
1482 :
1483 0 : if (aNewlineCount) {
1484 0 : *aNewlineCount += newlineCount;
1485 : }
1486 : }
1487 :
1488 : /**
1489 : * This method gets called when an end token has been
1490 : * encountered in the parse process. If the end tag matches
1491 : * the start tag on the stack, then simply close it. Otherwise,
1492 : * we have a erroneous state condition. This can be because we
1493 : * have a close tag with no prior open tag (user error) or because
1494 : * we screwed something up in the parse process. I'm not sure
1495 : * yet how to tell the difference.
1496 : *
1497 : * @param aToken -- next (start) token to be handled
1498 : * @return true if all went well; false if error occurred
1499 : */
1500 : nsresult
1501 344 : CNavDTD::HandleEndToken(CToken* aToken)
1502 : {
1503 344 : NS_PRECONDITION(nsnull != aToken, kNullToken);
1504 :
1505 344 : nsresult result = NS_OK;
1506 344 : eHTMLTags theChildTag = (eHTMLTags)aToken->GetTypeID();
1507 :
1508 : // Begin by dumping any attributes (bug 143512)
1509 344 : CollectAttributes(nsnull, theChildTag, aToken->GetAttributeCount());
1510 :
1511 344 : switch (theChildTag) {
1512 : case eHTMLTag_link:
1513 : case eHTMLTag_meta:
1514 0 : break;
1515 :
1516 : case eHTMLTag_head:
1517 : StripWSFollowingTag(theChildTag, mTokenizer, mTokenAllocator,
1518 0 : !mCountLines ? nsnull : &mLineNumber);
1519 0 : if (mBodyContext->LastOf(eHTMLTag_head) != kNotFound) {
1520 0 : result = CloseContainersTo(eHTMLTag_head, false);
1521 : }
1522 0 : mFlags &= ~NS_DTD_FLAG_HAS_EXPLICIT_HEAD;
1523 0 : break;
1524 :
1525 : case eHTMLTag_form:
1526 0 : result = CloseContainer(eHTMLTag_form, false);
1527 0 : break;
1528 :
1529 : case eHTMLTag_br:
1530 : {
1531 : // This is special NAV-QUIRKS code that allows users to use </BR>, even
1532 : // though that isn't a legitimate tag.
1533 0 : if (eDTDMode_quirks == mDTDMode) {
1534 : // Use recycler and pass the token thro' HandleToken() to fix bugs
1535 : // like 32782.
1536 : CToken* theToken = mTokenAllocator->CreateTokenOfType(eToken_start,
1537 0 : theChildTag);
1538 0 : result = HandleToken(theToken);
1539 : }
1540 : }
1541 0 : break;
1542 :
1543 : case eHTMLTag_body:
1544 : case eHTMLTag_html:
1545 : StripWSFollowingTag(theChildTag, mTokenizer, mTokenAllocator,
1546 0 : !mCountLines ? nsnull : &mLineNumber);
1547 0 : break;
1548 :
1549 : case eHTMLTag_script:
1550 : // Note: we don't fall through to the default case because
1551 : // CloseContainersTo() has the bad habit of closing tags that are opened
1552 : // by document.write(). Fortunately, the tokenizer guarantees that no
1553 : // actual tags appear between <script> and </script> so we won't be
1554 : // closing the wrong tag.
1555 0 : if (mBodyContext->Last() != eHTMLTag_script) {
1556 : // Except if we're here, then there's probably a stray script tag.
1557 0 : NS_ASSERTION(mBodyContext->LastOf(eHTMLTag_script) == kNotFound,
1558 : "Mishandling scripts in CNavDTD");
1559 0 : break;
1560 : }
1561 :
1562 0 : mBodyContext->Pop();
1563 0 : result = CloseContainer(eHTMLTag_script, aToken->IsInError());
1564 0 : break;
1565 :
1566 : default:
1567 : {
1568 : // Now check to see if this token should be omitted, or
1569 : // if it's gated from closing by the presence of another tag.
1570 344 : if (gHTMLElements[theChildTag].CanOmitEndTag()) {
1571 0 : PopStyle(theChildTag);
1572 : } else {
1573 344 : eHTMLTags theParentTag = mBodyContext->Last();
1574 :
1575 : // First open transient styles, so that we see any autoclosed style
1576 : // tags.
1577 344 : if (nsHTMLElement::IsResidualStyleTag(theChildTag)) {
1578 147 : result = OpenTransientStyles(theChildTag);
1579 147 : if (NS_FAILED(result)) {
1580 0 : return result;
1581 : }
1582 : }
1583 :
1584 344 : if (kNotFound ==
1585 : nsHTMLElement::GetIndexOfChildOrSynonym(*mBodyContext,
1586 344 : theChildTag)) {
1587 : // Ref: bug 30487
1588 : // Make sure that we don't cross boundaries, of certain elements,
1589 : // to close stylistic information.
1590 : // <font face="helvetica"><table><tr><td></font></td></tr></table> some text...
1591 : // In the above ex. the orphaned FONT tag, inside TD, should cross
1592 : // TD boundary to close the FONT tag above TABLE.
1593 : static eHTMLTags gBarriers[] = {
1594 : eHTMLTag_thead, eHTMLTag_tbody, eHTMLTag_tfoot, eHTMLTag_table
1595 : };
1596 :
1597 0 : if (!FindTagInSet(theParentTag, gBarriers,
1598 0 : ArrayLength(gBarriers)) &&
1599 0 : nsHTMLElement::IsResidualStyleTag(theChildTag)) {
1600 : // Fix bug 77746
1601 0 : mBodyContext->RemoveStyle(theChildTag);
1602 : }
1603 :
1604 : // If the bit kHandleStrayTag is set then we automatically open up a
1605 : // matching start tag (compatibility). Currently this bit is set on
1606 : // P tag. This also fixes Bug: 22623
1607 0 : if (gHTMLElements[theChildTag].HasSpecialProperty(kHandleStrayTag) &&
1608 : mDTDMode != eDTDMode_full_standards &&
1609 : mDTDMode != eDTDMode_almost_standards) {
1610 : // Oh boy!! we found a "stray" tag. Nav4.x and IE introduce line
1611 : // break in such cases. So, let's simulate that effect for
1612 : // compatibility.
1613 : // Ex. <html><body>Hello</P>There</body></html>
1614 0 : PRInt32 theParentContains = -1;
1615 0 : if (!CanOmit(theParentTag, theChildTag, theParentContains)) {
1616 : CToken* theStartToken =
1617 0 : mTokenAllocator->CreateTokenOfType(eToken_start, theChildTag);
1618 0 : NS_ENSURE_TRUE(theStartToken, NS_ERROR_OUT_OF_MEMORY);
1619 :
1620 : // This check for NS_DTD_FLAG_IN_MISPLACED_CONTENT was added
1621 : // to fix bug 142965.
1622 0 : if (!(mFlags & NS_DTD_FLAG_IN_MISPLACED_CONTENT)) {
1623 : // We're not handling misplaced content right now, just push
1624 : // these new tokens back on the stack and handle them in the
1625 : // regular flow of HandleToken.
1626 0 : IF_HOLD(aToken);
1627 0 : mTokenizer->PushTokenFront(aToken);
1628 0 : mTokenizer->PushTokenFront(theStartToken);
1629 : } else {
1630 : // Oops, we're in misplaced content. Handle these tokens
1631 : // directly instead of trying to push them onto the tokenizer
1632 : // stack.
1633 0 : result = HandleToken(theStartToken);
1634 0 : NS_ENSURE_SUCCESS(result, result);
1635 :
1636 0 : IF_HOLD(aToken);
1637 0 : result = HandleToken(aToken);
1638 : }
1639 : }
1640 : }
1641 0 : return result;
1642 : }
1643 344 : if (result == NS_OK) {
1644 : eHTMLTags theTarget =
1645 : FindAutoCloseTargetForEndTag(theChildTag, *mBodyContext,
1646 344 : mDTDMode);
1647 344 : if (eHTMLTag_unknown != theTarget) {
1648 344 : result = CloseContainersTo(theTarget, false);
1649 : }
1650 : }
1651 : }
1652 : }
1653 344 : break;
1654 : }
1655 :
1656 344 : return result;
1657 : }
1658 :
1659 : /**
1660 : * This method will be triggered when the end of a table is
1661 : * encountered. Its primary purpose is to process all the
1662 : * bad-contents pertaining a particular table. The position
1663 : * of the table is the token bank ID.
1664 : *
1665 : * @update harishd 03/24/99
1666 : * @param aTag - This ought to be a table tag
1667 : *
1668 : */
1669 : nsresult
1670 0 : CNavDTD::HandleSavedTokens(PRInt32 anIndex)
1671 : {
1672 0 : NS_PRECONDITION(mBodyContext != nsnull && mBodyContext->GetCount() > 0, "invalid context");
1673 :
1674 0 : nsresult result = NS_OK;
1675 :
1676 0 : if (mSink && (anIndex > kNotFound)) {
1677 0 : PRInt32 theBadTokenCount = mMisplacedContent.GetSize();
1678 :
1679 0 : if (theBadTokenCount > 0) {
1680 0 : mFlags |= NS_DTD_FLAG_IN_MISPLACED_CONTENT;
1681 :
1682 0 : if (!mTempContext && !(mTempContext = new nsDTDContext())) {
1683 0 : return NS_ERROR_OUT_OF_MEMORY;
1684 : }
1685 :
1686 : CToken* theToken;
1687 : eHTMLTags theTag;
1688 : PRInt32 attrCount;
1689 0 : PRInt32 theTopIndex = anIndex + 1;
1690 0 : PRInt32 theTagCount = mBodyContext->GetCount();
1691 :
1692 : // Pause the main context and switch to the new context.
1693 0 : result = mSink->BeginContext(anIndex);
1694 :
1695 0 : NS_ENSURE_SUCCESS(result, result);
1696 :
1697 : // The body context should contain contents only upto the marked position.
1698 0 : mBodyContext->MoveEntries(*mTempContext, theTagCount - theTopIndex);
1699 :
1700 : // Now flush out all the bad contents.
1701 0 : while (theBadTokenCount-- > 0){
1702 0 : theToken = (CToken*)mMisplacedContent.PopFront();
1703 0 : if (theToken) {
1704 0 : theTag = (eHTMLTags)theToken->GetTypeID();
1705 0 : attrCount = theToken->GetAttributeCount();
1706 : // Put back attributes, which once got popped out, into the
1707 : // tokenizer. Make sure we preserve their ordering, however!
1708 : // XXXbz would it be faster to get the tokens out with ObjectAt and
1709 : // put them in the tokenizer and then PopFront them all from
1710 : // mMisplacedContent?
1711 0 : nsDeque temp;
1712 0 : for (PRInt32 j = 0; j < attrCount; ++j) {
1713 0 : CToken* theAttrToken = (CToken*)mMisplacedContent.PopFront();
1714 0 : if (theAttrToken) {
1715 0 : temp.Push(theAttrToken);
1716 : }
1717 0 : theBadTokenCount--;
1718 : }
1719 0 : mTokenizer->PrependTokens(temp);
1720 :
1721 0 : if (eToken_end == theToken->GetTokenType()) {
1722 : // Ref: Bug 25202
1723 : // Make sure that the BeginContext() is ended only by the call to
1724 : // EndContext(). Ex: <center><table><a></center>.
1725 : // In the Ex. above </center> should not close <center> above table.
1726 : // Doing so will cause the current context to get closed prematurely.
1727 : eHTMLTags closed = FindAutoCloseTargetForEndTag(theTag, *mBodyContext,
1728 0 : mDTDMode);
1729 : PRInt32 theIndex = closed != eHTMLTag_unknown
1730 0 : ? mBodyContext->LastOf(closed)
1731 0 : : kNotFound;
1732 :
1733 0 : if (theIndex != kNotFound &&
1734 : theIndex <= mBodyContext->mContextTopIndex) {
1735 0 : IF_FREE(theToken, mTokenAllocator);
1736 0 : continue;
1737 : }
1738 : }
1739 :
1740 : // XXX This should go away, with this call, it becomes extremely
1741 : // difficult to handle misplaced style and link tags, since it's hard
1742 : // to propagate the block return all the way up and then re-enter this
1743 : // method.
1744 0 : result = HandleToken(theToken);
1745 : }
1746 : }
1747 :
1748 0 : if (theTopIndex != mBodyContext->GetCount()) {
1749 : // CloseContainersTo does not close any forms we might have opened while
1750 : // handling saved tokens, because the parser does not track forms on its
1751 : // mBodyContext stack.
1752 : CloseContainersTo(theTopIndex, mBodyContext->TagAt(theTopIndex),
1753 0 : true);
1754 : }
1755 :
1756 : // Bad-contents were successfully processed. Now, itz time to get
1757 : // back to the original body context state.
1758 0 : mTempContext->MoveEntries(*mBodyContext, theTagCount - theTopIndex);
1759 :
1760 : // Terminate the new context and switch back to the main context
1761 0 : mSink->EndContext(anIndex);
1762 :
1763 0 : mFlags &= ~NS_DTD_FLAG_IN_MISPLACED_CONTENT;
1764 : }
1765 : }
1766 0 : return result;
1767 : }
1768 :
1769 :
1770 : /**
1771 : * This method gets called when an entity token has been
1772 : * encountered in the parse process.
1773 : *
1774 : * @update gess 3/25/98
1775 : * @param aToken -- next (start) token to be handled
1776 : * @return true if all went well; false if error occurred
1777 : */
1778 : nsresult
1779 0 : CNavDTD::HandleEntityToken(CToken* aToken)
1780 : {
1781 0 : NS_PRECONDITION(nsnull != aToken, kNullToken);
1782 :
1783 0 : nsresult result = NS_OK;
1784 :
1785 0 : const nsSubstring& theStr = aToken->GetStringValue();
1786 :
1787 0 : if (kHashsign != theStr.First() &&
1788 0 : -1 == nsHTMLEntities::EntityToUnicode(theStr)) {
1789 : // If you're here we have a bogus entity.
1790 : // Convert it into a text token.
1791 : CToken *theToken;
1792 :
1793 0 : nsAutoString entityName;
1794 0 : entityName.AssignLiteral("&");
1795 0 : entityName.Append(theStr);
1796 : theToken = mTokenAllocator->CreateTokenOfType(eToken_text, eHTMLTag_text,
1797 0 : entityName);
1798 0 : NS_ENSURE_TRUE(theToken, NS_ERROR_OUT_OF_MEMORY);
1799 :
1800 : // theToken should get recycled automagically...
1801 0 : return HandleToken(theToken);
1802 : }
1803 :
1804 0 : eHTMLTags theParentTag = mBodyContext->Last();
1805 0 : nsCParserNode* theNode = mNodeAllocator.CreateNode(aToken, mTokenAllocator);
1806 0 : NS_ENSURE_TRUE(theNode, NS_ERROR_OUT_OF_MEMORY);
1807 :
1808 0 : PRInt32 theParentContains = -1;
1809 0 : if (CanOmit(theParentTag, eHTMLTag_entity, theParentContains)) {
1810 0 : eHTMLTags theCurrTag = (eHTMLTags)aToken->GetTypeID();
1811 0 : HandleOmittedTag(aToken, theCurrTag, theParentTag, theNode);
1812 : } else {
1813 0 : result = AddLeaf(theNode);
1814 : }
1815 0 : IF_FREE(theNode, &mNodeAllocator);
1816 0 : return result;
1817 : }
1818 :
1819 : /**
1820 : * This method gets called when a comment token has been
1821 : * encountered in the parse process. After making sure
1822 : * we're somewhere in the body, we handle the comment
1823 : * in the same code that we use for text.
1824 : *
1825 : * @update gess 3/25/98
1826 : * @param aToken -- next (start) token to be handled
1827 : * @return true if all went well; false if error occurred
1828 : */
1829 : nsresult
1830 25 : CNavDTD::HandleCommentToken(CToken* aToken)
1831 : {
1832 25 : NS_PRECONDITION(nsnull != aToken, kNullToken);
1833 25 : return NS_OK;
1834 : }
1835 :
1836 :
1837 : /**
1838 : * This method gets called when an attribute token has been
1839 : * encountered in the parse process. This is an error, since
1840 : * all attributes should have been accounted for in the prior
1841 : * start or end tokens
1842 : *
1843 : * @update gess 3/25/98
1844 : * @param aToken -- next (start) token to be handled
1845 : * @return true if all went well; false if error occurred
1846 : */
1847 : nsresult
1848 0 : CNavDTD::HandleAttributeToken(CToken* aToken)
1849 : {
1850 0 : NS_ERROR("attribute encountered -- this shouldn't happen.");
1851 0 : return NS_OK;
1852 : }
1853 :
1854 : /**
1855 : * This method gets called when an "instruction" token has been
1856 : * encountered in the parse process.
1857 : *
1858 : * @update gess 3/25/98
1859 : * @param aToken -- next (start) token to be handled
1860 : * @return true if all went well; false if error occurred
1861 : */
1862 : nsresult
1863 0 : CNavDTD::HandleProcessingInstructionToken(CToken* aToken)
1864 : {
1865 0 : NS_PRECONDITION(nsnull != aToken, kNullToken);
1866 0 : return NS_OK;
1867 : }
1868 :
1869 : /**
1870 : * This method gets called when a DOCTYPE token has been
1871 : * encountered in the parse process.
1872 : *
1873 : * @update harishd 09/02/99
1874 : * @param aToken -- The very first token to be handled
1875 : * @return true if all went well; false if error occurred
1876 : */
1877 : nsresult
1878 25 : CNavDTD::HandleDocTypeDeclToken(CToken* aToken)
1879 : {
1880 25 : NS_PRECONDITION(nsnull != aToken, kNullToken);
1881 :
1882 25 : CDoctypeDeclToken* theToken = static_cast<CDoctypeDeclToken*>(aToken);
1883 50 : nsAutoString docTypeStr(theToken->GetStringValue());
1884 : // XXX Doesn't this count the newlines twice?
1885 25 : if (mCountLines) {
1886 25 : mLineNumber += docTypeStr.CountChar(kNewLine);
1887 : }
1888 :
1889 25 : PRInt32 len = docTypeStr.Length();
1890 25 : PRInt32 pos = docTypeStr.RFindChar(kGreaterThan);
1891 25 : if (pos != kNotFound) {
1892 : // First remove '>' from the end.
1893 25 : docTypeStr.Cut(pos, len - pos);
1894 : }
1895 :
1896 : // Now remove "<!" from the begining
1897 25 : docTypeStr.Cut(0, 2);
1898 25 : theToken->SetStringValue(docTypeStr);
1899 25 : return NS_OK;
1900 : }
1901 :
1902 : /**
1903 : * Retrieve the attributes for this node, and add then into
1904 : * the node.
1905 : *
1906 : * @update gess4/22/98
1907 : * @param aNode is the node you want to collect attributes for
1908 : * @param aCount is the # of attributes you're expecting
1909 : * @return error code (should be 0)
1910 : */
1911 : nsresult
1912 581 : CNavDTD::CollectAttributes(nsIParserNode *aNode, eHTMLTags aTag, PRInt32 aCount)
1913 : {
1914 581 : int attr = 0;
1915 581 : nsresult result = NS_OK;
1916 581 : int theAvailTokenCount = mTokenizer->GetCount();
1917 :
1918 581 : if (aCount <= theAvailTokenCount) {
1919 : CToken* theToken;
1920 1313 : for (attr = 0; attr < aCount; ++attr) {
1921 732 : theToken = mTokenizer->PopToken();
1922 732 : if (theToken) {
1923 732 : eHTMLTokenTypes theType = eHTMLTokenTypes(theToken->GetTokenType());
1924 732 : if (theType != eToken_attribute) {
1925 : // If you're here then it means that the token does not
1926 : // belong to this node. Put the token back into the tokenizer
1927 : // and let it go thro' the regular path. Bug: 59189.
1928 0 : mTokenizer->PushTokenFront(theToken);
1929 0 : break;
1930 : }
1931 :
1932 732 : if (mCountLines) {
1933 732 : mLineNumber += theToken->GetNewlineCount();
1934 : }
1935 :
1936 732 : if (aNode) {
1937 : // If the key is empty, the attribute is unusable, so we should not
1938 : // add it to the node.
1939 732 : if (!((CAttributeToken*)theToken)->GetKey().IsEmpty()) {
1940 732 : aNode->AddAttribute(theToken);
1941 : } else {
1942 0 : IF_FREE(theToken, mTokenAllocator);
1943 : }
1944 : } else {
1945 0 : IF_FREE(theToken, mTokenAllocator);
1946 : }
1947 : }
1948 : }
1949 : } else {
1950 0 : result = kEOF;
1951 : }
1952 581 : return result;
1953 : }
1954 :
1955 : /**
1956 : * This method is called to determine whether or not a tag
1957 : * of one type can contain a tag of another type.
1958 : *
1959 : * @update gess 4/8/98
1960 : * @param aParent -- tag enum of parent container
1961 : * @param aChild -- tag enum of child container
1962 : * @return true if parent can contain child
1963 : */
1964 : NS_IMETHODIMP_(bool)
1965 3003 : CNavDTD::CanContain(PRInt32 aParent, PRInt32 aChild) const
1966 : {
1967 3003 : bool result = gHTMLElements[aParent].CanContain((eHTMLTags)aChild, mDTDMode);
1968 :
1969 3003 : if (eHTMLTag_nobr == aChild &&
1970 0 : IsInlineElement(aParent, aParent) &&
1971 0 : HasOpenContainer(eHTMLTag_nobr)) {
1972 0 : return false;
1973 : }
1974 :
1975 3003 : return result;
1976 : }
1977 :
1978 : /**
1979 : * This method is called to determine whether or not
1980 : * the given childtag is a block element.
1981 : *
1982 : * @update gess 6June2000
1983 : * @param aChildID -- tag id of child
1984 : * @param aParentID -- tag id of parent (or eHTMLTag_unknown)
1985 : * @return true if this tag is a block tag
1986 : */
1987 : bool
1988 330 : CNavDTD::IsBlockElement(PRInt32 aTagID, PRInt32 aParentID) const
1989 : {
1990 330 : eHTMLTags theTag = (eHTMLTags)aTagID;
1991 :
1992 : return (theTag > eHTMLTag_unknown && theTag < eHTMLTag_userdefined) &&
1993 330 : (gHTMLElements[theTag].IsMemberOf(kBlock) ||
1994 330 : gHTMLElements[theTag].IsMemberOf(kBlockEntity) ||
1995 269 : gHTMLElements[theTag].IsMemberOf(kHeading) ||
1996 269 : gHTMLElements[theTag].IsMemberOf(kPreformatted) ||
1997 1528 : gHTMLElements[theTag].IsMemberOf(kList));
1998 : }
1999 :
2000 : /**
2001 : * This method is called to determine whether or not
2002 : * the given childtag is an inline element.
2003 : *
2004 : * @update gess 6June2000
2005 : * @param aChildID -- tag id of child
2006 : * @param aParentID -- tag id of parent (or eHTMLTag_unknown)
2007 : * @return true if this tag is an inline tag
2008 : */
2009 : bool
2010 61 : CNavDTD::IsInlineElement(PRInt32 aTagID, PRInt32 aParentID) const
2011 : {
2012 61 : eHTMLTags theTag = (eHTMLTags)aTagID;
2013 :
2014 : return (theTag > eHTMLTag_unknown && theTag < eHTMLTag_userdefined) &&
2015 61 : (gHTMLElements[theTag].IsMemberOf(kInlineEntity) ||
2016 0 : gHTMLElements[theTag].IsMemberOf(kFontStyle) ||
2017 0 : gHTMLElements[theTag].IsMemberOf(kPhrase) ||
2018 0 : gHTMLElements[theTag].IsMemberOf(kSpecial) ||
2019 122 : gHTMLElements[theTag].IsMemberOf(kFormControl));
2020 : }
2021 :
2022 : /**
2023 : * This method is called to determine whether or not
2024 : * the necessary intermediate tags should be propagated
2025 : * between the given parent and given child.
2026 : *
2027 : * @update gess 4/8/98
2028 : * @param aParent -- tag enum of parent container
2029 : * @param aChild -- tag enum of child container
2030 : * @return true if propagation should occur
2031 : */
2032 : bool
2033 330 : CNavDTD::CanPropagate(eHTMLTags aParent, eHTMLTags aChild,
2034 : PRInt32 aParentContains)
2035 : {
2036 330 : bool result = false;
2037 330 : if (aParentContains == -1) {
2038 0 : aParentContains = CanContain(aParent, aChild);
2039 : }
2040 :
2041 330 : if (aParent == aChild) {
2042 83 : return result;
2043 : }
2044 :
2045 247 : if (nsHTMLElement::IsContainer(aChild)) {
2046 247 : mScratch.Truncate();
2047 247 : if (!gHTMLElements[aChild].HasSpecialProperty(kNoPropagate)) {
2048 172 : if (nsHTMLElement::IsBlockParent(aParent) ||
2049 86 : gHTMLElements[aParent].GetSpecialChildren()) {
2050 0 : result = ForwardPropagate(mScratch, aParent, aChild);
2051 0 : if (!result) {
2052 0 : if (eHTMLTag_unknown != aParent) {
2053 0 : result = BackwardPropagate(mScratch, aParent, aChild);
2054 : } else {
2055 0 : result = BackwardPropagate(mScratch, eHTMLTag_html, aChild);
2056 : }
2057 : }
2058 : }
2059 : }
2060 247 : if (mScratch.Length() - 1 > gHTMLElements[aParent].mPropagateRange) {
2061 247 : result = false;
2062 : }
2063 : } else {
2064 0 : result = !!aParentContains;
2065 : }
2066 :
2067 :
2068 247 : return result;
2069 : }
2070 :
2071 :
2072 : /**
2073 : * This method gets called to determine whether a given
2074 : * tag can be omitted from opening. Most cannot.
2075 : *
2076 : * @param aParent
2077 : * @param aChild
2078 : * @param aParentContains
2079 : * @return true if given tag can contain other tags
2080 : */
2081 : bool
2082 2147 : CNavDTD::CanOmit(eHTMLTags aParent, eHTMLTags aChild, PRInt32& aParentContains)
2083 : {
2084 2147 : eHTMLTags theAncestor = gHTMLElements[aChild].mExcludingAncestor;
2085 2147 : if (eHTMLTag_unknown != theAncestor && HasOpenContainer(theAncestor)) {
2086 0 : return true;
2087 : }
2088 :
2089 2147 : theAncestor = gHTMLElements[aChild].mRequiredAncestor;
2090 2147 : if (eHTMLTag_unknown != theAncestor) {
2091 : // If there's a required ancestor, we only omit if it isn't open and we
2092 : // can't get to it through propagation.
2093 0 : return !HasOpenContainer(theAncestor) &&
2094 0 : !CanPropagate(aParent, aChild, aParentContains);
2095 : }
2096 :
2097 2147 : if (gHTMLElements[aParent].CanExclude(aChild)) {
2098 0 : return true;
2099 : }
2100 :
2101 : // Now the obvious test: if the parent can contain the child, don't omit.
2102 2147 : if (-1 == aParentContains) {
2103 0 : aParentContains = CanContain(aParent, aChild);
2104 : }
2105 :
2106 2147 : if (aParentContains || aChild == aParent) {
2107 1900 : return false;
2108 : }
2109 :
2110 247 : if (gHTMLElements[aParent].IsBlockEntity() &&
2111 0 : nsHTMLElement::IsInlineEntity(aChild)) {
2112 : // Feel free to drop inlines that a block doesn't contain.
2113 0 : return true;
2114 : }
2115 :
2116 247 : if (gHTMLElements[aParent].HasSpecialProperty(kBadContentWatch)) {
2117 : // We can only omit this child if it does not have the kBadContentWatch
2118 : // special property.
2119 0 : return !gHTMLElements[aChild].HasSpecialProperty(kBadContentWatch);
2120 : }
2121 :
2122 247 : if (gHTMLElements[aParent].HasSpecialProperty(kSaveMisplaced)) {
2123 0 : return true;
2124 : }
2125 :
2126 247 : if (aParent == eHTMLTag_body) {
2127 : // There are very few tags that the body does not contain. If we get here
2128 : // the best thing to do is just drop them.
2129 0 : return true;
2130 : }
2131 :
2132 247 : return false;
2133 : }
2134 :
2135 : /**
2136 : * This method gets called to determine whether a given
2137 : * tag is itself a container
2138 : *
2139 : * @update gess 4/8/98
2140 : * @param aTag -- tag to test as a container
2141 : * @return true if given tag can contain other tags
2142 : */
2143 : NS_IMETHODIMP_(bool)
2144 0 : CNavDTD::IsContainer(PRInt32 aTag) const
2145 : {
2146 0 : return nsHTMLElement::IsContainer((eHTMLTags)aTag);
2147 : }
2148 :
2149 :
2150 : bool
2151 0 : CNavDTD::ForwardPropagate(nsString& aSequence, eHTMLTags aParent,
2152 : eHTMLTags aChild)
2153 : {
2154 0 : bool result = false;
2155 :
2156 0 : switch(aParent) {
2157 : case eHTMLTag_table:
2158 0 : if (eHTMLTag_tr == aChild || eHTMLTag_td == aChild) {
2159 0 : return BackwardPropagate(aSequence, aParent, aChild);
2160 : }
2161 : // Otherwise, intentionally fall through...
2162 :
2163 : case eHTMLTag_tr:
2164 0 : if (CanContain(eHTMLTag_td, aChild)) {
2165 0 : aSequence.Append((PRUnichar)eHTMLTag_td);
2166 0 : result = BackwardPropagate(aSequence, aParent, eHTMLTag_td);
2167 : }
2168 0 : break;
2169 :
2170 : default:
2171 0 : break;
2172 : }
2173 :
2174 0 : return result;
2175 : }
2176 :
2177 : bool
2178 0 : CNavDTD::BackwardPropagate(nsString& aSequence, eHTMLTags aParent,
2179 : eHTMLTags aChild) const
2180 : {
2181 0 : eHTMLTags theParent = aParent;
2182 :
2183 0 : do {
2184 0 : const TagList* theRootTags = gHTMLElements[aChild].GetRootTags();
2185 0 : if (!theRootTags) {
2186 0 : break;
2187 : }
2188 :
2189 0 : theParent = theRootTags->mTags[0];
2190 0 : NS_ASSERTION(CanContain(theParent, aChild),
2191 : "Children must be contained by their root tags");
2192 :
2193 0 : aChild = theParent;
2194 0 : aSequence.Append((PRUnichar)theParent);
2195 : } while (theParent != eHTMLTag_unknown && theParent != aParent);
2196 :
2197 0 : return aParent == theParent;
2198 : }
2199 :
2200 109 : bool CNavDTD::HasOpenContainer(eHTMLTags aContainer) const
2201 : {
2202 109 : switch (aContainer) {
2203 : case eHTMLTag_form:
2204 0 : return !(~mFlags & NS_DTD_FLAG_HAS_OPEN_FORM);
2205 : case eHTMLTag_map:
2206 0 : return mOpenMapCount > 0;
2207 : default:
2208 109 : return mBodyContext->HasOpenContainer(aContainer);
2209 : }
2210 : }
2211 :
2212 : bool
2213 25 : CNavDTD::HasOpenContainer(const eHTMLTags aTagSet[], PRInt32 aCount) const
2214 : {
2215 : int theIndex;
2216 25 : int theTopIndex = mBodyContext->GetCount() - 1;
2217 :
2218 50 : for (theIndex = theTopIndex; theIndex > 0; --theIndex) {
2219 25 : if (FindTagInSet((*mBodyContext)[theIndex], aTagSet, aCount)) {
2220 0 : return true;
2221 : }
2222 : }
2223 :
2224 25 : return false;
2225 : }
2226 :
2227 : eHTMLTags
2228 0 : CNavDTD::GetTopNode() const
2229 : {
2230 0 : return mBodyContext->Last();
2231 : }
2232 :
2233 : /**
2234 : * It is with great trepidation that I offer this method (privately of course).
2235 : * The gets called whenever a container gets opened. This methods job is to
2236 : * take a look at the (transient) style stack, and open any style containers that
2237 : * are there. Of course, we shouldn't bother to open styles that are incompatible
2238 : * with our parent container.
2239 : *
2240 : * @update gess6/4/98
2241 : * @param tag of the container just opened
2242 : * @return 0 (for now)
2243 : */
2244 : nsresult
2245 1311 : CNavDTD::OpenTransientStyles(eHTMLTags aChildTag, bool aCloseInvalid)
2246 : {
2247 1311 : nsresult result = NS_OK;
2248 :
2249 : // No need to open transient styles in head context - Fix for 41427
2250 2192 : if ((mFlags & NS_DTD_FLAG_ENABLE_RESIDUAL_STYLE) &&
2251 : eHTMLTag_newline != aChildTag &&
2252 881 : !(mFlags & NS_DTD_FLAG_HAS_OPEN_HEAD)) {
2253 856 : if (CanContain(eHTMLTag_font, aChildTag)) {
2254 856 : PRUint32 theCount = mBodyContext->GetCount();
2255 856 : PRUint32 theLevel = theCount;
2256 :
2257 : // This first loop is used to determine how far up the containment
2258 : // hierarchy we go looking for residual styles.
2259 5325 : while (1 < theLevel) {
2260 3613 : eHTMLTags theParentTag = mBodyContext->TagAt(--theLevel);
2261 3613 : if (gHTMLElements[theParentTag].HasSpecialProperty(kNoStyleLeaksIn)) {
2262 0 : break;
2263 : }
2264 : }
2265 :
2266 856 : mFlags &= ~NS_DTD_FLAG_ENABLE_RESIDUAL_STYLE;
2267 4469 : for (; theLevel < theCount; ++theLevel) {
2268 3613 : nsEntryStack* theStack = mBodyContext->GetStylesAt(theLevel);
2269 3613 : if (theStack) {
2270 : // Don't open transient styles if it makes the stack deep, bug 58917.
2271 0 : if (theCount + theStack->mCount >= FONTSTYLE_IGNORE_DEPTH) {
2272 0 : break;
2273 : }
2274 :
2275 0 : PRInt32 sindex = 0;
2276 :
2277 0 : nsTagEntry *theEntry = theStack->mEntries;
2278 0 : bool isHeadingOpen = HasOpenTagOfType(kHeading, *mBodyContext);
2279 0 : for (sindex = 0; sindex < theStack->mCount; ++sindex) {
2280 0 : nsCParserNode* theNode = (nsCParserNode*)theEntry->mNode;
2281 0 : if (1 == theNode->mUseCount) {
2282 0 : eHTMLTags theNodeTag = (eHTMLTags)theNode->GetNodeType();
2283 0 : if (gHTMLElements[theNodeTag].CanContain(aChildTag, mDTDMode)) {
2284 : // XXX The following comment is entirely incoherent.
2285 : // We do this too, because this entry differs from the new one
2286 : // we're pushing.
2287 0 : theEntry->mParent = theStack;
2288 0 : if (isHeadingOpen) {
2289 : // Bug 77352
2290 : // The style system needs to identify residual style tags
2291 : // within heading tags so that heading tags' size can take
2292 : // precedence over the residual style tags' size info..
2293 : // *Note: Make sure that this attribute is transient since it
2294 : // should not get carried over to cases other than heading.
2295 0 : CAttributeToken theAttrToken(NS_LITERAL_STRING("_moz-rs-heading"),
2296 0 : EmptyString());
2297 0 : theNode->AddAttribute(&theAttrToken);
2298 0 : result = OpenContainer(theNode, theNodeTag, theStack);
2299 0 : theNode->PopAttributeToken();
2300 : } else {
2301 0 : result = OpenContainer(theNode, theNodeTag, theStack);
2302 : }
2303 0 : } else if (aCloseInvalid) {
2304 : // If the node tag can't contain the child tag, then remove the
2305 : // child tag from the style stack
2306 0 : nsCParserNode* node = theStack->Remove(sindex, theNodeTag);
2307 0 : IF_FREE(node, &mNodeAllocator);
2308 0 : --theEntry;
2309 : }
2310 : }
2311 0 : ++theEntry;
2312 : }
2313 : }
2314 : }
2315 856 : mFlags |= NS_DTD_FLAG_ENABLE_RESIDUAL_STYLE;
2316 : }
2317 : }
2318 :
2319 1311 : return result;
2320 : }
2321 :
2322 : /**
2323 : * This method gets called when an explicit style close-tag is encountered.
2324 : * It results in the style tag id being popped from our internal style stack.
2325 : *
2326 : * @update gess6/4/98
2327 : * @param
2328 : * @return 0 if all went well (which it always does)
2329 : */
2330 : void
2331 0 : CNavDTD::PopStyle(eHTMLTags aTag)
2332 : {
2333 0 : if ((mFlags & NS_DTD_FLAG_ENABLE_RESIDUAL_STYLE) &&
2334 0 : nsHTMLElement::IsResidualStyleTag(aTag)) {
2335 0 : nsCParserNode* node = mBodyContext->PopStyle(aTag);
2336 0 : IF_FREE(node, &mNodeAllocator);
2337 : }
2338 0 : }
2339 :
2340 :
2341 : /**
2342 : * This method does two things: 1st, help construct
2343 : * our own internal model of the content-stack; and
2344 : * 2nd, pass this message on to the sink.
2345 : *
2346 : * @update gess4/22/98
2347 : * @param aNode -- next node to be added to model
2348 : */
2349 : nsresult
2350 28 : CNavDTD::OpenHTML(const nsCParserNode *aNode)
2351 : {
2352 28 : NS_PRECONDITION(mBodyContext->GetCount() >= 0, kInvalidTagStackPos);
2353 :
2354 28 : nsresult result = mSink ? mSink->OpenContainer(*aNode) : NS_OK;
2355 :
2356 : // Don't push more than one HTML tag into the stack.
2357 28 : if (mBodyContext->GetCount() == 0) {
2358 28 : mBodyContext->Push(const_cast<nsCParserNode*>(aNode), 0, false);
2359 : }
2360 :
2361 28 : return result;
2362 : }
2363 :
2364 : /**
2365 : * This method does two things: 1st, help construct
2366 : * our own internal model of the content-stack; and
2367 : * 2nd, pass this message on to the sink.
2368 : * @update gess4/6/98
2369 : * @param aNode -- next node to be added to model
2370 : * @return TRUE if ok, FALSE if error
2371 : */
2372 : nsresult
2373 28 : CNavDTD::OpenBody(const nsCParserNode *aNode)
2374 : {
2375 28 : NS_PRECONDITION(mBodyContext->GetCount() >= 0, kInvalidTagStackPos);
2376 :
2377 28 : nsresult result = NS_OK;
2378 :
2379 28 : if (!(mFlags & NS_DTD_FLAG_HAD_FRAMESET)) {
2380 28 : mFlags |= NS_DTD_FLAG_HAD_BODY;
2381 :
2382 : // Make sure the head is closed by the time the body is opened.
2383 28 : CloseContainer(eHTMLTag_head, false);
2384 :
2385 : // Now we can open the body.
2386 28 : result = mSink ? mSink->OpenContainer(*aNode) : NS_OK;
2387 :
2388 28 : if (!HasOpenContainer(eHTMLTag_body)) {
2389 28 : mBodyContext->Push(const_cast<nsCParserNode*>(aNode), 0, false);
2390 28 : mTokenizer->PrependTokens(mMisplacedContent);
2391 : }
2392 : }
2393 :
2394 28 : return result;
2395 : }
2396 :
2397 : /**
2398 : * This method does two things: 1st, help construct
2399 : * our own internal model of the content-stack; and
2400 : * 2nd, pass this message on to the sink.
2401 : * @update gess4/6/98
2402 : * @param aNode -- next node to be added to model
2403 : * @param aClosedByStartTag -- ONLY TRUE if the container is being closed by opening of another container.
2404 : * @return TRUE if ok, FALSE if error
2405 : */
2406 : nsresult
2407 800 : CNavDTD::OpenContainer(const nsCParserNode *aNode,
2408 : eHTMLTags aTag,
2409 : nsEntryStack* aStyleStack)
2410 : {
2411 800 : NS_PRECONDITION(mBodyContext->GetCount() >= 0, kInvalidTagStackPos);
2412 :
2413 800 : nsresult result = NS_OK;
2414 800 : bool done = true;
2415 800 : bool rs_tag = nsHTMLElement::IsResidualStyleTag(aTag);
2416 : // We need to open transient styles to encompass the <li> so that the bullets
2417 : // inherit the proper colors.
2418 800 : bool li_tag = aTag == eHTMLTag_li;
2419 :
2420 800 : if (rs_tag || li_tag) {
2421 : /*
2422 : * Here's an interesting problem:
2423 : *
2424 : * If there's an <a> on the RS-stack, and you're trying to open
2425 : * another <a>, the one on the RS-stack should be discarded.
2426 : *
2427 : * I'm updating OpenTransientStyles to throw old <a>'s away.
2428 : */
2429 147 : OpenTransientStyles(aTag, !li_tag);
2430 : }
2431 :
2432 800 : switch (aTag) {
2433 : case eHTMLTag_html:
2434 28 : result = OpenHTML(aNode);
2435 28 : break;
2436 :
2437 : case eHTMLTag_head:
2438 0 : if (!(mFlags & NS_DTD_FLAG_HAS_OPEN_HEAD)) {
2439 0 : mFlags |= NS_DTD_FLAG_HAS_OPEN_HEAD;
2440 0 : done = false;
2441 : }
2442 0 : break;
2443 :
2444 : case eHTMLTag_body:
2445 : {
2446 28 : eHTMLTags theParent = mBodyContext->Last();
2447 28 : if (!gHTMLElements[aTag].IsSpecialParent(theParent)) {
2448 28 : mFlags |= NS_DTD_FLAG_HAS_OPEN_BODY;
2449 28 : result = OpenBody(aNode);
2450 : } else {
2451 0 : done = false;
2452 : }
2453 : }
2454 28 : break;
2455 :
2456 : case eHTMLTag_map:
2457 0 : ++mOpenMapCount;
2458 0 : done = false;
2459 0 : break;
2460 :
2461 : case eHTMLTag_form:
2462 : // Discard nested forms - bug 72639
2463 0 : if (!(mFlags & NS_DTD_FLAG_HAS_OPEN_FORM)) {
2464 0 : mFlags |= NS_DTD_FLAG_HAS_OPEN_FORM;
2465 0 : result = mSink ? mSink->OpenContainer(*aNode) : NS_OK;
2466 : }
2467 0 : break;
2468 :
2469 : case eHTMLTag_frameset:
2470 : // Make sure that the head is closed before we try to open this frameset.
2471 0 : CloseContainer(eHTMLTag_head, false);
2472 :
2473 : // Now that the head is closed, continue on with opening this frameset.
2474 0 : mFlags |= NS_DTD_FLAG_HAD_FRAMESET;
2475 0 : done = false;
2476 0 : break;
2477 :
2478 : case eHTMLTag_noembed:
2479 : // <noembed> is unconditionally alternate content.
2480 0 : done = false;
2481 0 : mFlags |= NS_DTD_FLAG_ALTERNATE_CONTENT;
2482 0 : break;
2483 :
2484 : case eHTMLTag_noscript:
2485 : // We want to make sure that OpenContainer gets called below since we're
2486 : // not doing it here
2487 0 : done = false;
2488 :
2489 0 : if (mFlags & NS_IPARSER_FLAG_SCRIPT_ENABLED) {
2490 : // XXX This flag doesn't currently do anything (and would be
2491 : // insufficient if it did).
2492 0 : mFlags |= NS_DTD_FLAG_ALTERNATE_CONTENT;
2493 : }
2494 0 : break;
2495 :
2496 : case eHTMLTag_iframe: // Bug 84491
2497 : case eHTMLTag_noframes:
2498 0 : done = false;
2499 0 : if (mFlags & NS_IPARSER_FLAG_FRAMES_ENABLED) {
2500 0 : mFlags |= NS_DTD_FLAG_ALTERNATE_CONTENT;
2501 : }
2502 0 : break;
2503 :
2504 : default:
2505 744 : done = false;
2506 744 : break;
2507 : }
2508 :
2509 800 : if (!done) {
2510 :
2511 744 : result = mSink ? mSink->OpenContainer(*aNode) : NS_OK;
2512 :
2513 : // For residual style tags rs_tag will be true and hence
2514 : // the body context will hold an extra reference to the node.
2515 744 : mBodyContext->Push(const_cast<nsCParserNode*>(aNode), aStyleStack, rs_tag);
2516 : }
2517 :
2518 800 : return result;
2519 : }
2520 :
2521 : nsresult
2522 0 : CNavDTD::CloseResidualStyleTags(const eHTMLTags aTag,
2523 : bool aClosedByStartTag)
2524 : {
2525 0 : const PRInt32 count = mBodyContext->GetCount();
2526 0 : PRInt32 pos = count;
2527 0 : while (nsHTMLElement::IsResidualStyleTag(mBodyContext->TagAt(pos - 1)))
2528 0 : --pos;
2529 0 : if (pos < count)
2530 0 : return CloseContainersTo(pos, aTag, aClosedByStartTag);
2531 0 : return NS_OK;
2532 : }
2533 :
2534 : /**
2535 : * This method does two things: 1st, help construct
2536 : * our own internal model of the content-stack; and
2537 : * 2nd, pass this message on to the sink.
2538 : * @update gess4/6/98
2539 : * @param aTag -- id of tag to be closed
2540 : * @return TRUE if ok, FALSE if error
2541 : */
2542 : nsresult
2543 878 : CNavDTD::CloseContainer(const eHTMLTags aTag, bool aMalformed)
2544 : {
2545 878 : nsresult result = NS_OK;
2546 878 : bool done = true;
2547 :
2548 878 : switch (aTag) {
2549 : case eHTMLTag_head:
2550 53 : if (mFlags & NS_DTD_FLAG_HAS_OPEN_HEAD) {
2551 25 : mFlags &= ~NS_DTD_FLAG_HAS_OPEN_HEAD;
2552 25 : if (mBodyContext->Last() == eHTMLTag_head) {
2553 0 : mBodyContext->Pop();
2554 : } else {
2555 : // This else can happen because CloseContainer is called both directly
2556 : // and from CloseContainersTo. CloseContainersTo pops the current tag
2557 : // off of the stack before calling CloseContainer.
2558 25 : NS_ASSERTION(mBodyContext->LastOf(eHTMLTag_head) == kNotFound,
2559 : "Closing the wrong tag");
2560 : }
2561 25 : done = false;
2562 : }
2563 53 : break;
2564 :
2565 : case eHTMLTag_map:
2566 0 : if (mOpenMapCount) {
2567 0 : mOpenMapCount--;
2568 0 : done = false;
2569 : }
2570 0 : break;
2571 :
2572 : case eHTMLTag_form:
2573 0 : if (mFlags & NS_DTD_FLAG_HAS_OPEN_FORM) {
2574 0 : mFlags &= ~NS_DTD_FLAG_HAS_OPEN_FORM;
2575 0 : done = false;
2576 : // If we neglect to close these tags, the sink will refuse to close the
2577 : // form because the form will not be on the top of the SinkContext stack.
2578 : // See HTMLContentSink::CloseForm. (XXX do this in other cases?)
2579 0 : CloseResidualStyleTags(eHTMLTag_form, false);
2580 : }
2581 0 : break;
2582 :
2583 : case eHTMLTag_iframe:
2584 : case eHTMLTag_noembed:
2585 : case eHTMLTag_noscript:
2586 : case eHTMLTag_noframes:
2587 : // Switch from alternate content state to regular state.
2588 0 : mFlags &= ~NS_DTD_FLAG_ALTERNATE_CONTENT;
2589 :
2590 : // falling thro' intentionally....
2591 : default:
2592 825 : done = false;
2593 : }
2594 :
2595 878 : if (!done) {
2596 :
2597 850 : if (mSink) {
2598 850 : result = !aMalformed
2599 850 : ? mSink->CloseContainer(aTag)
2600 1700 : : mSink->CloseMalformedContainer(aTag);
2601 : }
2602 :
2603 : // If we were dealing with a head container in the body, make sure to
2604 : // close the head context now, so that body content doesn't get sucked
2605 : // into the head.
2606 850 : if (mBodyContext->GetCount() == mHeadContainerPosition) {
2607 0 : mHeadContainerPosition = -1;
2608 0 : nsresult headresult = CloseContainer(eHTMLTag_head, false);
2609 :
2610 : // Note: we could be assigning NS_OK into NS_OK here, but that's ok.
2611 : // This test is to avoid a successful CloseHead result stomping over a
2612 : // request to block the parser.
2613 0 : if (NS_SUCCEEDED(result)) {
2614 0 : result = headresult;
2615 : }
2616 : }
2617 : }
2618 :
2619 878 : return result;
2620 : }
2621 :
2622 : /**
2623 : * This method does two things: 1st, help construct
2624 : * our own internal model of the content-stack; and
2625 : * 2nd, pass this message on to the sink.
2626 : * @update gess4/6/98
2627 : * @param anIndex
2628 : * @param aTag
2629 : * @param aClosedByStartTag -- if TRUE, then we're closing something because a start tag caused it
2630 : * @return TRUE if ok, FALSE if error
2631 : */
2632 : nsresult
2633 755 : CNavDTD::CloseContainersTo(PRInt32 anIndex, eHTMLTags aTarget,
2634 : bool aClosedByStartTag)
2635 : {
2636 755 : NS_PRECONDITION(mBodyContext->GetCount() > 0, kInvalidTagStackPos);
2637 755 : nsresult result = NS_OK;
2638 :
2639 755 : if (anIndex < mBodyContext->GetCount() && anIndex >= 0) {
2640 755 : PRInt32 count = 0;
2641 2360 : while ((count = mBodyContext->GetCount()) > anIndex) {
2642 850 : nsEntryStack* theChildStyleStack = 0;
2643 850 : eHTMLTags theTag = mBodyContext->Last();
2644 850 : nsCParserNode* theNode = mBodyContext->Pop(theChildStyleStack);
2645 850 : result = CloseContainer(theTag, false);
2646 :
2647 850 : bool theTagIsStyle = nsHTMLElement::IsResidualStyleTag(theTag);
2648 : // If the current tag cannot leak out then we shouldn't leak out of the target - Fix 40713
2649 850 : bool theStyleDoesntLeakOut = gHTMLElements[theTag].HasSpecialProperty(kNoStyleLeaksOut);
2650 850 : if (!theStyleDoesntLeakOut) {
2651 850 : theStyleDoesntLeakOut = gHTMLElements[aTarget].HasSpecialProperty(kNoStyleLeaksOut);
2652 : }
2653 :
2654 : // Do not invoke residual style handling when dealing with
2655 : // alternate content. This fixed bug 25214.
2656 850 : if (theTagIsStyle && !(mFlags & NS_DTD_FLAG_ALTERNATE_CONTENT)) {
2657 147 : NS_ASSERTION(theNode, "residual style node should not be null");
2658 147 : if (!theNode) {
2659 0 : if (theChildStyleStack) {
2660 0 : mBodyContext->PushStyles(theChildStyleStack);
2661 : }
2662 0 : return NS_OK;
2663 : }
2664 :
2665 147 : bool theTargetTagIsStyle = nsHTMLElement::IsResidualStyleTag(aTarget);
2666 147 : if (aClosedByStartTag) {
2667 : // Handle closure due to new start tag.
2668 : // The cases we're handing here:
2669 : // 1. <body><b><DIV> //<b> gets pushed onto <body>.mStyles.
2670 : // 2. <body><a>text<a> //in this case, the target matches, so don't push style
2671 0 : if (theNode->mUseCount == 0) {
2672 0 : if (theTag != aTarget) {
2673 0 : if (theChildStyleStack) {
2674 0 : theChildStyleStack->PushFront(theNode);
2675 : } else {
2676 0 : mBodyContext->PushStyle(theNode);
2677 : }
2678 : }
2679 0 : } else if (theTag == aTarget && !gHTMLElements[aTarget].CanContainSelf()) {
2680 : //here's a case we missed: <a><div>text<a>text</a></div>
2681 : //The <div> pushes the 1st <a> onto the rs-stack, then the 2nd <a>
2682 : //pops the 1st <a> from the rs-stack altogether.
2683 0 : nsCParserNode* node = mBodyContext->PopStyle(theTag);
2684 0 : IF_FREE(node, &mNodeAllocator);
2685 : }
2686 :
2687 0 : if (theChildStyleStack) {
2688 0 : mBodyContext->PushStyles(theChildStyleStack);
2689 : }
2690 : } else {
2691 : /*
2692 : * if you're here, then we're dealing with the closure of tags
2693 : * caused by a close tag (as opposed to an open tag).
2694 : * At a minimum, we should consider pushing residual styles up
2695 : * up the stack or popping and recycling displaced nodes.
2696 : *
2697 : * Known cases:
2698 : * 1. <body><b><div>text</DIV>
2699 : * Here the <b> will leak into <div> (see case given above), and
2700 : * when <div> closes the <b> is dropped since it's already residual.
2701 : *
2702 : * 2. <body><div><b>text</div>
2703 : * Here the <b> will leak out of the <div> and get pushed onto
2704 : * the RS stack for the <body>, since it originated in the <div>.
2705 : *
2706 : * 3. <body><span><b>text</span>
2707 : * In this case, the the <b> get's pushed onto the style stack.
2708 : * Later we deal with RS styles stored on the <span>
2709 : *
2710 : * 4. <body><span><b>text</i>
2711 : * Here we the <b>is closed by a (synonymous) style tag.
2712 : * In this case, the <b> is simply closed.
2713 : */
2714 147 : if (theChildStyleStack) {
2715 0 : if (!theStyleDoesntLeakOut) {
2716 0 : if (theTag != aTarget) {
2717 0 : if (theNode->mUseCount == 0) {
2718 0 : theChildStyleStack->PushFront(theNode);
2719 : }
2720 0 : } else if (theNode->mUseCount == 1) {
2721 : // This fixes bug 30885,29626.
2722 : // Make sure that the node, which is about to
2723 : // get released does not stay on the style stack...
2724 : // Also be sure to remove the correct style off the
2725 : // style stack. - Ref. bug 94208.
2726 : // Ex <FONT><B><I></FONT><FONT></B></I></FONT>
2727 : // Make sure that </B> removes B off the style stack.
2728 0 : mBodyContext->RemoveStyle(theTag);
2729 : }
2730 0 : mBodyContext->PushStyles(theChildStyleStack);
2731 : } else{
2732 0 : IF_DELETE(theChildStyleStack, &mNodeAllocator);
2733 : }
2734 147 : } else if (theNode->mUseCount == 0) {
2735 : // The old version of this only pushed if the targettag wasn't
2736 : // style. But that misses this case: <font><b>text</font>, where
2737 : // the b should leak.
2738 147 : if (aTarget != theTag) {
2739 0 : mBodyContext->PushStyle(theNode);
2740 : }
2741 : } else {
2742 : // Ah, at last, the final case. If you're here, then we just popped
2743 : // a style tag that got onto that tag stack from a stylestack
2744 : // somewhere. Pop it from the stylestack if the target is also a
2745 : // style tag. Make sure to remove the matching style. In the
2746 : // following example:
2747 : // <FONT><B><I></FONT><FONT color=red></B></I></FONT>
2748 : // make sure that </I> does not remove
2749 : // <FONT color=red> off the style stack. - bug 94208
2750 0 : if (theTargetTagIsStyle && theTag == aTarget) {
2751 0 : mBodyContext->RemoveStyle(theTag);
2752 : }
2753 : }
2754 147 : }
2755 : } else {
2756 : // The tag is not a style tag.
2757 703 : if (theChildStyleStack) {
2758 0 : if (theStyleDoesntLeakOut) {
2759 0 : IF_DELETE(theChildStyleStack, &mNodeAllocator);
2760 : } else {
2761 0 : mBodyContext->PushStyles(theChildStyleStack);
2762 : }
2763 : }
2764 : }
2765 850 : IF_FREE(theNode, &mNodeAllocator);
2766 : }
2767 : }
2768 755 : return result;
2769 : }
2770 :
2771 : /**
2772 : * This method does two things: 1st, help construct
2773 : * our own internal model of the content-stack; and
2774 : * 2nd, pass this message on to the sink.
2775 : * @update gess4/6/98
2776 : * @param aTag --
2777 : * @param aClosedByStartTag -- ONLY TRUE if the container is being closed by opening of another container.
2778 : * @return TRUE if ok, FALSE if error
2779 : */
2780 : nsresult
2781 425 : CNavDTD::CloseContainersTo(eHTMLTags aTag, bool aClosedByStartTag)
2782 : {
2783 425 : NS_PRECONDITION(mBodyContext->GetCount() > 0, kInvalidTagStackPos);
2784 :
2785 425 : PRInt32 pos = mBodyContext->LastOf(aTag);
2786 :
2787 425 : if (kNotFound != pos) {
2788 : // The tag is indeed open, so close it.
2789 425 : return CloseContainersTo(pos, aTag, aClosedByStartTag);
2790 : }
2791 :
2792 0 : eHTMLTags theTopTag = mBodyContext->Last();
2793 :
2794 0 : bool theTagIsSynonymous = (nsHTMLElement::IsResidualStyleTag(aTag) &&
2795 0 : nsHTMLElement::IsResidualStyleTag(theTopTag)) ||
2796 0 : (gHTMLElements[aTag].IsMemberOf(kHeading) &&
2797 0 : gHTMLElements[theTopTag].IsMemberOf(kHeading));
2798 :
2799 0 : if (theTagIsSynonymous) {
2800 : // If you're here, it's because we're trying to close one tag,
2801 : // but a different (synonymous) one is actually open. Because this is NAV4x
2802 : // compatibililty mode, we must close the one that's really open.
2803 0 : aTag = theTopTag;
2804 0 : pos = mBodyContext->LastOf(aTag);
2805 0 : if (kNotFound != pos) {
2806 : // The tag is indeed open, so close it.
2807 0 : return CloseContainersTo(pos, aTag, aClosedByStartTag);
2808 : }
2809 : }
2810 :
2811 0 : nsresult result = NS_OK;
2812 0 : const TagList* theRootTags = gHTMLElements[aTag].GetRootTags();
2813 : // XXX Can we just bail if !theRootTags? Does that ever happen?
2814 0 : eHTMLTags theParentTag = theRootTags ? theRootTags->mTags[0] : eHTMLTag_unknown;
2815 0 : pos = mBodyContext->LastOf(theParentTag);
2816 0 : if (kNotFound != pos) {
2817 : // The parent container is open, so close it instead
2818 0 : result = CloseContainersTo(pos + 1, aTag, aClosedByStartTag);
2819 : }
2820 0 : return result;
2821 : }
2822 :
2823 : /**
2824 : * This method does two things: 1st, help construct
2825 : * our own internal model of the content-stack; and
2826 : * 2nd, pass this message on to the sink.
2827 : * @update gess4/6/98
2828 : * @param aNode -- next node to be added to model
2829 : * @return error code; 0 means OK
2830 : */
2831 : nsresult
2832 1017 : CNavDTD::AddLeaf(const nsIParserNode *aNode)
2833 : {
2834 1017 : nsresult result = NS_OK;
2835 :
2836 1017 : if (mSink) {
2837 1017 : eHTMLTags theTag = (eHTMLTags)aNode->GetNodeType();
2838 1017 : OpenTransientStyles(theTag);
2839 :
2840 1017 : result = mSink->AddLeaf(*aNode);
2841 : }
2842 :
2843 1017 : return result;
2844 : }
2845 :
2846 : /**
2847 : * Call this method ONLY when you want to write a leaf
2848 : * into the head container.
2849 : *
2850 : * @update gess 03/14/99
2851 : * @param aNode -- next node to be added to model
2852 : * @return error code; 0 means OK
2853 : */
2854 : nsresult
2855 150 : CNavDTD::AddHeadContent(nsIParserNode *aNode)
2856 : {
2857 150 : nsresult result = NS_OK;
2858 :
2859 : static eHTMLTags gNoXTags[] = { eHTMLTag_noembed, eHTMLTag_noframes };
2860 :
2861 150 : eHTMLTags theTag = (eHTMLTags)aNode->GetNodeType();
2862 :
2863 : // XXX - SCRIPT inside NOTAGS should not get executed unless the pref.
2864 : // says so. Since we don't have this support yet..lets ignore the
2865 : // SCRIPT inside NOTAGS. Ref Bug 25880.
2866 150 : if (eHTMLTag_meta == theTag || eHTMLTag_script == theTag) {
2867 25 : if (HasOpenContainer(gNoXTags, ArrayLength(gNoXTags))) {
2868 0 : return result;
2869 : }
2870 : }
2871 :
2872 150 : if (mSink) {
2873 : // Make sure the head is opened.
2874 150 : if (!(mFlags & NS_DTD_FLAG_HAS_OPEN_HEAD)) {
2875 25 : result = mSink->OpenHead();
2876 25 : mBodyContext->PushTag(eHTMLTag_head);
2877 25 : mFlags |= NS_DTD_FLAG_HAS_OPEN_HEAD;
2878 : }
2879 :
2880 : // Note: userdefined tags in the head are treated as leaves.
2881 150 : if (!nsHTMLElement::IsContainer(theTag) || theTag == eHTMLTag_userdefined) {
2882 125 : result = mSink->AddLeaf(*aNode);
2883 :
2884 125 : if (mFlags & NS_DTD_FLAG_HAS_MAIN_CONTAINER) {
2885 : // Close the head now so that body content doesn't get sucked into it.
2886 0 : CloseContainer(eHTMLTag_head, false);
2887 : }
2888 : } else {
2889 25 : if ((mFlags & NS_DTD_FLAG_HAS_MAIN_CONTAINER) &&
2890 : mHeadContainerPosition == -1) {
2891 : // Keep track of this so that we know when to close the head, when
2892 : // this tag is done with.
2893 0 : mHeadContainerPosition = mBodyContext->GetCount();
2894 : }
2895 :
2896 : // Note: The head context is already opened.
2897 25 : result = mSink->OpenContainer(*aNode);
2898 :
2899 : mBodyContext->Push(static_cast<nsCParserNode*>(aNode), nsnull,
2900 25 : false);
2901 : }
2902 : }
2903 :
2904 150 : return result;
2905 : }
2906 :
2907 : void
2908 0 : CNavDTD::CreateContextStackFor(eHTMLTags aParent, eHTMLTags aChild)
2909 : {
2910 0 : mScratch.Truncate();
2911 :
2912 0 : bool result = ForwardPropagate(mScratch, aParent, aChild);
2913 :
2914 0 : if (!result) {
2915 0 : if (eHTMLTag_unknown == aParent) {
2916 0 : result = BackwardPropagate(mScratch, eHTMLTag_html, aChild);
2917 0 : } else if (aParent != aChild) {
2918 : // Don't even bother if we're already inside a similar element...
2919 0 : result = BackwardPropagate(mScratch, aParent, aChild);
2920 : }
2921 : }
2922 :
2923 0 : if (!result) {
2924 0 : return;
2925 : }
2926 :
2927 0 : PRInt32 theLen = mScratch.Length();
2928 0 : eHTMLTags theTag = (eHTMLTags)mScratch[--theLen];
2929 :
2930 : // Now, build up the stack according to the tags.
2931 0 : while (theLen) {
2932 0 : theTag = (eHTMLTags)mScratch[--theLen];
2933 :
2934 : // Note: These tokens should all wind up on contextstack, so don't recycle
2935 : // them.
2936 0 : CToken *theToken = mTokenAllocator->CreateTokenOfType(eToken_start, theTag);
2937 0 : HandleToken(theToken);
2938 : }
2939 4392 : }
|