1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set sw=2 ts=2 et tw=78: */
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998
21 * the Initial Developer. All Rights Reserved.
24 * jce2@po.cwru.edu <Jason Eager>: Added pref to turn on/off
25 * Boris Zbarsky <bzbarsky@mit.edu>
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();
61 # define START_TIMER()
65 #include "nsViewSourceHTML.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"
78 #include "nsNetUtil.h"
80 #include "nsIServiceManager.h"
82 #include "nsElementTable.h"
84 #include "prenv.h" //this is here for debug reasons...
85 #include "prtypes.h" //this is here for debug reasons...
90 #ifdef RAPTOR_PERF_METRICS
91 #include "stopwatch.h"
97 // Define this to dump the viewsource stuff to a file
98 //#define DUMP_TO_FILE
102 static const char* gDumpFileName
= "/tmp/viewsource.html";
103 // static const char* gDumpFileName = "\\temp\\viewsource.html";
104 #endif // DUMP_TO_FILE
106 // bug 22022 - these are used to toggle 'Wrap Long Lines' on the viewsource
107 // window by selectively setting/unsetting the following class defined in
108 // viewsource.css; the setting is remembered between invocations using a pref.
109 static const char kBodyId
[] = "viewsource";
110 static const char kBodyClassWrap
[] = "wrap";
112 NS_IMPL_ISUPPORTS1(CViewSourceHTML
, nsIDTD
)
114 /********************************************
115 ********************************************/
131 static const char* const kElementClasses
[] = {
145 static const char* const kBeforeText
[] = {
159 static const char* const kAfterText
[] = {
174 static const char* const kDumpFileBeforeText
[] = {
188 static const char* const kDumpFileAfterText
[] = {
201 #endif // DUMP_TO_FILE
204 * Default constructor
206 * @update gess 4/9/98
210 CViewSourceHTML::CViewSourceHTML()
212 mSyntaxHighlight
= PR_FALSE
;
213 mWrapLongLines
= PR_FALSE
;
214 nsCOMPtr
<nsIPrefBranch
> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID
));
218 rv
= prefBranch
->GetBoolPref("view_source.syntax_highlight", &temp
);
219 mSyntaxHighlight
= NS_SUCCEEDED(rv
) ? temp
: PR_TRUE
;
221 rv
= prefBranch
->GetBoolPref("view_source.wrap_long_lines", &temp
);
222 mWrapLongLines
= NS_SUCCEEDED(rv
) ? temp
: PR_FALSE
;
229 mDocType
=eHTML_Quirks
;
230 mHasOpenRoot
=PR_FALSE
;
231 mHasOpenBody
=PR_FALSE
;
236 gDumpFile
= fopen(gDumpFileName
,"w");
237 #endif // DUMP_TO_FILE
246 * @update gess 4/9/98
250 CViewSourceHTML::~CViewSourceHTML(){
251 mParser
=0; //just to prove we destructed...
255 * The parser uses a code sandwich to wrap the parsing process. Before
256 * the process begins, WillBuildModel() is called. Afterwards the parser
257 * calls DidBuildModel().
258 * @update rickg 03.20.2000
259 * @param aParserContext
261 * @return error code (almost always 0)
263 nsresult
CViewSourceHTML::WillBuildModel(const CParserContext
& aParserContext
,
264 nsITokenizer
* aTokenizer
,
265 nsIContentSink
* aSink
){
267 nsresult result
=NS_OK
;
269 #ifdef RAPTOR_PERF_METRICS
271 NS_START_STOPWATCH(vsTimer
);
275 mSink
=(nsIHTMLContentSink
*)aSink
;
277 if((!aParserContext
.mPrevContext
) && (mSink
)) {
279 nsAString
& contextFilename
= aParserContext
.mScanner
->GetFilename();
280 mFilename
= Substring(contextFilename
,
281 12, // The length of "view-source:"
282 contextFilename
.Length() - 12);
284 mDocType
=aParserContext
.mDocType
;
285 mMimeType
=aParserContext
.mMimeType
;
286 mDTDMode
=aParserContext
.mDTDMode
;
287 mParserCommand
=aParserContext
.mParserCommand
;
288 mTokenizer
= aTokenizer
;
293 fprintf(gDumpFile
, "<html>\n");
294 fprintf(gDumpFile
, "<head>\n");
295 fprintf(gDumpFile
, "<title>");
296 fprintf(gDumpFile
, "Source of: ");
297 fputs(NS_ConvertUTF16toUTF8(mFilename
).get(), gDumpFile
);
298 fprintf(gDumpFile
, "</title>\n");
299 fprintf(gDumpFile
, "<link rel=\"stylesheet\" type=\"text/css\" href=\"resource://gre/res/viewsource.css\">\n");
300 fprintf(gDumpFile
, "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n");
301 fprintf(gDumpFile
, "</head>\n");
302 fprintf(gDumpFile
, "<body id=\"viewsource\">\n");
303 fprintf(gDumpFile
, "<pre id=\"line1\">\n");
305 #endif //DUMP_TO_FILE
309 if(eViewSource
!=aParserContext
.mParserCommand
)
311 else mDocType
=aParserContext
.mDocType
;
314 // Munge the DTD mode so that the document will be in standards mode even if
315 // the original source was quirks. The CONST_CAST is evil, but the other
316 // options seem to be:
317 // 1) Change the WillBuildModel signature to take an nsIParser so that we can
318 // push a new parser context right here.
319 // 2) Make some assumptions about the exact class of mSink and get at the
320 // document that way.
321 // #1 doesn't seem worth it, and #2 is even more evil, since we plan to reset
322 // the DTD mode right back to what it was before, let's risk this.
323 CParserContext
& parserContext
= const_cast<CParserContext
&>(aParserContext
);
324 parserContext
.mDTDMode
= eDTDMode_full_standards
;
325 result
= mSink
->WillBuildModel();
326 // And reset the DTD mode back to the right one
327 parserContext
.mDTDMode
= mDTDMode
;
333 * The parser uses a code sandwich to wrap the parsing process. Before
334 * the process begins, WillBuildModel() is called. Afterwards the parser
335 * calls DidBuildModel().
336 * @update gess5/18/98
337 * @param aFilename is the name of the file being parsed.
338 * @return error code (almost always 0)
340 NS_IMETHODIMP
CViewSourceHTML::BuildModel(nsIParser
* aParser
,nsITokenizer
* aTokenizer
,nsITokenObserver
* anObserver
,nsIContentSink
* aSink
) {
341 nsresult result
=NS_OK
;
343 if(aTokenizer
&& aParser
) {
345 nsITokenizer
* oldTokenizer
=mTokenizer
;
346 mTokenizer
=aTokenizer
;
347 nsTokenAllocator
* theAllocator
=mTokenizer
->GetTokenAllocator();
350 // For the stack-allocated tokens below, it's safe to pass a null
351 // token allocator, because there are no attributes on the tokens.
352 CStartToken
htmlToken(NS_LITERAL_STRING("HTML"), eHTMLTag_html
);
353 nsCParserNode
htmlNode(&htmlToken
, 0/*stack token*/);
354 mSink
->OpenContainer(htmlNode
);
356 CStartToken
headToken(NS_LITERAL_STRING("HEAD"), eHTMLTag_head
);
357 nsCParserNode
headNode(&headToken
, 0/*stack token*/);
358 mSink
->OpenContainer(headNode
);
360 CStartToken
titleToken(NS_LITERAL_STRING("TITLE"), eHTMLTag_title
);
361 nsCParserNode
titleNode(&titleToken
, 0/*stack token*/);
362 mSink
->OpenContainer(titleNode
);
364 // Note that XUL will automatically add the prefix "Source of: "
365 if (StringBeginsWith(mFilename
, NS_LITERAL_STRING("data:")) &&
366 mFilename
.Length() > 50) {
367 nsAutoString
dataFilename(Substring(mFilename
, 0, 50));
368 dataFilename
.AppendLiteral("...");
369 CTextToken
titleText(dataFilename
);
370 nsCParserNode
titleTextNode(&titleText
, 0/*stack token*/);
371 mSink
->AddLeaf(titleTextNode
);
373 CTextToken
titleText(mFilename
);
374 nsCParserNode
titleTextNode(&titleText
, 0/*stack token*/);
375 mSink
->AddLeaf(titleTextNode
);
378 mSink
->CloseContainer(eHTMLTag_title
);
381 CStartToken
* theToken
=
382 static_cast<CStartToken
*>
383 (theAllocator
->CreateTokenOfType(eToken_start
,
385 NS_LITERAL_STRING("LINK")));
387 nsCParserStartNode
theNode(theToken
, theAllocator
);
389 AddAttrToNode(theNode
, theAllocator
,
390 NS_LITERAL_STRING("rel"),
391 NS_LITERAL_STRING("stylesheet"));
393 AddAttrToNode(theNode
, theAllocator
,
394 NS_LITERAL_STRING("type"),
395 NS_LITERAL_STRING("text/css"));
397 AddAttrToNode(theNode
, theAllocator
,
398 NS_LITERAL_STRING("href"),
399 NS_LITERAL_STRING("resource://gre/res/viewsource.css"));
401 mSink
->AddLeaf(theNode
);
403 IF_FREE(theToken
, theAllocator
);
406 result
= mSink
->CloseContainer(eHTMLTag_head
);
407 if(NS_SUCCEEDED(result
)) {
408 mHasOpenRoot
= PR_TRUE
;
411 if (NS_SUCCEEDED(result
) && !mHasOpenBody
) {
413 CStartToken
* bodyToken
=
414 static_cast<CStartToken
*>
415 (theAllocator
->CreateTokenOfType(eToken_start
,
417 NS_LITERAL_STRING("BODY")));
419 nsCParserStartNode
bodyNode(bodyToken
, theAllocator
);
421 AddAttrToNode(bodyNode
, theAllocator
,
422 NS_LITERAL_STRING("id"),
423 NS_ConvertASCIItoUTF16(kBodyId
));
425 if (mWrapLongLines
) {
426 AddAttrToNode(bodyNode
, theAllocator
,
427 NS_LITERAL_STRING("class"),
428 NS_ConvertASCIItoUTF16(kBodyClassWrap
));
430 result
= mSink
->OpenContainer(bodyNode
);
431 if(NS_SUCCEEDED(result
)) mHasOpenBody
=PR_TRUE
;
433 IF_FREE(bodyToken
, theAllocator
);
435 if (NS_SUCCEEDED(result
)) {
436 CStartToken
* preToken
=
437 static_cast<CStartToken
*>
438 (theAllocator
->CreateTokenOfType(eToken_start
,
440 NS_LITERAL_STRING("PRE")));
442 nsCParserStartNode
preNode(preToken
, theAllocator
);
443 AddAttrToNode(preNode
, theAllocator
,
444 NS_LITERAL_STRING("id"),
445 NS_LITERAL_STRING("line1"));
446 result
= mSink
->OpenContainer(preNode
);
448 result
= NS_ERROR_OUT_OF_MEMORY
;
450 IF_FREE(preToken
, theAllocator
);
455 while(NS_SUCCEEDED(result
)){
456 CToken
* theToken
=mTokenizer
->PopToken();
458 result
=HandleToken(theToken
,aParser
);
459 if(NS_SUCCEEDED(result
)) {
460 IF_FREE(theToken
, mTokenizer
->GetTokenAllocator());
461 if (mParser
->CanInterrupt() &&
462 mSink
->DidProcessAToken() == NS_ERROR_HTMLPARSER_INTERRUPTED
) {
463 result
= NS_ERROR_HTMLPARSER_INTERRUPTED
;
467 mTokenizer
->PushTokenFront(theToken
);
473 mTokenizer
=oldTokenizer
;
475 else result
=NS_ERROR_HTMLPARSER_BADTOKENIZER
;
480 * Call this to start a new PRE block. See bug 86355 for why this
481 * makes some pages much faster.
483 void CViewSourceHTML::StartNewPreBlock(void){
484 CEndToken
endToken(eHTMLTag_pre
);
485 nsCParserNode
endNode(&endToken
, 0/*stack token*/);
486 mSink
->CloseContainer(eHTMLTag_pre
);
488 nsTokenAllocator
* theAllocator
= mTokenizer
->GetTokenAllocator();
493 CStartToken
* theToken
=
494 static_cast<CStartToken
*>
495 (theAllocator
->CreateTokenOfType(eToken_start
,
497 NS_LITERAL_STRING("PRE")));
502 nsCParserStartNode
startNode(theToken
, theAllocator
);
503 AddAttrToNode(startNode
, theAllocator
,
504 NS_LITERAL_STRING("id"),
505 NS_ConvertASCIItoUTF16(nsPrintfCString("line%d", mLineNumber
)));
506 mSink
->OpenContainer(startNode
);
507 IF_FREE(theToken
, theAllocator
);
511 fprintf(gDumpFile
, "</pre>\n");
512 fprintf(gDumpFile
, "<pre id=\"line%d\">\n", mLineNumber
);
514 #endif // DUMP_TO_FILE
519 void CViewSourceHTML::AddAttrToNode(nsCParserStartNode
& aNode
,
520 nsTokenAllocator
* aAllocator
,
521 const nsAString
& aAttrName
,
522 const nsAString
& aAttrValue
)
524 NS_PRECONDITION(aAllocator
, "Must have a token allocator!");
526 CAttributeToken
* theAttr
=
527 (CAttributeToken
*) aAllocator
->CreateTokenOfType(eToken_attribute
,
531 NS_ERROR("Failed to allocate attribute token");
535 theAttr
->SetKey(aAttrName
);
536 aNode
.AddAttribute(theAttr
);
538 // Parser nodes assume that they are being handed a ref when AddAttribute is
539 // called, unlike Init() and construction, when they actually addref the
540 // incoming token. Do NOT release here unless this setup changes.
545 * @update gess5/18/98
549 NS_IMETHODIMP
CViewSourceHTML::DidBuildModel(nsresult anErrorCode
,PRBool aNotifySink
,nsIParser
* aParser
,nsIContentSink
* aSink
){
550 nsresult result
= NS_OK
;
552 //ADD CODE HERE TO CLOSE OPEN CONTAINERS...
556 mParser
=(nsParser
*)aParser
; //debug XXX
559 mSink
=(nsIHTMLContentSink
*)aParser
->GetContentSink();
560 if((aNotifySink
) && (mSink
)) {
561 //now let's close automatically auto-opened containers...
565 fprintf(gDumpFile
, "</pre>\n");
566 fprintf(gDumpFile
, "</body>\n");
567 fprintf(gDumpFile
, "</html>\n");
570 #endif // DUMP_TO_FILE
572 if(ePlainText
!=mDocType
) {
573 mSink
->CloseContainer(eHTMLTag_pre
);
574 mSink
->CloseContainer(eHTMLTag_body
);
575 mSink
->CloseContainer(eHTMLTag_html
);
577 result
= mSink
->DidBuildModel();
584 #ifdef RAPTOR_PERF_METRICS
585 NS_STOP_STOPWATCH(vsTimer
);
586 printf("viewsource timer: ");
595 * Use this id you want to stop the building content model
596 * --------------[ Sets DTD to STOP mode ]----------------
597 * It's recommended to use this method in accordance with
598 * the parser's terminate() method.
600 * @update harishd 07/22/99
605 CViewSourceHTML::Terminate() {
608 NS_IMETHODIMP_(PRInt32
)
609 CViewSourceHTML::GetType() {
610 return NS_IPARSER_FLAG_HTML
;
615 * @update gess5/18/98
619 NS_IMETHODIMP
CViewSourceHTML::WillResumeParse(nsIContentSink
* aSink
){
620 nsresult result
= NS_OK
;
622 result
= mSink
->WillResume();
629 * @update gess5/18/98
633 NS_IMETHODIMP
CViewSourceHTML::WillInterruptParse(nsIContentSink
* aSink
){
634 nsresult result
= NS_OK
;
636 result
= mSink
->WillInterrupt();
642 * Called by the parser to enable/disable dtd verification of the
643 * internal context stack.
644 * @update gess 7/23/98
648 void CViewSourceHTML::SetVerification(PRBool aEnabled
)
653 * This method is called to determine whether or not a tag
654 * of one type can contain a tag of another type.
656 * @update gess 3/25/98
657 * @param aParent -- int tag of parent container
658 * @param aChild -- int tag of child container
659 * @return PR_TRUE if parent can contain child
661 PRBool
CViewSourceHTML::CanContain(PRInt32 aParent
,PRInt32 aChild
) const{
662 PRBool result
=PR_TRUE
;
667 * This method gets called to determine whether a given
668 * tag is itself a container
670 * @update gess 3/25/98
671 * @param aTag -- tag to test for containership
672 * @return PR_TRUE if given tag can contain other tags
674 PRBool
CViewSourceHTML::IsContainer(PRInt32 aTag
) const{
675 PRBool result
=PR_TRUE
;
680 * This method gets called when a tag needs to write it's attributes
682 * @update gess 3/25/98
684 * @return result status
686 nsresult
CViewSourceHTML::WriteAttributes(const nsAString
& tagName
,
687 nsTokenAllocator
* allocator
,
688 PRInt32 attrCount
, PRBool aOwnerInError
) {
689 nsresult result
=NS_OK
;
691 if(attrCount
){ //go collect the attributes...
693 for(attr
= 0; attr
< attrCount
; ++attr
){
694 CToken
* theToken
= mTokenizer
->PeekToken();
696 eHTMLTokenTypes theType
= eHTMLTokenTypes(theToken
->GetTokenType());
697 if(eToken_attribute
== theType
){
698 mTokenizer
->PopToken(); //pop it for real...
699 mTokenNode
.AddAttribute(theToken
); //and add it to the node.
701 CAttributeToken
* theAttrToken
= (CAttributeToken
*)theToken
;
702 const nsSubstring
& theKey
= theAttrToken
->GetKey();
704 // The attribute is only in error if its owner is NOT in error.
705 const PRBool attributeInError
=
706 !aOwnerInError
&& theAttrToken
->IsInError();
708 result
= WriteTag(kAttributeName
,theKey
,0,attributeInError
);
709 const nsSubstring
& theValue
= theAttrToken
->GetValue();
711 if(!theValue
.IsEmpty() || theAttrToken
->mHasEqualWithoutValue
){
712 if (IsUrlAttribute(tagName
, theKey
, theValue
)) {
713 WriteHrefAttribute(allocator
, theValue
);
715 WriteTag(kAttributeValue
,theValue
,0,attributeInError
);
728 * This method gets called when a tag needs to be sent out
730 * @update gess 3/25/98
732 * @return result status
734 nsresult
CViewSourceHTML::WriteTag(PRInt32 aTagType
,const nsSubstring
& aText
,PRInt32 attrCount
,PRBool aTagInError
) {
735 nsresult result
=NS_OK
;
737 // adjust line number to what it will be after we finish writing this tag
738 // XXXbz life here sucks. We can't use the GetNewlineCount on the token,
739 // because Text tokens in <style>, <script>, etc lie through their teeth.
740 // On the other hand, the parser messes up newline counting in some token
741 // types (bug 137315). So our line numbers will disagree with the parser's
743 mLineNumber
+= aText
.CountChar(PRUnichar('\n'));
745 nsTokenAllocator
* theAllocator
=mTokenizer
->GetTokenAllocator();
746 NS_ASSERTION(0!=theAllocator
,"Error: no allocator");
748 return NS_ERROR_FAILURE
;
750 // Highlight all parts of all erroneous tags.
751 if (mSyntaxHighlight
&& aTagInError
) {
752 CStartToken
* theTagToken
=
753 static_cast<CStartToken
*>
754 (theAllocator
->CreateTokenOfType(eToken_start
,
756 NS_LITERAL_STRING("SPAN")));
757 NS_ENSURE_TRUE(theTagToken
, NS_ERROR_OUT_OF_MEMORY
);
758 mErrorNode
.Init(theTagToken
, theAllocator
);
759 AddAttrToNode(mErrorNode
, theAllocator
,
760 NS_LITERAL_STRING("class"),
761 NS_LITERAL_STRING("error"));
762 mSink
->OpenContainer(mErrorNode
);
763 IF_FREE(theTagToken
, theAllocator
);
766 fprintf(gDumpFile
, "<span class=\"error\">");
771 if (kBeforeText
[aTagType
][0] != 0) {
772 NS_ConvertASCIItoUTF16
beforeText(kBeforeText
[aTagType
]);
773 mITextToken
.SetIndirectString(beforeText
);
774 nsCParserNode
theNode(&mITextToken
, 0/*stack token*/);
775 mSink
->AddLeaf(theNode
);
778 if (gDumpFile
&& kDumpFileBeforeText
[aTagType
][0])
779 fprintf(gDumpFile
, kDumpFileBeforeText
[aTagType
]);
780 #endif // DUMP_TO_FILE
782 if (mSyntaxHighlight
&& aTagType
!= kText
) {
783 CStartToken
* theTagToken
=
784 static_cast<CStartToken
*>
785 (theAllocator
->CreateTokenOfType(eToken_start
,
787 NS_LITERAL_STRING("SPAN")));
788 NS_ENSURE_TRUE(theTagToken
, NS_ERROR_OUT_OF_MEMORY
);
789 mStartNode
.Init(theTagToken
, theAllocator
);
790 AddAttrToNode(mStartNode
, theAllocator
,
791 NS_LITERAL_STRING("class"),
792 NS_ConvertASCIItoUTF16(kElementClasses
[aTagType
]));
793 mSink
->OpenContainer(mStartNode
); //emit <starttag>...
794 IF_FREE(theTagToken
, theAllocator
);
797 fprintf(gDumpFile
, "<span class=\"");
798 fprintf(gDumpFile
, kElementClasses
[aTagType
]);
799 fprintf(gDumpFile
, "\">");
801 #endif // DUMP_TO_FILE
806 mITextToken
.SetIndirectString(aText
); //now emit the tag name...
808 nsCParserNode
theNode(&mITextToken
, 0/*stack token*/);
809 mSink
->AddLeaf(theNode
);
812 fputs(NS_ConvertUTF16toUTF8(aText
).get(), gDumpFile
);
814 #endif // DUMP_TO_FILE
816 if (mSyntaxHighlight
&& aTagType
!= kText
) {
817 mStartNode
.ReleaseAll();
818 mSink
->CloseContainer(eHTMLTag_span
); //emit </endtag>...
821 fprintf(gDumpFile
, "</span>");
822 #endif //DUMP_TO_FILE
826 result
=WriteAttributes(aText
, theAllocator
, attrCount
, aTagInError
);
829 // Tokens are set in error if their ending > is not there, so don't output
831 if (!aTagInError
&& kAfterText
[aTagType
][0] != 0) {
832 NS_ConvertASCIItoUTF16
afterText(kAfterText
[aTagType
]);
833 mITextToken
.SetIndirectString(afterText
);
834 nsCParserNode
theNode(&mITextToken
, 0/*stack token*/);
835 mSink
->AddLeaf(theNode
);
838 if (!aTagInError
&& gDumpFile
&& kDumpFileAfterText
[aTagType
][0])
839 fprintf(gDumpFile
, kDumpFileAfterText
[aTagType
]);
840 #endif // DUMP_TO_FILE
842 if (mSyntaxHighlight
&& aTagInError
) {
843 mErrorNode
.ReleaseAll();
844 mSink
->CloseContainer(eHTMLTag_span
); //emit </endtag>...
847 fprintf(gDumpFile
, "</span>");
848 #endif //DUMP_TO_FILE
858 * @update gess 3/25/98
859 * @param aToken -- token object to be put into content model
860 * @return 0 if all is well; non-zero is an error
862 NS_IMETHODIMP
CViewSourceHTML::HandleToken(CToken
* aToken
,nsIParser
* aParser
)
864 nsresult result
=NS_OK
;
865 CHTMLToken
* theToken
= (CHTMLToken
*)(aToken
);
866 eHTMLTokenTypes theType
= (eHTMLTokenTypes
)theToken
->GetTokenType();
868 mParser
=(nsParser
*)aParser
;
869 mSink
=(nsIHTMLContentSink
*)aParser
->GetContentSink();
871 mTokenNode
.Init(theToken
, mTokenizer
->GetTokenAllocator());
877 const nsSubstring
& startValue
= aToken
->GetStringValue();
878 result
= WriteTag(kStartTag
,startValue
,aToken
->GetAttributeCount(),aToken
->IsInError());
880 if((ePlainText
!=mDocType
) && mParser
&& (NS_OK
==result
)) {
881 result
= mSink
->NotifyTagObservers(&mTokenNode
);
888 const nsSubstring
& endValue
= aToken
->GetStringValue();
889 result
= WriteTag(kEndTag
,endValue
,aToken
->GetAttributeCount(),aToken
->IsInError());
893 case eToken_cdatasection
:
896 theStr
.AssignLiteral("<!");
897 theStr
.Append(aToken
->GetStringValue());
898 if (!aToken
->IsInError()) {
899 theStr
.AppendLiteral(">");
901 result
=WriteTag(kCData
,theStr
,0,aToken
->IsInError());
905 case eToken_markupDecl
:
908 theStr
.AssignLiteral("<!");
909 theStr
.Append(aToken
->GetStringValue());
910 if (!aToken
->IsInError()) {
911 theStr
.AppendLiteral(">");
913 result
=WriteTag(kMarkupDecl
,theStr
,0,aToken
->IsInError());
920 aToken
->AppendSourceTo(theStr
);
921 result
=WriteTag(kComment
,theStr
,0,aToken
->IsInError());
925 case eToken_doctypeDecl
:
927 const nsSubstring
& doctypeValue
= aToken
->GetStringValue();
928 result
=WriteTag(kDoctype
,doctypeValue
,0,aToken
->IsInError());
934 const nsSubstring
& newlineValue
= aToken
->GetStringValue();
935 result
=WriteTag(kText
,newlineValue
,0,PR_FALSE
);
937 if (NS_VIEWSOURCE_TOKENS_PER_BLOCK
> 0 &&
938 mTokenCount
> NS_VIEWSOURCE_TOKENS_PER_BLOCK
) {
944 case eToken_whitespace
:
946 const nsSubstring
& wsValue
= aToken
->GetStringValue();
947 result
=WriteTag(kText
,wsValue
,0,PR_FALSE
);
949 if (NS_VIEWSOURCE_TOKENS_PER_BLOCK
> 0 &&
950 mTokenCount
> NS_VIEWSOURCE_TOKENS_PER_BLOCK
&&
951 !wsValue
.IsEmpty()) {
952 PRUnichar ch
= wsValue
.Last();
953 if (ch
== kLF
|| ch
== kCR
)
961 const nsSubstring
& str
= aToken
->GetStringValue();
962 result
=WriteTag(kText
,str
,aToken
->GetAttributeCount(),aToken
->IsInError());
964 if (NS_VIEWSOURCE_TOKENS_PER_BLOCK
> 0 &&
965 mTokenCount
> NS_VIEWSOURCE_TOKENS_PER_BLOCK
&& !str
.IsEmpty()) {
966 PRUnichar ch
= str
.Last();
967 if (ch
== kLF
|| ch
== kCR
)
975 result
=WriteTag(kEntity
,aToken
->GetStringValue(),0,aToken
->IsInError());
978 case eToken_instruction
:
979 result
=WriteTag(kPI
,aToken
->GetStringValue(),0,aToken
->IsInError());
986 mTokenNode
.ReleaseAll();
991 PRBool
CViewSourceHTML::IsUrlAttribute(const nsAString
& tagName
,
992 const nsAString
& attrName
,
993 const nsAString
& attrValue
) {
994 const nsSubstring
&trimmedAttrName
= TrimTokenValue(attrName
);
996 PRBool isHref
= trimmedAttrName
.LowerCaseEqualsLiteral("href");
997 PRBool isSrc
= !isHref
&& trimmedAttrName
.LowerCaseEqualsLiteral("src");
999 // If this is the HREF attribute of a BASE element, then update the base URI.
1000 // This doesn't feel like the ideal place for this, but the alternatives don't
1001 // seem all that nice either.
1002 if (isHref
&& tagName
.LowerCaseEqualsLiteral("base")) {
1003 const nsSubstring
& baseSpec
= TrimTokenValue(attrValue
);
1004 SetBaseURI(baseSpec
);
1007 return isHref
|| isSrc
;
1010 void CViewSourceHTML::WriteHrefAttribute(nsTokenAllocator
* allocator
,
1011 const nsAString
& href
) {
1012 // The "href" will typically contain not only the href proper, but the single
1013 // or double quotes and often some surrounding whitespace as well. Find the
1014 // location of the href proper inside the string.
1015 nsAString::const_iterator startProper
, endProper
;
1016 href
.BeginReading(startProper
);
1017 href
.EndReading(endProper
);
1018 TrimTokenValue(startProper
, endProper
);
1020 // Break the href into three parts, the preceding text, the href proper, and
1021 // the succeeding text.
1022 nsAString::const_iterator start
, end
;
1023 href
.BeginReading(start
);
1024 href
.EndReading(end
);
1025 const nsAString
&precedingText
= Substring(start
, startProper
);
1026 const nsAString
&hrefProper
= Substring(startProper
, endProper
);
1027 const nsAString
&succeedingText
= Substring(endProper
, end
);
1029 nsAutoString fullPrecedingText
;
1030 fullPrecedingText
.Assign(kEqual
);
1031 fullPrecedingText
.Append(precedingText
);
1033 // Regular URLs and view-source URLs work the same way for .js and .css files.
1034 // However, if the user follows a link in the view source window to a .html
1035 // file (i.e. the HREF in an A tag), then presumably they will expect to see
1036 // the *source* of that new page, not the rendered version. So for now we
1037 // just slap a "view-source:" at the beginning of each URL. There are two
1038 // big downsides to doing it this way -- we must make relative URLs into
1039 // absolute URLs before we can turn them into view-source URLs, and links
1040 // to images don't work right -- nobody wants to see the bytes constituting a
1041 // PNG rendered as text. A smarter view-source handler might be able to deal
1042 // with the latter problem.
1044 // Construct a "view-source" URL for the HREF.
1045 nsAutoString viewSourceUrl
;
1046 CreateViewSourceURL(hrefProper
, viewSourceUrl
);
1048 // Construct the HTML that will represent the HREF.
1049 NS_NAMED_LITERAL_STRING(HREF
, "href");
1050 if (fullPrecedingText
.Length() > 0) {
1051 WriteTextInSpan(fullPrecedingText
, allocator
, EmptyString(), EmptyString());
1053 WriteTextInAnchor(hrefProper
, allocator
, HREF
, viewSourceUrl
);
1054 if (succeedingText
.Length() > 0) {
1055 WriteTextInSpan(succeedingText
, allocator
, EmptyString(), EmptyString());
1059 nsresult
CViewSourceHTML::CreateViewSourceURL(const nsAString
& linkUrl
,
1060 nsString
& viewSourceUrl
) {
1061 nsCOMPtr
<nsIURI
> baseURI
;
1062 nsCOMPtr
<nsIURI
> hrefURI
;
1065 // Default the view source URL to the empty string in case we fail. Links
1066 // with empty HREFs are essentially non-functional, at least as of Firefox
1067 // 3.03. This is preferrable behavior to links that look good but then 404.
1068 viewSourceUrl
.Truncate();
1070 // Get the character set.
1073 mParser
->GetDocumentCharset(charset
, source
);
1076 rv
= GetBaseURI(getter_AddRefs(baseURI
));
1077 NS_ENSURE_SUCCESS(rv
, rv
);
1079 // Use the link URL and the base URI to build a URI for the link.
1080 rv
= NS_NewURI(getter_AddRefs(hrefURI
), linkUrl
, charset
.get(), baseURI
);
1081 NS_ENSURE_SUCCESS(rv
, rv
);
1083 // Get the absolute URL from the link URI.
1084 nsCString absoluteLinkUrl
;
1085 hrefURI
->GetSpec(absoluteLinkUrl
);
1087 // Prepend "view-source:" onto the absolute URL and store it in the out param.
1088 viewSourceUrl
.AssignLiteral("view-source:");
1089 viewSourceUrl
.AppendWithConversion(absoluteLinkUrl
);
1094 void CViewSourceHTML::WriteTextInSpan(const nsAString
& text
,
1095 nsTokenAllocator
* allocator
,
1096 const nsAString
& attrName
,
1097 const nsAString
& attrValue
) {
1098 NS_NAMED_LITERAL_STRING(SPAN
, "SPAN");
1099 WriteTextInElement(SPAN
, eHTMLTag_span
, text
, allocator
, attrName
, attrValue
);
1102 void CViewSourceHTML::WriteTextInAnchor(const nsAString
& text
,
1103 nsTokenAllocator
* allocator
,
1104 const nsAString
& attrName
,
1105 const nsAString
& attrValue
) {
1106 NS_NAMED_LITERAL_STRING(ANCHOR
, "A");
1107 WriteTextInElement(ANCHOR
, eHTMLTag_a
, text
, allocator
, attrName
, attrValue
);
1110 void CViewSourceHTML::WriteTextInElement(const nsAString
& tagName
,
1111 eHTMLTags tagType
, const nsAString
& text
,
1112 nsTokenAllocator
* allocator
,
1113 const nsAString
& attrName
,
1114 const nsAString
& attrValue
) {
1115 // Open the element, supplying the attribute, if any.
1116 nsTokenAllocator
* theAllocator
= mTokenizer
->GetTokenAllocator();
1117 if (!theAllocator
) {
1121 CStartToken
* startToken
=
1122 static_cast<CStartToken
*>
1123 (theAllocator
->CreateTokenOfType(eToken_start
, tagType
, tagName
));
1128 nsCParserStartNode
startNode(startToken
, theAllocator
);
1129 if (!attrName
.IsEmpty()) {
1130 AddAttrToNode(startNode
, allocator
, attrName
, attrValue
);
1132 mSink
->OpenContainer(startNode
);
1134 // Add the text node.
1135 CTextToken
textToken(text
);
1136 nsCParserNode
textNode(&textToken
, 0/*stack token*/);
1137 mSink
->AddLeaf(textNode
);
1139 // Close the element.
1140 mSink
->CloseContainer(tagType
);
1143 const nsDependentSubstring
CViewSourceHTML::TrimTokenValue(const nsAString
& tokenValue
) {
1144 nsAString::const_iterator start
, end
;
1145 tokenValue
.BeginReading(start
);
1146 tokenValue
.EndReading(end
);
1147 TrimTokenValue(start
, end
);
1148 return Substring(start
, end
);
1151 void CViewSourceHTML::TrimTokenValue(nsAString::const_iterator
& start
,
1152 nsAString::const_iterator
& end
) {
1153 // Token values -- tag names, attribute names, and attribute values --
1154 // generally contain adjacent whitespace and, in the case of attribute values,
1155 // the surrounding double or single quotes. Return a new string with this
1156 // adjacent text stripped off, so only the value proper remains.
1158 // Skip past any whitespace or quotes on the left.
1159 while (start
!= end
) {
1160 if (!IsTokenValueTrimmableCharacter(*start
)) break;
1164 // Skip past any whitespace or quotes on the right. Note that the interval
1165 // start..end is half-open. That means the last character of the interval is
1167 while (end
!= start
) {
1169 if (!IsTokenValueTrimmableCharacter(*end
)) {
1170 ++end
; // we've actually gone one too far at this point, so adjust.
1176 PRBool
CViewSourceHTML::IsTokenValueTrimmableCharacter(PRUnichar ch
) {
1177 if (ch
== ' ') return PR_TRUE
;
1178 if (ch
== '\t') return PR_TRUE
;
1179 if (ch
== '\r') return PR_TRUE
;
1180 if (ch
== '\n') return PR_TRUE
;
1181 if (ch
== '\'') return PR_TRUE
;
1182 if (ch
== '"') return PR_TRUE
;
1186 nsresult
CViewSourceHTML::GetBaseURI(nsIURI
**result
) {
1187 nsresult rv
= NS_OK
;
1189 rv
= SetBaseURI(mFilename
);
1191 NS_IF_ADDREF(*result
= mBaseURI
);
1195 nsresult
CViewSourceHTML::SetBaseURI(const nsAString
& baseSpec
) {
1196 // Get the character set.
1199 mParser
->GetDocumentCharset(charset
, source
);
1201 // Create a new base URI and store it in mBaseURI.
1202 nsCOMPtr
<nsIURI
> baseURI
;
1203 nsresult rv
= NS_NewURI(getter_AddRefs(baseURI
), baseSpec
, charset
.get());
1204 NS_ENSURE_SUCCESS(rv
, rv
);