Bug 436663. Work around ATSUI crasher caused by long Hebrew sequence. r=roc, sr=vlad
[wine-gecko.git] / parser / htmlparser / src / nsLoggingSink.cpp
blob8d43187c24c01c849a14f9cc2c74e1d15cf6776b
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 Communicator client 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):
25 * Alternatively, the contents of this file may be used under the terms of
26 * either of the GNU General Public License Version 2 or later (the "GPL"),
27 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
38 #include "nsIAtom.h"
39 #include "nsLoggingSink.h"
40 #include "nsHTMLTags.h"
41 #include "nsString.h"
42 #include "nsReadableUtils.h"
43 #include "prprf.h"
46 static NS_DEFINE_IID(kIContentSinkIID, NS_ICONTENT_SINK_IID);
47 static NS_DEFINE_IID(kIHTMLContentSinkIID, NS_IHTML_CONTENT_SINK_IID);
48 static NS_DEFINE_IID(kILoggingSinkIID, NS_ILOGGING_SINK_IID);
49 static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID);
51 nsresult
52 NS_NewHTMLLoggingSink(nsIContentSink** aInstancePtrResult)
54 NS_PRECONDITION(nsnull != aInstancePtrResult, "null ptr");
55 if (nsnull == aInstancePtrResult) {
56 return NS_ERROR_NULL_POINTER;
58 nsLoggingSink* it = new nsLoggingSink();
59 if (nsnull == it) {
60 return NS_ERROR_OUT_OF_MEMORY;
62 return it->QueryInterface(kIContentSinkIID, (void**) aInstancePtrResult);
65 nsLoggingSink::nsLoggingSink() {
66 mOutput = 0;
67 mLevel=-1;
68 mSink=0;
69 mParser=0;
72 nsLoggingSink::~nsLoggingSink() {
73 mSink=0;
74 if(mOutput && mAutoDeleteOutput) {
75 delete mOutput;
77 mOutput=0;
80 NS_IMPL_ISUPPORTS3(nsLoggingSink, nsILoggingSink, nsIContentSink, nsIHTMLContentSink)
82 NS_IMETHODIMP
83 nsLoggingSink::SetOutputStream(PRFileDesc *aStream,PRBool autoDeleteOutput) {
84 mOutput = aStream;
85 mAutoDeleteOutput=autoDeleteOutput;
86 return NS_OK;
89 static
90 void WriteTabs(PRFileDesc * out,int aTabCount) {
91 int tabs;
92 for(tabs=0;tabs<aTabCount;++tabs)
93 PR_fprintf(out, " ");
96 NS_IMETHODIMP
97 nsLoggingSink::WillTokenize() {
98 return NS_OK;
101 NS_IMETHODIMP
102 nsLoggingSink::WillBuildModel() {
104 WriteTabs(mOutput,++mLevel);
105 PR_fprintf(mOutput, "<begin>\n");
107 //proxy the call to the real sink if you have one.
108 if(mSink) {
109 mSink->WillBuildModel();
112 return NS_OK;
115 NS_IMETHODIMP
116 nsLoggingSink::DidBuildModel() {
118 WriteTabs(mOutput,--mLevel);
119 PR_fprintf(mOutput, "</begin>\n");
121 //proxy the call to the real sink if you have one.
122 nsresult theResult=NS_OK;
123 if(mSink) {
124 theResult=mSink->DidBuildModel();
127 return theResult;
130 NS_IMETHODIMP
131 nsLoggingSink::WillInterrupt() {
132 nsresult theResult=NS_OK;
134 //proxy the call to the real sink if you have one.
135 if(mSink) {
136 theResult=mSink->WillInterrupt();
139 return theResult;
142 NS_IMETHODIMP
143 nsLoggingSink::WillResume() {
144 nsresult theResult=NS_OK;
146 //proxy the call to the real sink if you have one.
147 if(mSink) {
148 theResult=mSink->WillResume();
151 return theResult;
154 NS_IMETHODIMP
155 nsLoggingSink::SetParser(nsIParser* aParser) {
156 nsresult theResult=NS_OK;
158 //proxy the call to the real sink if you have one.
159 if(mSink) {
160 theResult=mSink->SetParser(aParser);
163 NS_IF_RELEASE(mParser);
165 mParser = aParser;
167 NS_IF_ADDREF(mParser);
169 return theResult;
172 NS_IMETHODIMP
173 nsLoggingSink::OpenContainer(const nsIParserNode& aNode) {
175 OpenNode("container", aNode); //do the real logging work...
177 nsresult theResult=NS_OK;
179 //then proxy the call to the real sink if you have one.
180 if(mSink) {
181 theResult=mSink->OpenContainer(aNode);
184 return theResult;
188 NS_IMETHODIMP
189 nsLoggingSink::CloseContainer(const nsHTMLTag aTag) {
191 nsresult theResult=NS_OK;
193 nsHTMLTag nodeType = nsHTMLTag(aTag);
194 if ((nodeType >= eHTMLTag_unknown) &&
195 (nodeType <= nsHTMLTag(NS_HTML_TAG_MAX))) {
196 const PRUnichar* tag = nsHTMLTags::GetStringValue(nodeType);
197 theResult = CloseNode(NS_ConvertUTF16toUTF8(tag).get());
199 else theResult= CloseNode("???");
201 //then proxy the call to the real sink if you have one.
202 if(mSink) {
203 theResult=mSink->CloseContainer(aTag);
206 return theResult;
210 NS_IMETHODIMP
211 nsLoggingSink::AddLeaf(const nsIParserNode& aNode) {
212 LeafNode(aNode);
214 nsresult theResult=NS_OK;
216 //then proxy the call to the real sink if you have one.
217 if(mSink) {
218 theResult=mSink->AddLeaf(aNode);
221 return theResult;
226 * This gets called by the parser when you want to add
227 * a PI node to the current container in the content
228 * model.
230 * @updated gess 3/25/98
231 * @param
232 * @return
234 NS_IMETHODIMP
235 nsLoggingSink::AddProcessingInstruction(const nsIParserNode& aNode){
237 #ifdef VERBOSE_DEBUG
238 DebugDump("<",aNode.GetText(),(mNodeStackPos)*2);
239 #endif
241 nsresult theResult=NS_OK;
243 //then proxy the call to the real sink if you have one.
244 if(mSink) {
245 theResult=mSink->AddProcessingInstruction(aNode);
248 return theResult;
252 * This gets called by the parser when it encounters
253 * a DOCTYPE declaration in the HTML document.
256 NS_IMETHODIMP
257 nsLoggingSink::AddDocTypeDecl(const nsIParserNode& aNode) {
259 #ifdef VERBOSE_DEBUG
260 DebugDump("<",aNode.GetText(),(mNodeStackPos)*2);
261 #endif
263 nsresult theResult=NS_OK;
265 //then proxy the call to the real sink if you have one.
266 if(mSink) {
267 theResult=mSink->AddDocTypeDecl(aNode);
270 return theResult;
275 * This gets called by the parser when you want to add
276 * a comment node to the current container in the content
277 * model.
279 * @updated gess 3/25/98
280 * @param
281 * @return
283 NS_IMETHODIMP
284 nsLoggingSink::AddComment(const nsIParserNode& aNode){
286 #ifdef VERBOSE_DEBUG
287 DebugDump("<",aNode.GetText(),(mNodeStackPos)*2);
288 #endif
290 nsresult theResult=NS_OK;
292 //then proxy the call to the real sink if you have one.
293 if(mSink) {
294 theResult=mSink->AddComment(aNode);
297 return theResult;
301 NS_IMETHODIMP
302 nsLoggingSink::OpenHead() {
303 WriteTabs(mOutput,++mLevel);
304 PR_fprintf(mOutput,"<open container=head>\n");
306 nsresult theResult=NS_OK;
308 //then proxy the call to the real sink if you have one.
309 if(mSink) {
310 theResult=mSink->OpenHead();
313 return theResult;
316 nsresult
317 nsLoggingSink::OpenNode(const char* aKind, const nsIParserNode& aNode) {
318 WriteTabs(mOutput,++mLevel);
320 PR_fprintf(mOutput,"<open container=");
322 nsHTMLTag nodeType = nsHTMLTag(aNode.GetNodeType());
323 if ((nodeType >= eHTMLTag_unknown) &&
324 (nodeType <= nsHTMLTag(NS_HTML_TAG_MAX))) {
325 const PRUnichar* tag = nsHTMLTags::GetStringValue(nodeType);
326 PR_fprintf(mOutput, "\"%s\"", NS_ConvertUTF16toUTF8(tag).get());
328 else {
329 char* text = nsnull;
330 GetNewCString(aNode.GetText(), &text);
331 if(text) {
332 PR_fprintf(mOutput, "\"%s\"", text);
333 nsMemory::Free(text);
337 if (WillWriteAttributes(aNode)) {
338 PR_fprintf(mOutput, ">\n");
339 WriteAttributes(aNode);
340 PR_fprintf(mOutput, "</open>\n");
342 else {
343 PR_fprintf(mOutput, ">\n");
346 return NS_OK;
349 nsresult
350 nsLoggingSink::CloseNode(const char* aKind) {
351 WriteTabs(mOutput,mLevel--);
352 PR_fprintf(mOutput, "<close container=\"%s\">\n", aKind);
353 return NS_OK;
357 nsresult
358 nsLoggingSink::WriteAttributes(const nsIParserNode& aNode) {
360 WriteTabs(mOutput,1+mLevel);
361 nsAutoString tmp;
362 PRInt32 ac = aNode.GetAttributeCount();
363 for (PRInt32 i = 0; i < ac; ++i) {
364 char* key=nsnull;
365 char* value=nsnull;
366 const nsAString& k = aNode.GetKeyAt(i);
367 const nsAString& v = aNode.GetValueAt(i);
369 GetNewCString(k, &key);
370 if(key) {
371 PR_fprintf(mOutput, " <attr key=\"%s\" value=\"", key);
372 nsMemory::Free(key);
375 tmp.Truncate();
376 tmp.Append(v);
377 if(!tmp.IsEmpty()) {
378 PRUnichar first = tmp.First();
379 if ((first == '"') || (first == '\'')) {
380 if (tmp.Last() == first) {
381 tmp.Cut(0, 1);
382 PRInt32 pos = tmp.Length() - 1;
383 if (pos >= 0) {
384 tmp.Cut(pos, 1);
386 } else {
387 // Mismatched quotes - leave them in
390 GetNewCString(tmp, &value);
392 if(value) {
393 PR_fprintf(mOutput, "%s\"/>\n", value);
394 WriteTabs(mOutput,1+mLevel);
395 nsMemory::Free(value);
400 WriteTabs(mOutput,1+mLevel);
401 return NS_OK;
404 PRBool
405 nsLoggingSink::WillWriteAttributes(const nsIParserNode& aNode)
407 PRInt32 ac = aNode.GetAttributeCount();
408 if (0 != ac) {
409 return PR_TRUE;
411 return PR_FALSE;
414 nsresult
415 nsLoggingSink::LeafNode(const nsIParserNode& aNode)
417 WriteTabs(mOutput,1+mLevel);
418 nsHTMLTag nodeType = nsHTMLTag(aNode.GetNodeType());
420 if ((nodeType >= eHTMLTag_unknown) &&
421 (nodeType <= nsHTMLTag(NS_HTML_TAG_MAX))) {
422 const PRUnichar* tag = nsHTMLTags::GetStringValue(nodeType);
424 if(tag)
425 PR_fprintf(mOutput, "<leaf tag=\"%s\"", NS_ConvertUTF16toUTF8(tag).get());
426 else
427 PR_fprintf(mOutput, "<leaf tag=\"???\"");
429 if (WillWriteAttributes(aNode)) {
430 PR_fprintf(mOutput, ">\n");
431 WriteAttributes(aNode);
432 PR_fprintf(mOutput, "</leaf>\n");
434 else {
435 PR_fprintf(mOutput, "/>\n");
438 else {
439 PRInt32 pos;
440 nsAutoString tmp;
441 char* str = nsnull;
442 switch (nodeType) {
443 case eHTMLTag_whitespace:
444 case eHTMLTag_text:
445 GetNewCString(aNode.GetText(), &str);
446 if(str) {
447 PR_fprintf(mOutput, "<text value=\"%s\"/>\n", str);
448 nsMemory::Free(str);
450 break;
452 case eHTMLTag_newline:
453 PR_fprintf(mOutput, "<newline/>\n");
454 break;
456 case eHTMLTag_entity:
457 tmp.Append(aNode.GetText());
458 tmp.Cut(0, 1);
459 pos = tmp.Length() - 1;
460 if (pos >= 0) {
461 tmp.Cut(pos, 1);
463 PR_fprintf(mOutput, "<entity value=\"%s\"/>\n", NS_LossyConvertUTF16toASCII(tmp).get());
464 break;
466 default:
467 NS_NOTREACHED("unsupported leaf node type");
468 }//switch
470 return NS_OK;
473 nsresult
474 nsLoggingSink::QuoteText(const nsAString& aValue, nsString& aResult) {
475 aResult.Truncate();
477 if you're stepping through the string anyway, why not use iterators instead of forcing the string to copy?
479 const nsPromiseFlatString& flat = PromiseFlatString(aValue);
480 const PRUnichar* cp = flat.get();
481 const PRUnichar* end = cp + aValue.Length();
482 while (cp < end) {
483 PRUnichar ch = *cp++;
484 if (ch == '"') {
485 aResult.AppendLiteral("&quot;");
487 else if (ch == '&') {
488 aResult.AppendLiteral("&amp;");
490 else if ((ch < 32) || (ch >= 127)) {
491 aResult.AppendLiteral("&#");
492 aResult.AppendInt(PRInt32(ch), 10);
493 aResult.Append(PRUnichar(';'));
495 else {
496 aResult.Append(ch);
499 return NS_OK;
503 * Use this method to convert nsString to char*.
504 * REMEMBER: Match this call with nsMemory::Free(aResult);
506 * @update 04/04/99 harishd
507 * @param aValue - The string value
508 * @param aResult - String coverted to char*.
510 nsresult
511 nsLoggingSink::GetNewCString(const nsAString& aValue, char** aResult)
513 nsresult result=NS_OK;
514 nsAutoString temp;
515 result=QuoteText(aValue,temp);
516 if(NS_SUCCEEDED(result)) {
517 *aResult = temp.IsEmpty() ? nsnull : ToNewCString(temp);
519 return result;
523 * This gets called when handling illegal contents, especially
524 * in dealing with tables. This method creates a new context.
526 * @update 04/04/99 harishd
527 * @param aPosition - The position from where the new context begins.
529 NS_IMETHODIMP
530 nsLoggingSink::BeginContext(PRInt32 aPosition)
532 return NS_OK;
536 * This method terminates any new context that got created by
537 * BeginContext and switches back to the main context.
539 * @update 04/04/99 harishd
540 * @param aPosition - Validates the end of a context.
542 NS_IMETHODIMP
543 nsLoggingSink::EndContext(PRInt32 aPosition)
545 return NS_OK;