Bug 436663. Work around ATSUI crasher caused by long Hebrew sequence. r=roc, sr=vlad
[wine-gecko.git] / parser / htmlparser / src / nsViewSourceHTML.cpp
blobce6b536792c230815df7a16eb13723e73f9012ba
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set sw=2 ts=2 et tw=78: */
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998
21 * the Initial Developer. All Rights Reserved.
23 * Contributor(s):
24 * jce2@po.cwru.edu <Jason Eager>: Added pref to turn on/off
25 * Boris Zbarsky <bzbarsky@mit.edu>
26 * rbs@maths.uq.edu.au
27 * Andreas M. Schneider <clarence@clarence.de>
29 * Alternatively, the contents of this file may be used under the terms of
30 * either of the GNU General Public License Version 2 or later (the "GPL"),
31 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
32 * in which case the provisions of the GPL or the LGPL are applicable instead
33 * of those above. If you wish to allow use of your version of this file only
34 * under the terms of either the GPL or the LGPL, and not to allow others to
35 * use your version of this file under the terms of the MPL, indicate your
36 * decision by deleting the provisions above and replace them with the notice
37 * and other provisions required by the GPL or the LGPL. If you do not delete
38 * the provisions above, a recipient may use your version of this file under
39 * the terms of any one of the MPL, the GPL or the LGPL.
41 * ***** END LICENSE BLOCK ***** */
44 * Set NS_VIEWSOURCE_TOKENS_PER_BLOCK to 0 to disable multi-block
45 * output. Multi-block output helps reduce the amount of bidi
46 * processing we have to do on the resulting content model.
48 #define NS_VIEWSOURCE_TOKENS_PER_BLOCK 16
50 #ifdef RAPTOR_PERF_METRICS
51 # define START_TIMER() \
52 if(mParser) mParser->mParseTime.Start(PR_FALSE); \
53 if(mParser) mParser->mDTDTime.Start(PR_FALSE);
55 # define STOP_TIMER() \
56 if(mParser) mParser->mParseTime.Stop(); \
57 if(mParser) mParser->mDTDTime.Stop();
59 #else
60 # define STOP_TIMER()
61 # define START_TIMER()
62 #endif
64 #include "nsIAtom.h"
65 #include "nsViewSourceHTML.h"
66 #include "nsCRT.h"
67 #include "nsParser.h"
68 #include "nsScanner.h"
69 #include "nsIParser.h"
70 #include "nsDTDUtils.h"
71 #include "nsIContentSink.h"
72 #include "nsIHTMLContentSink.h"
73 #include "nsHTMLTokenizer.h"
74 #include "nsIPrefService.h"
75 #include "nsIPrefBranch.h"
76 #include "nsUnicharUtils.h"
77 #include "nsPrintfCString.h"
79 #include "nsIServiceManager.h"
81 #include "nsElementTable.h"
83 #include "prenv.h" //this is here for debug reasons...
84 #include "prtypes.h" //this is here for debug reasons...
85 #include "prio.h"
86 #include "plstr.h"
87 #include "prmem.h"
89 #ifdef RAPTOR_PERF_METRICS
90 #include "stopwatch.h"
91 Stopwatch vsTimer;
92 #endif
96 // Define this to dump the viewsource stuff to a file
97 //#define DUMP_TO_FILE
98 #ifdef DUMP_TO_FILE
99 #include <stdio.h>
100 FILE* gDumpFile=0;
101 static const char* gDumpFileName = "/tmp/viewsource.html";
102 // static const char* gDumpFileName = "\\temp\\viewsource.html";
103 #endif // DUMP_TO_FILE
105 // bug 22022 - these are used to toggle 'Wrap Long Lines' on the viewsource
106 // window by selectively setting/unsetting the following class defined in
107 // viewsource.css; the setting is remembered between invocations using a pref.
108 static const char kBodyId[] = "viewsource";
109 static const char kBodyClassWrap[] = "wrap";
111 NS_IMPL_ISUPPORTS1(CViewSourceHTML, nsIDTD)
113 /********************************************
114 ********************************************/
116 enum {
117 kStartTag = 0,
118 kEndTag,
119 kComment,
120 kCData,
121 kDoctype,
122 kPI,
123 kEntity,
124 kText,
125 kAttributeName,
126 kAttributeValue,
127 kMarkupDecl
130 static const char* const kElementClasses[] = {
131 "start-tag",
132 "end-tag",
133 "comment",
134 "cdata",
135 "doctype",
136 "pi",
137 "entity",
138 "text",
139 "attribute-name",
140 "attribute-value",
141 "markupdeclaration"
144 static const char* const kBeforeText[] = {
145 "<",
146 "</",
151 "&",
154 "=",
158 static const char* const kAfterText[] = {
159 ">",
160 ">",
172 #ifdef DUMP_TO_FILE
173 static const char* const kDumpFileBeforeText[] = {
174 "&lt;",
175 "&lt;/",
180 "&amp;",
183 "=",
187 static const char* const kDumpFileAfterText[] = {
188 "&gt;",
189 "&gt;",
200 #endif // DUMP_TO_FILE
203 * Default constructor
205 * @update gess 4/9/98
206 * @param
207 * @return
209 CViewSourceHTML::CViewSourceHTML()
211 mSyntaxHighlight = PR_FALSE;
212 mWrapLongLines = PR_FALSE;
213 nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
214 if (prefBranch) {
215 PRBool temp;
216 nsresult rv;
217 rv = prefBranch->GetBoolPref("view_source.syntax_highlight", &temp);
218 mSyntaxHighlight = NS_SUCCEEDED(rv) ? temp : PR_TRUE;
220 rv = prefBranch->GetBoolPref("view_source.wrap_long_lines", &temp);
221 mWrapLongLines = NS_SUCCEEDED(rv) ? temp : PR_FALSE;
224 mParser = 0;
225 mSink = 0;
226 mLineNumber = 1;
227 mTokenizer = 0;
228 mDocType=eHTML3_Quirks; // why?
229 mHasOpenRoot=PR_FALSE;
230 mHasOpenBody=PR_FALSE;
232 mTokenCount=0;
234 #ifdef DUMP_TO_FILE
235 gDumpFile = fopen(gDumpFileName,"w");
236 #endif // DUMP_TO_FILE
243 * Default destructor
245 * @update gess 4/9/98
246 * @param
247 * @return
249 CViewSourceHTML::~CViewSourceHTML(){
250 mParser=0; //just to prove we destructed...
254 * The parser uses a code sandwich to wrap the parsing process. Before
255 * the process begins, WillBuildModel() is called. Afterwards the parser
256 * calls DidBuildModel().
257 * @update rickg 03.20.2000
258 * @param aParserContext
259 * @param aSink
260 * @return error code (almost always 0)
262 nsresult CViewSourceHTML::WillBuildModel(const CParserContext& aParserContext,
263 nsITokenizer* aTokenizer,
264 nsIContentSink* aSink){
266 nsresult result=NS_OK;
268 #ifdef RAPTOR_PERF_METRICS
269 vsTimer.Reset();
270 NS_START_STOPWATCH(vsTimer);
271 #endif
273 STOP_TIMER();
274 mSink=(nsIHTMLContentSink*)aSink;
276 if((!aParserContext.mPrevContext) && (mSink)) {
278 nsAString & contextFilename = aParserContext.mScanner->GetFilename();
279 mFilename = Substring(contextFilename,
280 12, // The length of "view-source:"
281 contextFilename.Length() - 12);
283 mDocType=aParserContext.mDocType;
284 mMimeType=aParserContext.mMimeType;
285 mDTDMode=aParserContext.mDTDMode;
286 mParserCommand=aParserContext.mParserCommand;
287 mTokenizer = aTokenizer;
289 #ifdef DUMP_TO_FILE
290 if (gDumpFile) {
292 fprintf(gDumpFile, "<html>\n");
293 fprintf(gDumpFile, "<head>\n");
294 fprintf(gDumpFile, "<title>");
295 fprintf(gDumpFile, "Source of: ");
296 fputs(NS_ConvertUTF16toUTF8(mFilename).get(), gDumpFile);
297 fprintf(gDumpFile, "</title>\n");
298 fprintf(gDumpFile, "<link rel=\"stylesheet\" type=\"text/css\" href=\"resource://gre/res/viewsource.css\">\n");
299 fprintf(gDumpFile, "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n");
300 fprintf(gDumpFile, "</head>\n");
301 fprintf(gDumpFile, "<body id=\"viewsource\">\n");
302 fprintf(gDumpFile, "<pre id=\"line1\">\n");
304 #endif //DUMP_TO_FILE
308 if(eViewSource!=aParserContext.mParserCommand)
309 mDocType=ePlainText;
310 else mDocType=aParserContext.mDocType;
312 mLineNumber = 1;
313 // Munge the DTD mode so that the document will be in standards mode even if
314 // the original source was quirks. The CONST_CAST is evil, but the other
315 // options seem to be:
316 // 1) Change the WillBuildModel signature to take an nsIParser so that we can
317 // push a new parser context right here.
318 // 2) Make some assumptions about the exact class of mSink and get at the
319 // document that way.
320 // #1 doesn't seem worth it, and #2 is even more evil, since we plan to reset
321 // the DTD mode right back to what it was before, let's risk this.
322 CParserContext& parserContext = const_cast<CParserContext&>(aParserContext);
323 parserContext.mDTDMode = eDTDMode_full_standards;
324 result = mSink->WillBuildModel();
325 // And reset the DTD mode back to the right one
326 parserContext.mDTDMode = mDTDMode;
327 START_TIMER();
328 return result;
332 * The parser uses a code sandwich to wrap the parsing process. Before
333 * the process begins, WillBuildModel() is called. Afterwards the parser
334 * calls DidBuildModel().
335 * @update gess5/18/98
336 * @param aFilename is the name of the file being parsed.
337 * @return error code (almost always 0)
339 NS_IMETHODIMP CViewSourceHTML::BuildModel(nsIParser* aParser,nsITokenizer* aTokenizer,nsITokenObserver* anObserver,nsIContentSink* aSink) {
340 nsresult result=NS_OK;
342 if(aTokenizer && aParser) {
344 nsITokenizer* oldTokenizer=mTokenizer;
345 mTokenizer=aTokenizer;
346 nsTokenAllocator* theAllocator=mTokenizer->GetTokenAllocator();
348 if(!mHasOpenRoot) {
349 // For the stack-allocated tokens below, it's safe to pass a null
350 // token allocator, because there are no attributes on the tokens.
351 CStartToken htmlToken(NS_LITERAL_STRING("HTML"), eHTMLTag_html);
352 nsCParserNode htmlNode(&htmlToken, 0/*stack token*/);
353 mSink->OpenContainer(htmlNode);
355 CStartToken headToken(NS_LITERAL_STRING("HEAD"), eHTMLTag_head);
356 nsCParserNode headNode(&headToken, 0/*stack token*/);
357 mSink->OpenContainer(headNode);
359 CStartToken titleToken(NS_LITERAL_STRING("TITLE"), eHTMLTag_title);
360 nsCParserNode titleNode(&titleToken, 0/*stack token*/);
361 mSink->OpenContainer(titleNode);
363 // Note that XUL will automatically add the prefix "Source of: "
364 if (StringBeginsWith(mFilename, NS_LITERAL_STRING("data:")) &&
365 mFilename.Length() > 50) {
366 nsAutoString dataFilename(Substring(mFilename, 0, 50));
367 dataFilename.AppendLiteral("...");
368 CTextToken titleText(dataFilename);
369 nsCParserNode titleTextNode(&titleText, 0/*stack token*/);
370 mSink->AddLeaf(titleTextNode);
371 } else {
372 CTextToken titleText(mFilename);
373 nsCParserNode titleTextNode(&titleText, 0/*stack token*/);
374 mSink->AddLeaf(titleTextNode);
377 mSink->CloseContainer(eHTMLTag_title);
379 if (theAllocator) {
380 CStartToken* theToken=
381 static_cast<CStartToken*>
382 (theAllocator->CreateTokenOfType(eToken_start,
383 eHTMLTag_link,
384 NS_LITERAL_STRING("LINK")));
385 if (theToken) {
386 nsCParserStartNode theNode(theToken, theAllocator);
388 AddAttrToNode(theNode, theAllocator,
389 NS_LITERAL_STRING("rel"),
390 NS_LITERAL_STRING("stylesheet"));
392 AddAttrToNode(theNode, theAllocator,
393 NS_LITERAL_STRING("type"),
394 NS_LITERAL_STRING("text/css"));
396 AddAttrToNode(theNode, theAllocator,
397 NS_LITERAL_STRING("href"),
398 NS_LITERAL_STRING("resource://gre/res/viewsource.css"));
400 mSink->AddLeaf(theNode);
402 IF_FREE(theToken, theAllocator);
405 result = mSink->CloseContainer(eHTMLTag_head);
406 if(NS_SUCCEEDED(result)) {
407 mHasOpenRoot = PR_TRUE;
410 if (NS_SUCCEEDED(result) && !mHasOpenBody) {
411 if (theAllocator) {
412 CStartToken* bodyToken=
413 static_cast<CStartToken*>
414 (theAllocator->CreateTokenOfType(eToken_start,
415 eHTMLTag_body,
416 NS_LITERAL_STRING("BODY")));
417 if (bodyToken) {
418 nsCParserStartNode bodyNode(bodyToken, theAllocator);
420 AddAttrToNode(bodyNode, theAllocator,
421 NS_LITERAL_STRING("id"),
422 NS_ConvertASCIItoUTF16(kBodyId));
424 if (mWrapLongLines) {
425 AddAttrToNode(bodyNode, theAllocator,
426 NS_LITERAL_STRING("class"),
427 NS_ConvertASCIItoUTF16(kBodyClassWrap));
429 result = mSink->OpenContainer(bodyNode);
430 if(NS_SUCCEEDED(result)) mHasOpenBody=PR_TRUE;
432 IF_FREE(bodyToken, theAllocator);
434 if (NS_SUCCEEDED(result)) {
435 CStartToken* preToken =
436 static_cast<CStartToken*>
437 (theAllocator->CreateTokenOfType(eToken_start,
438 eHTMLTag_pre,
439 NS_LITERAL_STRING("PRE")));
440 if (preToken) {
441 nsCParserStartNode preNode(preToken, theAllocator);
442 AddAttrToNode(preNode, theAllocator,
443 NS_LITERAL_STRING("id"),
444 NS_LITERAL_STRING("line1"));
445 result = mSink->OpenContainer(preNode);
446 } else {
447 result = NS_ERROR_OUT_OF_MEMORY;
449 IF_FREE(preToken, theAllocator);
454 mSink->WillProcessTokens();
456 while(NS_SUCCEEDED(result)){
457 CToken* theToken=mTokenizer->PopToken();
458 if(theToken) {
459 result=HandleToken(theToken,aParser);
460 if(NS_SUCCEEDED(result)) {
461 IF_FREE(theToken, mTokenizer->GetTokenAllocator());
462 if (mParser->CanInterrupt() &&
463 mSink->DidProcessAToken() == NS_ERROR_HTMLPARSER_INTERRUPTED) {
464 result = NS_ERROR_HTMLPARSER_INTERRUPTED;
465 break;
467 } else {
468 mTokenizer->PushTokenFront(theToken);
471 else break;
472 }//while
474 mTokenizer=oldTokenizer;
476 else result=NS_ERROR_HTMLPARSER_BADTOKENIZER;
477 return result;
481 * Call this to start a new PRE block. See bug 86355 for why this
482 * makes some pages much faster.
484 void CViewSourceHTML::StartNewPreBlock(void){
485 CEndToken endToken(eHTMLTag_pre);
486 nsCParserNode endNode(&endToken, 0/*stack token*/);
487 mSink->CloseContainer(eHTMLTag_pre);
489 nsTokenAllocator* theAllocator = mTokenizer->GetTokenAllocator();
490 if (!theAllocator) {
491 return;
494 CStartToken* theToken =
495 static_cast<CStartToken*>
496 (theAllocator->CreateTokenOfType(eToken_start,
497 eHTMLTag_pre,
498 NS_LITERAL_STRING("PRE")));
499 if (!theToken) {
500 return;
503 nsCParserStartNode startNode(theToken, theAllocator);
504 AddAttrToNode(startNode, theAllocator,
505 NS_LITERAL_STRING("id"),
506 NS_ConvertASCIItoUTF16(nsPrintfCString("line%d", mLineNumber)));
507 mSink->OpenContainer(startNode);
508 IF_FREE(theToken, theAllocator);
510 #ifdef DUMP_TO_FILE
511 if (gDumpFile) {
512 fprintf(gDumpFile, "</pre>\n");
513 fprintf(gDumpFile, "<pre id=\"line%d\">\n", mLineNumber);
515 #endif // DUMP_TO_FILE
517 mTokenCount = 0;
520 void CViewSourceHTML::AddAttrToNode(nsCParserStartNode& aNode,
521 nsTokenAllocator* aAllocator,
522 const nsAString& aAttrName,
523 const nsAString& aAttrValue)
525 NS_PRECONDITION(aAllocator, "Must have a token allocator!");
527 CAttributeToken* theAttr =
528 (CAttributeToken*) aAllocator->CreateTokenOfType(eToken_attribute,
529 eHTMLTag_unknown,
530 aAttrValue);
531 if (!theAttr) {
532 NS_ERROR("Failed to allocate attribute token");
533 return;
536 theAttr->SetKey(aAttrName);
537 aNode.AddAttribute(theAttr);
539 // Parser nodes assume that they are being handed a ref when AddAttribute is
540 // called, unlike Init() and construction, when they actually addref the
541 // incoming token. Do NOT release here unless this setup changes.
546 * @update gess5/18/98
547 * @param
548 * @return
550 NS_IMETHODIMP CViewSourceHTML::DidBuildModel(nsresult anErrorCode,PRBool aNotifySink,nsIParser* aParser,nsIContentSink* aSink){
551 nsresult result= NS_OK;
553 //ADD CODE HERE TO CLOSE OPEN CONTAINERS...
555 if(aParser){
557 mParser=(nsParser*)aParser; //debug XXX
558 STOP_TIMER();
560 mSink=(nsIHTMLContentSink*)aParser->GetContentSink();
561 if((aNotifySink) && (mSink)) {
562 //now let's close automatically auto-opened containers...
564 #ifdef DUMP_TO_FILE
565 if(gDumpFile) {
566 fprintf(gDumpFile, "</pre>\n");
567 fprintf(gDumpFile, "</body>\n");
568 fprintf(gDumpFile, "</html>\n");
569 fclose(gDumpFile);
571 #endif // DUMP_TO_FILE
573 if(ePlainText!=mDocType) {
574 mSink->CloseContainer(eHTMLTag_pre);
575 mSink->CloseContainer(eHTMLTag_body);
576 mSink->CloseContainer(eHTMLTag_html);
578 result = mSink->DidBuildModel();
581 START_TIMER();
585 #ifdef RAPTOR_PERF_METRICS
586 NS_STOP_STOPWATCH(vsTimer);
587 printf("viewsource timer: ");
588 vsTimer.Print();
589 printf("\n");
590 #endif
592 return result;
596 * Use this id you want to stop the building content model
597 * --------------[ Sets DTD to STOP mode ]----------------
598 * It's recommended to use this method in accordance with
599 * the parser's terminate() method.
601 * @update harishd 07/22/99
602 * @param
603 * @return
605 NS_IMETHODIMP_(void)
606 CViewSourceHTML::Terminate() {
609 NS_IMETHODIMP_(PRInt32)
610 CViewSourceHTML::GetType() {
611 return NS_IPARSER_FLAG_HTML;
616 * @update gess5/18/98
617 * @param
618 * @return
620 NS_IMETHODIMP CViewSourceHTML::WillResumeParse(nsIContentSink* aSink){
621 nsresult result = NS_OK;
622 if(mSink) {
623 result = mSink->WillResume();
625 return result;
630 * @update gess5/18/98
631 * @param
632 * @return
634 NS_IMETHODIMP CViewSourceHTML::WillInterruptParse(nsIContentSink* aSink){
635 nsresult result = NS_OK;
636 if(mSink) {
637 result = mSink->WillInterrupt();
639 return result;
643 * Called by the parser to enable/disable dtd verification of the
644 * internal context stack.
645 * @update gess 7/23/98
646 * @param
647 * @return
649 void CViewSourceHTML::SetVerification(PRBool aEnabled)
654 * This method is called to determine whether or not a tag
655 * of one type can contain a tag of another type.
657 * @update gess 3/25/98
658 * @param aParent -- int tag of parent container
659 * @param aChild -- int tag of child container
660 * @return PR_TRUE if parent can contain child
662 PRBool CViewSourceHTML::CanContain(PRInt32 aParent,PRInt32 aChild) const{
663 PRBool result=PR_TRUE;
664 return result;
668 * This method gets called to determine whether a given
669 * tag is itself a container
671 * @update gess 3/25/98
672 * @param aTag -- tag to test for containership
673 * @return PR_TRUE if given tag can contain other tags
675 PRBool CViewSourceHTML::IsContainer(PRInt32 aTag) const{
676 PRBool result=PR_TRUE;
677 return result;
681 * This method gets called when a tag needs to write it's attributes
683 * @update gess 3/25/98
684 * @param
685 * @return result status
687 nsresult CViewSourceHTML::WriteAttributes(PRInt32 attrCount, PRBool aOwnerInError) {
688 nsresult result=NS_OK;
690 if(attrCount){ //go collect the attributes...
691 int attr = 0;
692 for(attr = 0; attr < attrCount; ++attr){
693 CToken* theToken = mTokenizer->PeekToken();
694 if(theToken) {
695 eHTMLTokenTypes theType = eHTMLTokenTypes(theToken->GetTokenType());
696 if(eToken_attribute == theType){
697 mTokenizer->PopToken(); //pop it for real...
698 mTokenNode.AddAttribute(theToken); //and add it to the node.
700 CAttributeToken* theAttrToken = (CAttributeToken*)theToken;
701 const nsSubstring& theKey = theAttrToken->GetKey();
703 // The attribute is only in error if its owner is NOT in error.
704 const PRBool attributeInError =
705 !aOwnerInError && theAttrToken->IsInError();
707 result = WriteTag(kAttributeName,theKey,0,attributeInError);
708 const nsSubstring& theValue = theAttrToken->GetValue();
710 if(!theValue.IsEmpty() || theAttrToken->mHasEqualWithoutValue){
711 result = WriteTag(kAttributeValue,theValue,0,attributeInError);
715 else return kEOF;
719 return result;
723 * This method gets called when a tag needs to be sent out
725 * @update gess 3/25/98
726 * @param
727 * @return result status
729 nsresult CViewSourceHTML::WriteTag(PRInt32 aTagType,const nsSubstring & aText,PRInt32 attrCount,PRBool aTagInError) {
730 nsresult result=NS_OK;
732 // adjust line number to what it will be after we finish writing this tag
733 // XXXbz life here sucks. We can't use the GetNewlineCount on the token,
734 // because Text tokens in <style>, <script>, etc lie through their teeth.
735 // On the other hand, the parser messes up newline counting in some token
736 // types (bug 137315). So our line numbers will disagree with the parser's
737 // in some cases...
738 mLineNumber += aText.CountChar(PRUnichar('\n'));
740 nsTokenAllocator* theAllocator=mTokenizer->GetTokenAllocator();
741 NS_ASSERTION(0!=theAllocator,"Error: no allocator");
742 if(0==theAllocator)
743 return NS_ERROR_FAILURE;
745 // Highlight all parts of all erroneous tags.
746 if (mSyntaxHighlight && aTagInError) {
747 CStartToken* theTagToken=
748 static_cast<CStartToken*>
749 (theAllocator->CreateTokenOfType(eToken_start,
750 eHTMLTag_span,
751 NS_LITERAL_STRING("SPAN")));
752 NS_ENSURE_TRUE(theTagToken, NS_ERROR_OUT_OF_MEMORY);
753 mErrorNode.Init(theTagToken, theAllocator);
754 AddAttrToNode(mErrorNode, theAllocator,
755 NS_LITERAL_STRING("class"),
756 NS_LITERAL_STRING("error"));
757 mSink->OpenContainer(mErrorNode);
758 IF_FREE(theTagToken, theAllocator);
759 #ifdef DUMP_TO_FILE
760 if (gDumpFile) {
761 fprintf(gDumpFile, "<span class=\"error\">");
763 #endif
766 if (kBeforeText[aTagType][0] != 0) {
767 NS_ConvertASCIItoUTF16 beforeText(kBeforeText[aTagType]);
768 mITextToken.SetIndirectString(beforeText);
769 nsCParserNode theNode(&mITextToken, 0/*stack token*/);
770 mSink->AddLeaf(theNode);
772 #ifdef DUMP_TO_FILE
773 if (gDumpFile && kDumpFileBeforeText[aTagType][0])
774 fprintf(gDumpFile, kDumpFileBeforeText[aTagType]);
775 #endif // DUMP_TO_FILE
777 if (mSyntaxHighlight && aTagType != kText) {
778 CStartToken* theTagToken=
779 static_cast<CStartToken*>
780 (theAllocator->CreateTokenOfType(eToken_start,
781 eHTMLTag_span,
782 NS_LITERAL_STRING("SPAN")));
783 NS_ENSURE_TRUE(theTagToken, NS_ERROR_OUT_OF_MEMORY);
784 mStartNode.Init(theTagToken, theAllocator);
785 AddAttrToNode(mStartNode, theAllocator,
786 NS_LITERAL_STRING("class"),
787 NS_ConvertASCIItoUTF16(kElementClasses[aTagType]));
788 mSink->OpenContainer(mStartNode); //emit <starttag>...
789 IF_FREE(theTagToken, theAllocator);
790 #ifdef DUMP_TO_FILE
791 if (gDumpFile) {
792 fprintf(gDumpFile, "<span class=\"");
793 fprintf(gDumpFile, kElementClasses[aTagType]);
794 fprintf(gDumpFile, "\">");
796 #endif // DUMP_TO_FILE
799 STOP_TIMER();
801 mITextToken.SetIndirectString(aText); //now emit the tag name...
803 nsCParserNode theNode(&mITextToken, 0/*stack token*/);
804 mSink->AddLeaf(theNode);
805 #ifdef DUMP_TO_FILE
806 if (gDumpFile) {
807 fputs(NS_ConvertUTF16toUTF8(aText).get(), gDumpFile);
809 #endif // DUMP_TO_FILE
811 if (mSyntaxHighlight && aTagType != kText) {
812 mStartNode.ReleaseAll();
813 mSink->CloseContainer(eHTMLTag_span); //emit </endtag>...
814 #ifdef DUMP_TO_FILE
815 if (gDumpFile)
816 fprintf(gDumpFile, "</span>");
817 #endif //DUMP_TO_FILE
820 if(attrCount){
821 result=WriteAttributes(attrCount, aTagInError);
824 // Tokens are set in error if their ending > is not there, so don't output
825 // the after-text
826 if (!aTagInError && kAfterText[aTagType][0] != 0) {
827 NS_ConvertASCIItoUTF16 afterText(kAfterText[aTagType]);
828 mITextToken.SetIndirectString(afterText);
829 nsCParserNode theNode(&mITextToken, 0/*stack token*/);
830 mSink->AddLeaf(theNode);
832 #ifdef DUMP_TO_FILE
833 if (!aTagInError && gDumpFile && kDumpFileAfterText[aTagType][0])
834 fprintf(gDumpFile, kDumpFileAfterText[aTagType]);
835 #endif // DUMP_TO_FILE
837 if (mSyntaxHighlight && aTagInError) {
838 mErrorNode.ReleaseAll();
839 mSink->CloseContainer(eHTMLTag_span); //emit </endtag>...
840 #ifdef DUMP_TO_FILE
841 if (gDumpFile)
842 fprintf(gDumpFile, "</span>");
843 #endif //DUMP_TO_FILE
846 START_TIMER();
848 return result;
853 * @update gess 3/25/98
854 * @param aToken -- token object to be put into content model
855 * @return 0 if all is well; non-zero is an error
857 NS_IMETHODIMP CViewSourceHTML::HandleToken(CToken* aToken,nsIParser* aParser)
859 nsresult result=NS_OK;
860 CHTMLToken* theToken= (CHTMLToken*)(aToken);
861 eHTMLTokenTypes theType= (eHTMLTokenTypes)theToken->GetTokenType();
863 mParser=(nsParser*)aParser;
864 mSink=(nsIHTMLContentSink*)aParser->GetContentSink();
866 mTokenNode.Init(theToken, mTokenizer->GetTokenAllocator());
868 switch(theType) {
870 case eToken_start:
872 const nsSubstring& startValue = aToken->GetStringValue();
873 result = WriteTag(kStartTag,startValue,aToken->GetAttributeCount(),aToken->IsInError());
875 if((ePlainText!=mDocType) && mParser && (NS_OK==result)) {
876 result = mSink->NotifyTagObservers(&mTokenNode);
879 break;
881 case eToken_end:
883 const nsSubstring& endValue = aToken->GetStringValue();
884 result = WriteTag(kEndTag,endValue,aToken->GetAttributeCount(),aToken->IsInError());
886 break;
888 case eToken_cdatasection:
890 nsAutoString theStr;
891 theStr.AssignLiteral("<!");
892 theStr.Append(aToken->GetStringValue());
893 if (!aToken->IsInError()) {
894 theStr.AppendLiteral(">");
896 result=WriteTag(kCData,theStr,0,aToken->IsInError());
898 break;
900 case eToken_markupDecl:
902 nsAutoString theStr;
903 theStr.AssignLiteral("<!");
904 theStr.Append(aToken->GetStringValue());
905 if (!aToken->IsInError()) {
906 theStr.AppendLiteral(">");
908 result=WriteTag(kMarkupDecl,theStr,0,aToken->IsInError());
910 break;
912 case eToken_comment:
914 nsAutoString theStr;
915 aToken->AppendSourceTo(theStr);
916 result=WriteTag(kComment,theStr,0,aToken->IsInError());
918 break;
920 case eToken_doctypeDecl:
922 const nsSubstring& doctypeValue = aToken->GetStringValue();
923 result=WriteTag(kDoctype,doctypeValue,0,aToken->IsInError());
925 break;
927 case eToken_newline:
929 const nsSubstring& newlineValue = aToken->GetStringValue();
930 result=WriteTag(kText,newlineValue,0,PR_FALSE);
931 ++mTokenCount;
932 if (NS_VIEWSOURCE_TOKENS_PER_BLOCK > 0 &&
933 mTokenCount > NS_VIEWSOURCE_TOKENS_PER_BLOCK) {
934 StartNewPreBlock();
937 break;
939 case eToken_whitespace:
941 const nsSubstring& wsValue = aToken->GetStringValue();
942 result=WriteTag(kText,wsValue,0,PR_FALSE);
943 ++mTokenCount;
944 if (NS_VIEWSOURCE_TOKENS_PER_BLOCK > 0 &&
945 mTokenCount > NS_VIEWSOURCE_TOKENS_PER_BLOCK &&
946 !wsValue.IsEmpty()) {
947 PRUnichar ch = wsValue.Last();
948 if (ch == kLF || ch == kCR)
949 StartNewPreBlock();
952 break;
954 case eToken_text:
956 const nsSubstring& str = aToken->GetStringValue();
957 result=WriteTag(kText,str,aToken->GetAttributeCount(),aToken->IsInError());
958 ++mTokenCount;
959 if (NS_VIEWSOURCE_TOKENS_PER_BLOCK > 0 &&
960 mTokenCount > NS_VIEWSOURCE_TOKENS_PER_BLOCK && !str.IsEmpty()) {
961 PRUnichar ch = str.Last();
962 if (ch == kLF || ch == kCR)
963 StartNewPreBlock();
967 break;
969 case eToken_entity:
970 result=WriteTag(kEntity,aToken->GetStringValue(),0,aToken->IsInError());
971 break;
973 case eToken_instruction:
974 result=WriteTag(kPI,aToken->GetStringValue(),0,aToken->IsInError());
975 break;
977 default:
978 result=NS_OK;
979 }//switch
981 mTokenNode.ReleaseAll();
983 return result;