Back to index

lightning-sunbird  0.9+nobinonly
nsViewSourceHTML.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   jce2@po.cwru.edu <Jason Eager>: Added pref to turn on/off
00024  *   Boris Zbarsky <bzbarsky@mit.edu>
00025  *   rbs@maths.uq.edu.au
00026  *   Andreas M. Schneider <clarence@clarence.de>
00027  *
00028  * Alternatively, the contents of this file may be used under the terms of
00029  * either of the GNU General Public License Version 2 or later (the "GPL"),
00030  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00031  * in which case the provisions of the GPL or the LGPL are applicable instead
00032  * of those above. If you wish to allow use of your version of this file only
00033  * under the terms of either the GPL or the LGPL, and not to allow others to
00034  * use your version of this file under the terms of the MPL, indicate your
00035  * decision by deleting the provisions above and replace them with the notice
00036  * and other provisions required by the GPL or the LGPL. If you do not delete
00037  * the provisions above, a recipient may use your version of this file under
00038  * the terms of any one of the MPL, the GPL or the LGPL.
00039  *
00040  * ***** END LICENSE BLOCK ***** */
00041 
00049 /*
00050  * Set NS_VIEWSOURCE_TOKENS_PER_BLOCK to 0 to disable multi-block
00051  * output.  Multi-block output helps reduce the amount of bidi
00052  * processing we have to do on the resulting content model.
00053  */
00054 #define NS_VIEWSOURCE_TOKENS_PER_BLOCK 16
00055 
00056 #ifdef RAPTOR_PERF_METRICS
00057 #  define START_TIMER()                    \
00058     if(mParser) mParser->mParseTime.Start(PR_FALSE); \
00059     if(mParser) mParser->mDTDTime.Start(PR_FALSE); 
00060 
00061 #  define STOP_TIMER()                     \
00062     if(mParser) mParser->mParseTime.Stop(); \
00063     if(mParser) mParser->mDTDTime.Stop(); 
00064 
00065 #else
00066 #  define STOP_TIMER() 
00067 #  define START_TIMER()
00068 #endif
00069 
00070 #include "nsIAtom.h"
00071 #include "nsViewSourceHTML.h"
00072 #include "nsCRT.h"
00073 #include "nsParser.h"
00074 #include "nsScanner.h"
00075 #include "nsIParser.h"
00076 #include "nsDTDUtils.h"
00077 #include "nsIContentSink.h"
00078 #include "nsIHTMLContentSink.h"
00079 #include "nsHTMLTokenizer.h"
00080 #include "nsIPrefService.h"
00081 #include "nsIPrefBranch.h"
00082 #include "nsUnicharUtils.h"
00083 #include "nsPrintfCString.h"
00084 
00085 #include "nsIServiceManager.h"
00086 
00087 #include "COtherDTD.h"
00088 #include "nsElementTable.h"
00089 
00090 #include "prenv.h"  //this is here for debug reasons...
00091 #include "prtypes.h"  //this is here for debug reasons...
00092 #include "prio.h"
00093 #include "plstr.h"
00094 #include "prmem.h"
00095 
00096 #ifdef RAPTOR_PERF_METRICS
00097 #include "stopwatch.h"
00098 Stopwatch vsTimer;
00099 #endif
00100 
00101 
00102 static NS_DEFINE_IID(kClassIID,     NS_VIEWSOURCE_HTML_IID);
00103 
00104 // Define this to dump the viewsource stuff to a file
00105 //#define DUMP_TO_FILE
00106 #ifdef DUMP_TO_FILE
00107 #include <stdio.h>
00108   FILE* gDumpFile=0;
00109   static const char* gDumpFileName = "/tmp/viewsource.html";
00110 //  static const char* gDumpFileName = "\\temp\\viewsource.html";
00111 #endif // DUMP_TO_FILE
00112 
00113 // bug 22022 - these are used to toggle 'Wrap Long Lines' on the viewsource
00114 // window by selectively setting/unsetting the following class defined in
00115 // viewsource.css; the setting is remembered between invocations using a pref.
00116 static const char kBodyId[] = "viewsource";
00117 static const char kBodyClassWrap[] = "wrap";
00118 
00129 nsresult CViewSourceHTML::QueryInterface(const nsIID& aIID, void** aInstancePtr)  
00130 {                                                                        
00131   if (NULL == aInstancePtr) {                                            
00132     return NS_ERROR_NULL_POINTER;                                        
00133   }                                                                      
00134 
00135   if(aIID.Equals(NS_GET_IID(nsISupports)))    {  //do IUnknown...
00136     *aInstancePtr = (nsIDTD*)(this);                                        
00137   }
00138   else if(aIID.Equals(NS_GET_IID(nsIDTD))) {  //do IParser base class...
00139     *aInstancePtr = (nsIDTD*)(this);                                        
00140   }
00141   else if(aIID.Equals(kClassIID)) {  //do this class...
00142     *aInstancePtr = (CViewSourceHTML*)(this);                                        
00143   }                 
00144   else {
00145     *aInstancePtr=0;
00146     return NS_NOINTERFACE;
00147   }
00148   NS_ADDREF_THIS();
00149   return NS_OK;                                                        
00150 }
00151 
00160 nsresult NS_NewViewSourceHTML(nsIDTD** aInstancePtrResult)
00161 {
00162   CViewSourceHTML* it = new CViewSourceHTML();
00163 
00164   if (it == 0) {
00165     return NS_ERROR_OUT_OF_MEMORY;
00166   }
00167 
00168   return it->QueryInterface(kClassIID, (void **) aInstancePtrResult);
00169 }
00170 
00171 
00172 NS_IMPL_ADDREF(CViewSourceHTML)
00173 NS_IMPL_RELEASE(CViewSourceHTML)
00174 
00175 /********************************************
00176  ********************************************/
00177 
00178 class CIndirectTextToken : public CTextToken {
00179 public:
00180   CIndirectTextToken() : CTextToken() {
00181     mIndirectString=0;
00182   }
00183   
00184   void SetIndirectString(const nsSubstring& aString) {
00185     mIndirectString=&aString;
00186   }
00187 
00188   virtual const nsSubstring& GetStringValue(void){
00189     return (const nsSubstring&)*mIndirectString;
00190   }
00191 
00192   const nsSubstring* mIndirectString;
00193 };
00194 
00195 
00196 /*******************************************************************
00197   Now define the CSharedVSCOntext class...
00198  *******************************************************************/
00199 
00200 class CSharedVSContext {
00201 public:
00202 
00203   CSharedVSContext() {
00204   }
00205   
00206   ~CSharedVSContext() {
00207   }
00208 
00209   static CSharedVSContext& GetSharedContext() {
00210     static CSharedVSContext gSharedVSContext;
00211     return gSharedVSContext;
00212   }
00213 
00214   nsCParserNode       mEndNode;
00215   nsCParserStartNode  mStartNode;
00216   nsCParserStartNode  mTokenNode;
00217   CIndirectTextToken  mITextToken;
00218   nsCParserStartNode  mErrorNode;
00219   nsCParserNode       mEndErrorNode;
00220 };
00221 
00222 enum {
00223   VIEW_SOURCE_START_TAG = 0,
00224   VIEW_SOURCE_END_TAG = 1,
00225   VIEW_SOURCE_COMMENT = 2,
00226   VIEW_SOURCE_CDATA = 3,
00227   VIEW_SOURCE_DOCTYPE = 4,
00228   VIEW_SOURCE_PI = 5,
00229   VIEW_SOURCE_ENTITY = 6,
00230   VIEW_SOURCE_TEXT = 7,
00231   VIEW_SOURCE_ATTRIBUTE_NAME = 8,
00232   VIEW_SOURCE_ATTRIBUTE_VALUE = 9,
00233   VIEW_SOURCE_SUMMARY = 10,
00234   VIEW_SOURCE_POPUP = 11,
00235   VIEW_SOURCE_MARKUPDECLARATION = 12
00236 };
00237 
00238 static const char* const kElementClasses[] = {
00239   "start-tag",
00240   "end-tag",
00241   "comment",
00242   "cdata",
00243   "doctype",
00244   "pi",
00245   "entity",
00246   "text",
00247   "attribute-name",
00248   "attribute-value",
00249   "summary",
00250   "popup",
00251   "markupdeclaration"  
00252 };
00253 
00254 static const char* const kBeforeText[] = {
00255   "<",
00256   "</",
00257   "",
00258   "",
00259   "",
00260   "",
00261   "&",
00262   "",
00263   "",
00264   "=",
00265   "",
00266   "",
00267   ""
00268 };
00269 
00270 static const char* const kAfterText[] = {
00271   ">",
00272   ">",
00273   "",
00274   "",
00275   "",
00276   "",
00277   "",
00278   "",
00279   "",
00280   "",
00281   "",
00282   "",
00283   ""
00284 };
00285 
00286 #ifdef DUMP_TO_FILE
00287 static const char* const kDumpFileBeforeText[] = {
00288   "&lt;",
00289   "&lt;/",
00290   "",
00291   "",
00292   "",
00293   "",
00294   "&amp;",
00295   "",
00296   "",
00297   "=",
00298   "",
00299   "",
00300   ""
00301 };
00302 
00303 static const char* const kDumpFileAfterText[] = {
00304   "&gt;",
00305   "&gt;",
00306   "",
00307   "",
00308   "",
00309   "",
00310   "",
00311   "",
00312   "",
00313   "",
00314   "",
00315   "",
00316   ""
00317 };
00318 #endif // DUMP_TO_FILE
00319 
00327 CViewSourceHTML::CViewSourceHTML() : mFilename(), mTags(), mErrors() {
00328   mStartTag = VIEW_SOURCE_START_TAG;
00329   mEndTag = VIEW_SOURCE_END_TAG;
00330   mCommentTag = VIEW_SOURCE_COMMENT;
00331   mCDATATag = VIEW_SOURCE_CDATA;
00332   mMarkupDeclaration = VIEW_SOURCE_MARKUPDECLARATION;
00333   mDocTypeTag = VIEW_SOURCE_DOCTYPE;
00334   mPITag = VIEW_SOURCE_PI;
00335   mEntityTag = VIEW_SOURCE_ENTITY;
00336   mText = VIEW_SOURCE_TEXT;
00337   mKey = VIEW_SOURCE_ATTRIBUTE_NAME;
00338   mValue = VIEW_SOURCE_ATTRIBUTE_VALUE;
00339   mSummaryTag = VIEW_SOURCE_SUMMARY;
00340   mPopupTag = VIEW_SOURCE_POPUP;
00341   mSyntaxHighlight = PR_FALSE;
00342   mWrapLongLines = PR_FALSE;
00343   nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
00344   if (prefBranch) {
00345     PRBool temp;
00346     nsresult rv;
00347     rv = prefBranch->GetBoolPref("view_source.syntax_highlight", &temp);
00348     mSyntaxHighlight = NS_SUCCEEDED(rv) ? temp : PR_TRUE;
00349 
00350     rv = prefBranch->GetBoolPref("view_source.wrap_long_lines", &temp);
00351     mWrapLongLines = NS_SUCCEEDED(rv) ? temp : PR_FALSE;
00352   }
00353 
00354   mParser = 0;
00355   mSink = 0;
00356   mLineNumber = 1;
00357   mTokenizer = 0;
00358   mDocType=eHTML3_Quirks; // why?
00359   mHasOpenRoot=PR_FALSE;
00360   mHasOpenBody=PR_FALSE;
00361 
00362   mTokenCount=0;
00363 
00364 #ifdef DUMP_TO_FILE
00365   gDumpFile = fopen(gDumpFileName,"w");
00366 #endif // DUMP_TO_FILE
00367 
00368 }
00369 
00370 
00371 
00379 CViewSourceHTML::~CViewSourceHTML(){
00380   mParser=0; //just to prove we destructed...
00381 }
00382 
00389 const nsIID& CViewSourceHTML::GetMostDerivedIID(void) const{
00390   return kClassIID;
00391 }
00392 
00400 nsresult CViewSourceHTML::CreateNewInstance(nsIDTD** aInstancePtrResult){
00401   return NS_NewViewSourceHTML(aInstancePtrResult);
00402 }
00403 
00413 NS_IMETHODIMP_(eAutoDetectResult)
00414 CViewSourceHTML::CanParse(CParserContext& aParserContext)
00415 {
00416   if (eViewSource == aParserContext.mParserCommand) {
00417     if (aParserContext.mDocType == ePlainText) {
00418       return eValidDetect;
00419     }
00420 
00421     // We claim to parse XML... now _that_ is funny.
00422     return ePrimaryDetect;
00423   }
00424   
00425   return eUnknownDetect;
00426 }
00427 
00428 
00438 nsresult CViewSourceHTML::WillBuildModel(const CParserContext& aParserContext,
00439                                          nsITokenizer* aTokenizer,
00440                                          nsIContentSink* aSink){
00441 
00442   nsresult result=NS_OK;
00443 
00444 #ifdef RAPTOR_PERF_METRICS
00445   vsTimer.Reset();
00446   NS_START_STOPWATCH(vsTimer);
00447 #endif 
00448 
00449   STOP_TIMER();
00450   mSink=(nsIHTMLContentSink*)aSink;
00451 
00452   if((!aParserContext.mPrevContext) && (mSink)) {
00453 
00454     nsAString & contextFilename = aParserContext.mScanner->GetFilename();
00455     mFilename = Substring(contextFilename,
00456                           12, // The length of "view-source:"
00457                           contextFilename.Length() - 12);
00458     
00459     mTags.Truncate();
00460     mErrors.Assign(NS_LITERAL_STRING(" HTML 4.0 Strict-DTD validation (enabled); [Should use Transitional?].\n"));
00461 
00462     mDocType=aParserContext.mDocType;
00463     mMimeType=aParserContext.mMimeType;
00464     mDTDMode=aParserContext.mDTDMode;
00465     mParserCommand=aParserContext.mParserCommand;
00466     mTokenizer = aTokenizer;
00467     mErrorCount=0;
00468     mTagCount=0;
00469 
00470 #ifdef DUMP_TO_FILE
00471     if (gDumpFile) {
00472 
00473       fprintf(gDumpFile, "<html>\n");
00474       fprintf(gDumpFile, "<head>\n");
00475       fprintf(gDumpFile, "<title>");
00476       fprintf(gDumpFile, "Source of: ");
00477       fputs(NS_ConvertUCS2toUTF8(mFilename).get(), gDumpFile);
00478       fprintf(gDumpFile, "</title>\n");
00479       fprintf(gDumpFile, "<link rel=\"stylesheet\" type=\"text/css\" href=\"resource://gre/res/viewsource.css\">\n");
00480       fprintf(gDumpFile, "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n");
00481       fprintf(gDumpFile, "</head>\n");
00482       fprintf(gDumpFile, "<body id=\"viewsource\">\n");
00483       fprintf(gDumpFile, "<pre id=\"line1\">\n");
00484     }
00485 #endif //DUMP_TO_FILE
00486   }
00487 
00488 
00489   if(eViewSource!=aParserContext.mParserCommand)
00490     mDocType=ePlainText;
00491   else mDocType=aParserContext.mDocType;
00492 
00493   mLineNumber = 1;
00494   // Munge the DTD mode so that the document will be in standards mode even if
00495   // the original source was quirks.  The CONST_CAST is evil, but the other
00496   // options seem to be:
00497   // 1) Change the WillBuildModel signature to take an nsIParser so that we can
00498   //    push a new parser context right here.
00499   // 2) Make some assumptions about the exact class of mSink and get at the
00500   //    document that way.
00501   // #1 doesn't seem worth it, and #2 is even more evil, since we plan to reset
00502   // the DTD mode right back to what it was before, let's risk this.
00503   CParserContext& parserContext = NS_CONST_CAST(CParserContext&, aParserContext);
00504   parserContext.mDTDMode = eDTDMode_full_standards;
00505   result = mSink->WillBuildModel();
00506   // And reset the DTD mode back to the right one
00507   parserContext.mDTDMode = mDTDMode;
00508   START_TIMER();
00509   return result;
00510 }
00511 
00520 NS_IMETHODIMP CViewSourceHTML::BuildModel(nsIParser* aParser,nsITokenizer* aTokenizer,nsITokenObserver* anObserver,nsIContentSink* aSink) {
00521   nsresult result=NS_OK;
00522 
00523   if(aTokenizer && aParser) {
00524 
00525     nsITokenizer*  oldTokenizer=mTokenizer;
00526     mTokenizer=aTokenizer;
00527     nsTokenAllocator* theAllocator=mTokenizer->GetTokenAllocator();
00528 
00529     if(!mHasOpenRoot) {
00530       // For the stack-allocated tokens below, it's safe to pass a null
00531       // token allocator, because there are no attributes on the tokens.
00532       PRBool didBlock = PR_FALSE;
00533 
00534       CStartToken htmlToken(NS_LITERAL_STRING("HTML"), eHTMLTag_html);
00535       nsCParserNode htmlNode(&htmlToken, 0/*stack token*/);
00536       mSink->OpenHTML(htmlNode);
00537 
00538       CStartToken headToken(NS_LITERAL_STRING("HEAD"), eHTMLTag_head);
00539       nsCParserNode headNode(&headToken, 0/*stack token*/);
00540       mSink->OpenHead(headNode);
00541 
00542       // Note that XUL will automatically add the prefix "Source of: "
00543       if (StringBeginsWith(mFilename, NS_LITERAL_STRING("data:")) &&
00544           mFilename.Length() > 50) {
00545         nsAutoString dataFilename(Substring(mFilename, 0, 50));
00546         dataFilename.AppendLiteral("...");
00547         mSink->SetTitle(dataFilename);
00548       } else {
00549         mSink->SetTitle(mFilename);
00550       }
00551 
00552       if (theAllocator) {
00553         CStartToken* theToken=
00554           NS_STATIC_CAST(CStartToken*,
00555                          theAllocator->CreateTokenOfType(eToken_start,
00556                                                          eHTMLTag_link,
00557                                                          NS_LITERAL_STRING("LINK")));
00558         if (theToken) {
00559           nsCParserStartNode theNode(theToken, theAllocator);
00560 
00561           AddAttrToNode(theNode, theAllocator,
00562                         NS_LITERAL_STRING("rel"),
00563                         NS_LITERAL_STRING("stylesheet"));
00564 
00565           AddAttrToNode(theNode, theAllocator,
00566                         NS_LITERAL_STRING("type"),
00567                         NS_LITERAL_STRING("text/css"));
00568 
00569           AddAttrToNode(theNode, theAllocator,
00570                         NS_LITERAL_STRING("href"),
00571                         NS_LITERAL_STRING("resource://gre/res/viewsource.css"));
00572           
00573           result = mSink->AddLeaf(theNode);
00574           didBlock = result == NS_ERROR_HTMLPARSER_BLOCK;
00575         }
00576       }
00577 
00578       CEndToken endHeadToken(eHTMLTag_head);
00579       nsCParserNode endHeadNode(&endHeadToken, 0/*stack token*/);
00580       result = mSink->CloseHead();
00581       if(NS_SUCCEEDED(result)) {
00582         mHasOpenRoot = PR_TRUE;
00583         if (didBlock) {
00584           result = NS_ERROR_HTMLPARSER_BLOCK;
00585         }
00586       }
00587     }
00588     if (NS_SUCCEEDED(result) && !mHasOpenBody) {
00589       if (theAllocator) {
00590         CStartToken* bodyToken=
00591           NS_STATIC_CAST(CStartToken*,
00592                          theAllocator->CreateTokenOfType(eToken_start,
00593                                                          eHTMLTag_body,
00594                                                          NS_LITERAL_STRING("BODY")));
00595         if (bodyToken) {
00596           nsCParserStartNode bodyNode(bodyToken, theAllocator);
00597 
00598           AddAttrToNode(bodyNode, theAllocator,
00599                         NS_LITERAL_STRING("id"),
00600                         NS_ConvertASCIItoUCS2(kBodyId));
00601           
00602           if (mWrapLongLines) {
00603             AddAttrToNode(bodyNode, theAllocator,
00604                           NS_LITERAL_STRING("class"),
00605                           NS_ConvertASCIItoUCS2(kBodyClassWrap));
00606           }
00607           result = mSink->OpenBody(bodyNode);
00608           if(NS_SUCCEEDED(result)) mHasOpenBody=PR_TRUE;
00609         }
00610         
00611         if (NS_SUCCEEDED(result)) {
00612           CStartToken* preToken =
00613             NS_STATIC_CAST(CStartToken*,
00614                            theAllocator->CreateTokenOfType(eToken_start,
00615                                                            eHTMLTag_pre,
00616                                                            NS_LITERAL_STRING("PRE")));
00617           if (preToken) {
00618             nsCParserStartNode preNode(preToken, theAllocator);
00619             AddAttrToNode(preNode, theAllocator,
00620                           NS_LITERAL_STRING("id"),
00621                           NS_LITERAL_STRING("line1"));
00622             result = mSink->OpenContainer(preNode);
00623           } else {
00624             result = NS_ERROR_OUT_OF_MEMORY;
00625           }
00626         }
00627       }
00628     }
00629 
00630     mSink->WillProcessTokens();
00631 
00632     while(NS_SUCCEEDED(result)){
00633       CToken* theToken=mTokenizer->PopToken();
00634       if(theToken) {
00635         result=HandleToken(theToken,aParser);
00636         if(NS_SUCCEEDED(result)) {
00637           IF_FREE(theToken, mTokenizer->GetTokenAllocator());
00638           if (mParser->CanInterrupt() &&
00639               mSink->DidProcessAToken() == NS_ERROR_HTMLPARSER_INTERRUPTED) {
00640             result = NS_ERROR_HTMLPARSER_INTERRUPTED;
00641             break;
00642           }
00643         }
00644         else if(NS_ERROR_HTMLPARSER_BLOCK!=result){
00645           mTokenizer->PushTokenFront(theToken);
00646         }
00647       }
00648       else break;
00649     }//while
00650    
00651     mTokenizer=oldTokenizer;
00652   }
00653   else result=NS_ERROR_HTMLPARSER_BADTOKENIZER;
00654   return result;
00655 }
00656 
00657 
00664 nsresult  CViewSourceHTML::GenerateSummary() {
00665   nsresult result=NS_OK;
00666 
00667   if(mErrorCount && mTagCount) {
00668 
00669     mErrors.AppendLiteral("\n\n ");
00670     mErrors.AppendInt(mErrorCount);
00671     mErrors.Append(NS_LITERAL_STRING(" error(s) detected -- see highlighted portions.\n"));
00672 
00673     result=WriteTag(mSummaryTag,mErrors,0,PR_FALSE);
00674   }
00675 
00676   return result;
00677 }
00678 
00683 void CViewSourceHTML::StartNewPreBlock(void){
00684   CEndToken endToken(eHTMLTag_pre);
00685   nsCParserNode endNode(&endToken, 0/*stack token*/);
00686   mSink->CloseContainer(eHTMLTag_pre);
00687 
00688   nsTokenAllocator* theAllocator = mTokenizer->GetTokenAllocator();
00689   if (!theAllocator) {
00690     return;
00691   }
00692   
00693   CStartToken* theToken =
00694     NS_STATIC_CAST(CStartToken*,
00695                    theAllocator->CreateTokenOfType(eToken_start,
00696                                                    eHTMLTag_pre,
00697                                                    NS_LITERAL_STRING("PRE")));
00698   if (!theToken) {
00699     return;
00700   }
00701 
00702   nsCParserStartNode startNode(theToken, theAllocator);
00703   AddAttrToNode(startNode, theAllocator,
00704                 NS_LITERAL_STRING("id"),
00705                 NS_ConvertASCIItoUCS2(nsPrintfCString("line%d", mLineNumber)));
00706   mSink->OpenContainer(startNode);
00707   
00708 #ifdef DUMP_TO_FILE
00709   if (gDumpFile) {
00710     fprintf(gDumpFile, "</pre>\n");
00711     fprintf(gDumpFile, "<pre id=\"line%d\">\n", mLineNumber);
00712   }
00713 #endif // DUMP_TO_FILE
00714 
00715   mTokenCount = 0;
00716 }
00717 
00718 void CViewSourceHTML::AddAttrToNode(nsCParserStartNode& aNode,
00719                                     nsTokenAllocator* aAllocator,
00720                                     const nsAString& aAttrName,
00721                                     const nsAString& aAttrValue)
00722 {
00723   NS_PRECONDITION(aAllocator, "Must have a token allocator!");
00724   
00725   CAttributeToken* theAttr =
00726     (CAttributeToken*) aAllocator->CreateTokenOfType(eToken_attribute,
00727                                                      eHTMLTag_unknown,
00728                                                      aAttrValue);
00729   if (!theAttr) {
00730     NS_ERROR("Failed to allocate attribute token");
00731     return;
00732   }
00733 
00734   theAttr->SetKey(aAttrName);
00735   aNode.AddAttribute(theAttr);
00736 }
00737 
00744 NS_IMETHODIMP CViewSourceHTML::DidBuildModel(nsresult anErrorCode,PRBool aNotifySink,nsIParser* aParser,nsIContentSink* aSink){
00745   nsresult result= NS_OK;
00746 
00747   //ADD CODE HERE TO CLOSE OPEN CONTAINERS...
00748 
00749   if(aParser){
00750 
00751     mParser=(nsParser*)aParser;  //debug XXX
00752     STOP_TIMER();
00753 
00754     mSink=(nsIHTMLContentSink*)aParser->GetContentSink();
00755     if((aNotifySink) && (mSink)) {
00756         //now let's close automatically auto-opened containers...
00757 
00758 #ifdef DUMP_TO_FILE
00759       if(gDumpFile) {
00760         fprintf(gDumpFile, "</pre>\n");
00761         fprintf(gDumpFile, "</body>\n");
00762         fprintf(gDumpFile, "</html>\n");
00763         fclose(gDumpFile);
00764       }
00765 #endif // DUMP_TO_FILE
00766 
00767       if(ePlainText!=mDocType) {
00768         CEndToken theToken(eHTMLTag_pre);
00769         nsCParserNode preNode(&theToken, 0/*stack token*/);
00770         mSink->CloseContainer(eHTMLTag_pre);
00771         
00772         CEndToken bodyToken(eHTMLTag_body);
00773         nsCParserNode bodyNode(&bodyToken, 0/*stack token*/);
00774         mSink->CloseBody();
00775         
00776         CEndToken htmlToken(eHTMLTag_html);
00777         nsCParserNode htmlNode(&htmlToken, 0/*stack token*/);
00778         mSink->CloseHTML();
00779       }
00780       result = mSink->DidBuildModel();
00781     }
00782 
00783     START_TIMER();
00784 
00785   }
00786 
00787 #ifdef RAPTOR_PERF_METRICS
00788   NS_STOP_STOPWATCH(vsTimer);
00789   printf("viewsource timer: ");
00790   vsTimer.Print();
00791   printf("\n");
00792 #endif 
00793 
00794   return result;
00795 }
00796 
00807 NS_IMETHODIMP_(void)  
00808 CViewSourceHTML::Terminate() {
00809 }
00810 
00811 NS_IMETHODIMP_(PRInt32)  
00812 CViewSourceHTML::GetType() {
00813   return NS_IPARSER_FLAG_HTML;
00814 }
00815 
00816 NS_IMETHODIMP 
00817 CViewSourceHTML::CollectSkippedContent(PRInt32 aTag, nsAString& aContent, PRInt32 &aLineNo)
00818 {
00819   return NS_OK;
00820 }
00821 
00828 NS_IMETHODIMP CViewSourceHTML::WillResumeParse(nsIContentSink* aSink){
00829   nsresult result = NS_OK;
00830   if(mSink) {
00831     result = mSink->WillResume();
00832   }
00833   return result;
00834 }
00835 
00842 NS_IMETHODIMP CViewSourceHTML::WillInterruptParse(nsIContentSink* aSink){
00843   nsresult result = NS_OK;
00844   if(mSink) {
00845     result = mSink->WillInterrupt();
00846   }
00847   return result;
00848 }
00849 
00857 void CViewSourceHTML::SetVerification(PRBool aEnabled)
00858 {
00859 }
00860 
00870 PRBool CViewSourceHTML::CanContain(PRInt32 aParent,PRInt32 aChild) const{
00871   PRBool result=PR_TRUE;
00872   return result;
00873 }
00874 
00883 PRBool CViewSourceHTML::IsContainer(PRInt32 aTag) const{
00884   PRBool result=PR_TRUE;
00885   return result;
00886 }
00887 
00895 nsresult CViewSourceHTML::WriteAttributes(PRInt32 attrCount, PRBool aOwnerInError) {
00896   nsresult result=NS_OK;
00897   
00898   if(attrCount){ //go collect the attributes...
00899 
00900     CSharedVSContext& theContext=CSharedVSContext::GetSharedContext();
00901 
00902     int attr = 0;
00903     for(attr = 0; attr < attrCount; ++attr){
00904       CToken* theToken = mTokenizer->PeekToken();
00905       if(theToken)  {
00906         eHTMLTokenTypes theType = eHTMLTokenTypes(theToken->GetTokenType());
00907         if(eToken_attribute == theType){
00908           mTokenizer->PopToken(); //pop it for real...
00909           theContext.mTokenNode.AddAttribute(theToken);  //and add it to the node.
00910 
00911           CAttributeToken* theAttrToken = (CAttributeToken*)theToken;
00912           const nsSubstring& theKey = theAttrToken->GetKey();
00913           
00914           // The attribute is only in error if its owner is NOT in error.
00915           const PRBool attributeInError =
00916             !aOwnerInError && theAttrToken->IsInError();
00917 
00918           result = WriteTag(mKey,theKey,0,attributeInError);
00919           const nsSubstring& theValue = theAttrToken->GetValue();
00920 
00921           if(!theValue.IsEmpty() || theAttrToken->mHasEqualWithoutValue){
00922             result = WriteTag(mValue,theValue,0,attributeInError);
00923           }
00924         } 
00925       }
00926       else return kEOF;
00927     }
00928   }
00929 
00930   return result;
00931 }
00932 
00940 nsresult CViewSourceHTML::WriteTag(PRInt32 aTagType,const nsSubstring & aText,PRInt32 attrCount,PRBool aTagInError) {
00941   nsresult result=NS_OK;
00942 
00943   // adjust line number to what it will be after we finish writing this tag
00944   // XXXbz life here sucks.  We can't use the GetNewlineCount on the token,
00945   // because Text tokens in <style>, <script>, etc lie through their teeth.
00946   // On the other hand, the parser messes up newline counting in some token
00947   // types (bug 137315).  So our line numbers will disagree with the parser's
00948   // in some cases...
00949   mLineNumber += aText.CountChar(PRUnichar('\n'));
00950   
00951   CSharedVSContext& theContext=CSharedVSContext::GetSharedContext();
00952 
00953   nsTokenAllocator* theAllocator=mTokenizer->GetTokenAllocator();
00954   NS_ASSERTION(0!=theAllocator,"Error: no allocator");
00955   if(0==theAllocator)
00956     return NS_ERROR_FAILURE;
00957 
00958   // Highlight all parts of all erroneous tags.
00959   if (mSyntaxHighlight && aTagInError) {
00960     CStartToken* theTagToken=
00961       NS_STATIC_CAST(CStartToken*,
00962                      theAllocator->CreateTokenOfType(eToken_start,
00963                                                      eHTMLTag_span,
00964                                                      NS_LITERAL_STRING("SPAN")));
00965     theContext.mErrorNode.Init(theTagToken, theAllocator);
00966     AddAttrToNode(theContext.mErrorNode, theAllocator,
00967                   NS_LITERAL_STRING("class"),
00968                   NS_LITERAL_STRING("error"));
00969     mSink->OpenContainer(theContext.mErrorNode);
00970 #ifdef DUMP_TO_FILE
00971     if (gDumpFile) {
00972       fprintf(gDumpFile, "<span class=\"error\">");
00973     }
00974 #endif
00975   }
00976 
00977   if (kBeforeText[aTagType][0] != 0) {
00978     NS_ConvertASCIItoUTF16 beforeText(kBeforeText[aTagType]);
00979     theContext.mITextToken.SetIndirectString(beforeText);
00980     nsCParserNode theNode(&theContext.mITextToken, 0/*stack token*/);
00981     mSink->AddLeaf(theNode);
00982   }
00983 #ifdef DUMP_TO_FILE
00984   if (gDumpFile && kDumpFileBeforeText[aTagType][0])
00985     fprintf(gDumpFile, kDumpFileBeforeText[aTagType]);
00986 #endif // DUMP_TO_FILE
00987   
00988   if (mSyntaxHighlight && aTagType != mText) {
00989     CStartToken* theTagToken=
00990       NS_STATIC_CAST(CStartToken*,
00991                      theAllocator->CreateTokenOfType(eToken_start,
00992                                                      eHTMLTag_span,
00993                                                      NS_LITERAL_STRING("SPAN")));
00994     theContext.mStartNode.Init(theTagToken, theAllocator);
00995     AddAttrToNode(theContext.mStartNode, theAllocator,
00996                   NS_LITERAL_STRING("class"),
00997                   NS_ConvertASCIItoUCS2(kElementClasses[aTagType]));
00998     mSink->OpenContainer(theContext.mStartNode);  //emit <starttag>...
00999 #ifdef DUMP_TO_FILE
01000     if (gDumpFile) {
01001       fprintf(gDumpFile, "<span class=\"");
01002       fprintf(gDumpFile, kElementClasses[aTagType]);
01003       fprintf(gDumpFile, "\">");
01004     }
01005 #endif // DUMP_TO_FILE
01006   }
01007 
01008   STOP_TIMER();
01009 
01010   theContext.mITextToken.SetIndirectString(aText);  //now emit the tag name...
01011 
01012   nsCParserNode theNode(&theContext.mITextToken, 0/*stack token*/);
01013   mSink->AddLeaf(theNode);
01014 #ifdef DUMP_TO_FILE
01015   if (gDumpFile) {
01016     fputs(NS_ConvertUCS2toUTF8(aText).get(), gDumpFile);
01017   }
01018 #endif // DUMP_TO_FILE
01019 
01020   if (mSyntaxHighlight && aTagType != mText) {
01021     theContext.mStartNode.ReleaseAll(); 
01022     CEndToken theEndToken(eHTMLTag_span);
01023     theContext.mEndNode.Init(&theEndToken, 0/*stack token*/);
01024     mSink->CloseContainer(eHTMLTag_span);  //emit </endtag>...
01025 #ifdef DUMP_TO_FILE
01026     if (gDumpFile)
01027       fprintf(gDumpFile, "</span>");
01028 #endif //DUMP_TO_FILE
01029   }
01030 
01031   if(attrCount){
01032     result=WriteAttributes(attrCount, aTagInError);
01033   }
01034 
01035   // Tokens are set in error if their ending > is not there, so don't output 
01036   // the after-text
01037   if (!aTagInError && kAfterText[aTagType][0] != 0) {
01038     NS_ConvertASCIItoUTF16 afterText(kAfterText[aTagType]);
01039     theContext.mITextToken.SetIndirectString(afterText);
01040     nsCParserNode theNode(&theContext.mITextToken, 0/*stack token*/);
01041     mSink->AddLeaf(theNode);
01042   }
01043 #ifdef DUMP_TO_FILE
01044   if (!aTagInError && gDumpFile && kDumpFileAfterText[aTagType][0])
01045     fprintf(gDumpFile, kDumpFileAfterText[aTagType]);
01046 #endif // DUMP_TO_FILE
01047 
01048   if (mSyntaxHighlight && aTagInError) {
01049     theContext.mErrorNode.ReleaseAll(); 
01050     CEndToken theEndToken(eHTMLTag_span);
01051     theContext.mEndErrorNode.Init(&theEndToken, 0/*stack token*/);
01052     mSink->CloseContainer(eHTMLTag_span);  //emit </endtag>...
01053 #ifdef DUMP_TO_FILE
01054     if (gDumpFile)
01055       fprintf(gDumpFile, "</span>");
01056 #endif //DUMP_TO_FILE
01057   }
01058 
01059   START_TIMER();
01060 
01061   return result;
01062 }
01063 
01070 NS_IMETHODIMP CViewSourceHTML::HandleToken(CToken* aToken,nsIParser* aParser) {
01071   nsresult        result=NS_OK;
01072   CHTMLToken*     theToken= (CHTMLToken*)(aToken);
01073   eHTMLTokenTypes theType= (eHTMLTokenTypes)theToken->GetTokenType();
01074  
01075   mParser=(nsParser*)aParser;
01076   mSink=(nsIHTMLContentSink*)aParser->GetContentSink();
01077  
01078   CSharedVSContext& theContext=CSharedVSContext::GetSharedContext();
01079   theContext.mTokenNode.Init(theToken, mTokenizer->GetTokenAllocator());
01080 
01081   eHTMLTags theParent=(mTags.Length()) ? (eHTMLTags)mTags.Last() : eHTMLTag_unknown;
01082   eHTMLTags theChild=(eHTMLTags)aToken->GetTypeID();
01083   
01084   switch(theType) {
01085     
01086     case eToken_start:
01087       {
01088         ++mTagCount;
01089 
01090         const nsSubstring& startValue = aToken->GetStringValue();
01091         result=WriteTag(mStartTag,startValue,aToken->GetAttributeCount(),aToken->IsInError());
01092 
01093         if((ePlainText!=mDocType) && mParser && (NS_OK==result)) {
01094           result = mSink->NotifyTagObservers(&theContext.mTokenNode);
01095         }
01096       }
01097       break;
01098 
01099     case eToken_end:
01100       {
01101         if(theParent==theChild) {
01102           mTags.Truncate(mTags.Length()-1);
01103         }
01104 
01105         const nsSubstring& endValue = aToken->GetStringValue();
01106         result=WriteTag(mEndTag,endValue,aToken->GetAttributeCount(),aToken->IsInError());
01107       }
01108       break;
01109 
01110     case eToken_cdatasection:
01111       {
01112         nsAutoString theStr;
01113         theStr.AssignLiteral("<!");
01114         theStr.Append(aToken->GetStringValue());
01115         if (!aToken->IsInError()) {
01116           theStr.AppendLiteral(">");
01117         }
01118         result=WriteTag(mCDATATag,theStr,0,aToken->IsInError());
01119       }
01120       break;
01121 
01122     case eToken_markupDecl:
01123       {
01124         nsAutoString theStr;
01125         theStr.AssignLiteral("<!");
01126         theStr.Append(aToken->GetStringValue());
01127         if (!aToken->IsInError()) {
01128           theStr.AppendLiteral(">");
01129         }
01130         result=WriteTag(mMarkupDeclaration,theStr,0,aToken->IsInError());
01131       }
01132       break;
01133 
01134     case eToken_comment: 
01135       {
01136         nsAutoString theStr;
01137         aToken->AppendSourceTo(theStr);
01138         result=WriteTag(mCommentTag,theStr,0,aToken->IsInError());
01139       }
01140       break;
01141 
01142     case eToken_doctypeDecl:
01143       {
01144         const nsSubstring& doctypeValue = aToken->GetStringValue();
01145         result=WriteTag(mDocTypeTag,doctypeValue,0,aToken->IsInError());
01146       }
01147       break;
01148 
01149     case eToken_newline:
01150       {
01151         const nsSubstring& newlineValue = aToken->GetStringValue();
01152         result=WriteTag(mText,newlineValue,0,PR_FALSE);
01153         ++mTokenCount;
01154         if (NS_VIEWSOURCE_TOKENS_PER_BLOCK > 0 &&
01155             mTokenCount > NS_VIEWSOURCE_TOKENS_PER_BLOCK)
01156           StartNewPreBlock();
01157       }
01158       break;
01159 
01160     case eToken_whitespace:
01161       {
01162         const nsSubstring& wsValue = aToken->GetStringValue();
01163         result=WriteTag(mText,wsValue,0,PR_FALSE);
01164         ++mTokenCount;
01165         if (NS_VIEWSOURCE_TOKENS_PER_BLOCK > 0 &&
01166             mTokenCount > NS_VIEWSOURCE_TOKENS_PER_BLOCK &&
01167             !wsValue.IsEmpty()) {
01168           PRUnichar ch = wsValue.Last();
01169           if (ch == kLF || ch == kCR)
01170             StartNewPreBlock();
01171         }
01172       }
01173       break;
01174 
01175     case eToken_text:
01176       {
01177         const nsSubstring& str = aToken->GetStringValue();         
01178         result=WriteTag(mText,str,aToken->GetAttributeCount(),aToken->IsInError());
01179         ++mTokenCount;
01180         if (NS_VIEWSOURCE_TOKENS_PER_BLOCK > 0 &&
01181             mTokenCount > NS_VIEWSOURCE_TOKENS_PER_BLOCK && !str.IsEmpty()) {
01182           PRUnichar ch = str.Last();
01183           if (ch == kLF || ch == kCR)
01184             StartNewPreBlock();
01185         }
01186       }
01187 
01188       break;
01189 
01190     case eToken_entity:
01191       result=WriteTag(mEntityTag,aToken->GetStringValue(),0,aToken->IsInError());
01192       break;
01193 
01194     case eToken_instruction:
01195       result=WriteTag(mPITag,aToken->GetStringValue(),0,aToken->IsInError());
01196       break;
01197 
01198     default:
01199       result=NS_OK;
01200   }//switch
01201 
01202   theContext.mTokenNode.ReleaseAll(); 
01203 
01204   return result;
01205 }
01206