/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/NPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All * Rights Reserved. * */ #include "nsCOMPtr.h" #include "nsXIFConverter.h" #include #include "nsIDOMSelection.h" MOZ_DECL_CTOR_COUNTER(nsXIFConverter); NS_IMPL_ISUPPORTS(nsXIFConverter, NS_GET_IID(nsIXIFConverter)) nsresult NS_NewXIFConverter(nsIXIFConverter **aResult) { *aResult = (nsIXIFConverter *)new nsXIFConverter; if (!*aResult) return NS_ERROR_OUT_OF_MEMORY; NS_ADDREF(*aResult); return NS_OK; } nsXIFConverter::nsXIFConverter() : mIndent(0) { MOZ_COUNT_CTOR(nsXIFConverter); NS_INIT_REFCNT(); mInScript = PR_FALSE; } nsXIFConverter::~nsXIFConverter() { MOZ_COUNT_DTOR(nsXIFConverter); #ifdef DEBUG_XIF WriteDebugFile(); #endif } NS_IMETHODIMP nsXIFConverter::Init(nsString &aBuffer) { mBuffer = &aBuffer; NS_ENSURE_ARG_POINTER(mBuffer); char* prolog = "\n"; char* doctype = "\n"; mBuffer->AppendWithConversion(prolog); mBuffer->AppendWithConversion(doctype); mAttr.AssignWithConversion("attr"); mName.AssignWithConversion("name"); mValue.AssignWithConversion("value"); mContent.AssignWithConversion("content"); mComment.AssignWithConversion("comment"); mContainer.AssignWithConversion("container"); mLeaf.AssignWithConversion("leaf"); mIsa.AssignWithConversion("isa"); mEntity.AssignWithConversion("entity"); mSelector.AssignWithConversion("css_selector"); mRule.AssignWithConversion("css_rule"); mSheet.AssignWithConversion("css_stylesheet"); mNULL.AssignWithConversion("?NULL"); mSpacing.AssignWithConversion(" "); mSpace.AssignWithConversion((char)' '); mLT.AssignWithConversion((char)'<'); mGT.AssignWithConversion((char)'>'); mLF.AssignWithConversion((char)'\n'); mSlash.AssignWithConversion((char)'/'); mBeginComment.AssignWithConversion(""); mQuote.AssignWithConversion((char)'\"'); mEqual.AssignWithConversion((char)'='); mMarkupDeclarationOpen.AssignWithConversion("markup_declaration"); return NS_OK; } NS_IMETHODIMP nsXIFConverter::SetSelection(nsIDOMSelection* aSelection) { NS_ENSURE_ARG_POINTER(aSelection); NS_ENSURE_TRUE(mBuffer,NS_ERROR_NOT_INITIALIZED); mSelectionWeak = getter_AddRefs( NS_GetWeakReference(aSelection) ); BeginStartTag( NS_ConvertToString("encode") ); if (!mSelectionWeak) { AddAttribute( NS_ConvertToString("selection"), NS_ConvertToString("0") ); } else { AddAttribute( NS_ConvertToString("selection"), NS_ConvertToString("1") ); } FinishStartTag(NS_ConvertToString("encode"),PR_TRUE,PR_TRUE); return NS_OK; } NS_IMETHODIMP nsXIFConverter::GetSelection(nsIDOMSelection** aSelection) { NS_ENSURE_ARG_POINTER(aSelection); nsCOMPtr sel = do_QueryReferent(mSelectionWeak); NS_IF_ADDREF(*aSelection = sel); return *aSelection?NS_OK :NS_ERROR_FAILURE; } NS_IMETHODIMP nsXIFConverter::BeginStartTag(const nsString& aTag) { NS_ENSURE_TRUE(mBuffer,NS_ERROR_NOT_INITIALIZED); // Make all element names lowercase nsAutoString tag(aTag); tag.ToLowerCase(); for (PRInt32 i = mIndent; --i >= 0; ) mBuffer->Append(mSpacing); mBuffer->Append(mLT); mBuffer->Append(tag); return NS_OK; } NS_IMETHODIMP nsXIFConverter::BeginStartTag(nsIAtom* aTag) { NS_ENSURE_TRUE(mBuffer,NS_ERROR_NOT_INITIALIZED); nsAutoString tag; tag.Assign(mNULL); if (nsnull != aTag) aTag->ToString(tag); BeginStartTag(tag); return NS_OK; } NS_IMETHODIMP nsXIFConverter::AddAttribute(const nsString& aName, const nsString& aValue) { NS_ENSURE_TRUE(mBuffer,NS_ERROR_NOT_INITIALIZED); // Make all attribute names lowercase nsAutoString name(aName); name.ToLowerCase(); mBuffer->Append(mSpace); mBuffer->Append(name); mBuffer->Append(mEqual); mBuffer->Append(mQuote); // XML requires quoted attributes mBuffer->Append(aValue); mBuffer->Append(mQuote); return NS_OK; } NS_IMETHODIMP nsXIFConverter::AddHTMLAttribute(const nsString& aName, const nsString& aValue) { NS_ENSURE_TRUE(mBuffer,NS_ERROR_NOT_INITIALIZED); BeginStartTag(mAttr); AddAttribute(mName,aName); AddAttribute(mValue,aValue); FinishStartTag(mAttr,PR_TRUE,PR_TRUE); return NS_OK; } NS_IMETHODIMP nsXIFConverter::AddAttribute(const nsString& aName, nsIAtom* aValue) { NS_ENSURE_TRUE(mBuffer,NS_ERROR_NOT_INITIALIZED); nsAutoString value(mNULL); if (nsnull != aValue) aValue->ToString(value); AddAttribute(aName,value); return NS_OK; } NS_IMETHODIMP nsXIFConverter::AddAttribute(const nsString& aName) { NS_ENSURE_TRUE(mBuffer,NS_ERROR_NOT_INITIALIZED); // A convention in XML DTD's is that ATTRIBUTE // names are lowercase. nsAutoString name(aName); name.ToLowerCase(); mBuffer->Append(mSpace); mBuffer->Append(name); return NS_OK; } NS_IMETHODIMP nsXIFConverter::AddAttribute(nsIAtom* aName) { NS_ENSURE_TRUE(mBuffer,NS_ERROR_NOT_INITIALIZED); nsAutoString name(mNULL); if (nsnull != aName) aName->ToString(name); AddAttribute(name); return NS_OK; } NS_IMETHODIMP nsXIFConverter::FinishStartTag(const nsString& aTag, PRBool aIsEmpty, PRBool aAddReturn) { NS_ENSURE_TRUE(mBuffer,NS_ERROR_NOT_INITIALIZED); if (aIsEmpty) { mBuffer->Append(mSlash); mBuffer->Append(mGT); // mIndent--; } else mBuffer->Append(mGT); if (aAddReturn == PR_TRUE) mBuffer->Append(mLF); return NS_OK; } NS_IMETHODIMP nsXIFConverter::FinishStartTag(nsIAtom* aTag, PRBool aIsEmpty, PRBool aAddReturn) { NS_ENSURE_TRUE(mBuffer,NS_ERROR_NOT_INITIALIZED); nsAutoString tag(mNULL); if (nsnull != aTag) aTag->ToString(tag); FinishStartTag(tag,aIsEmpty,aAddReturn); return NS_OK; } NS_IMETHODIMP nsXIFConverter::AddStartTag(const nsString& aTag, PRBool aAddReturn) { NS_ENSURE_TRUE(mBuffer,NS_ERROR_NOT_INITIALIZED); BeginStartTag(aTag); FinishStartTag(aTag,PR_FALSE,aAddReturn); return NS_OK; } NS_IMETHODIMP nsXIFConverter::AddStartTag(nsIAtom* aTag, PRBool aAddReturn) { NS_ENSURE_TRUE(mBuffer,NS_ERROR_NOT_INITIALIZED); nsAutoString tag(mNULL); if (nsnull != aTag) aTag->ToString(tag); AddStartTag(tag,aAddReturn); return NS_OK; } NS_IMETHODIMP nsXIFConverter::AddEndTag(const nsString& aTag, PRBool aDoIndent, PRBool aDoReturn) { NS_ENSURE_TRUE(mBuffer,NS_ERROR_NOT_INITIALIZED); // A convention in XML DTD's is that ELEMENT // names are UPPERCASE. nsAutoString tag(aTag); tag.ToLowerCase(); // mIndent--; if (aDoIndent == PR_TRUE) for (PRInt32 i = mIndent; --i >= 0; ) mBuffer->Append(mSpacing); mBuffer->Append(mLT); mBuffer->Append(mSlash); mBuffer->Append(tag); mBuffer->Append(mGT); if (aDoReturn == PR_TRUE) mBuffer->Append(mLF); return NS_OK; } NS_IMETHODIMP nsXIFConverter::AddEndTag(nsIAtom* aTag, PRBool aDoIndent, PRBool aDoReturn) { NS_ENSURE_TRUE(mBuffer,NS_ERROR_NOT_INITIALIZED); nsAutoString tag(mNULL); if (nsnull != aTag) aTag->ToString(tag); AddEndTag(tag,aDoIndent,aDoReturn); return NS_OK; } NS_IMETHODIMP nsXIFConverter::IsMarkupEntity(const PRUnichar aChar, PRBool *aReturn) { NS_ENSURE_TRUE(mBuffer,NS_ERROR_NOT_INITIALIZED); NS_ENSURE_ARG_POINTER(aReturn); // If we're in a script, pass entities straight through without conversion: if (mInScript) return PR_FALSE; *aReturn = PR_FALSE; switch (aChar) { case '<': case '>': case '&': *aReturn = PR_TRUE; break; } return NS_OK; } NS_IMETHODIMP nsXIFConverter::AddMarkupEntity(const PRUnichar aChar, PRBool *aReturn) { NS_ENSURE_TRUE(mBuffer,NS_ERROR_NOT_INITIALIZED); NS_ENSURE_ARG_POINTER(aReturn); nsAutoString data; PRBool result = PR_TRUE; switch (aChar) { case '<': data.AssignWithConversion("lt"); break; case '>': data.AssignWithConversion("gt"); break; case '&': data.AssignWithConversion("amp"); break; default: *aReturn = PR_FALSE; break; } if (*aReturn == PR_TRUE) { BeginStartTag(mEntity); AddAttribute(mValue,data); FinishStartTag(mEntity,PR_TRUE,PR_FALSE); } return NS_OK; } NS_IMETHODIMP nsXIFConverter::AddContent(const nsString& aContent) { NS_ENSURE_TRUE(mBuffer,NS_ERROR_NOT_INITIALIZED); nsAutoString tag(mContent); AddStartTag(tag,PR_FALSE); PRBool startTagAdded = PR_TRUE; PRInt32 length = aContent.Length(); PRUnichar ch; PRBool boolVal(PR_FALSE); for (PRInt32 i = 0; i < length; i++) { ch = aContent[i]; IsMarkupEntity(ch, &boolVal); if (boolVal) { if (startTagAdded == PR_TRUE) { AddEndTag(tag,PR_FALSE); startTagAdded = PR_FALSE; } AddMarkupEntity(ch, &boolVal); } else { if (startTagAdded == PR_FALSE) { AddStartTag(tag,PR_FALSE); startTagAdded = PR_TRUE; } mBuffer->Append(ch); } } AddEndTag(tag,PR_FALSE); return NS_OK; } // // AddComment is used to add a XIF comment, // not something that was a comment in the html; // that would be a content comment. // NS_IMETHODIMP nsXIFConverter::AddComment(const nsString& aContent) { NS_ENSURE_TRUE(mBuffer,NS_ERROR_NOT_INITIALIZED); mBuffer->Append(mBeginComment); mBuffer->Append(aContent); mBuffer->Append(mEndComment); return NS_OK; } // // AddContentComment is used to add an HTML comment, // which will be mapped back into the right thing // in the content sink on the other end when this is parsed. // NS_IMETHODIMP nsXIFConverter::AddContentComment(const nsString& aContent) { NS_ENSURE_TRUE(mBuffer,NS_ERROR_NOT_INITIALIZED); nsAutoString tag(mComment); AddStartTag(tag, PR_FALSE); AddContent(aContent); AddEndTag(tag, PR_FALSE); return NS_OK; } // // Use this method to add DOCTYPE, MARKEDSECTION, etc., that are // not really classified as ** tags ** // NS_IMETHODIMP nsXIFConverter::AddMarkupDeclaration(const nsString& aContent) { NS_ENSURE_TRUE(mBuffer,NS_ERROR_NOT_INITIALIZED); nsAutoString tag(mMarkupDeclarationOpen); AddStartTag(tag, PR_FALSE); AddContent(aContent); AddEndTag(tag, PR_FALSE); return NS_OK; } NS_IMETHODIMP nsXIFConverter::BeginContainer(nsIAtom* aTag) { NS_ENSURE_TRUE(mBuffer,NS_ERROR_NOT_INITIALIZED); nsAutoString container(mContainer); BeginStartTag(container); AddAttribute(mIsa,aTag); FinishStartTag(container,PR_FALSE); return NS_OK; } NS_IMETHODIMP nsXIFConverter::EndContainer(nsIAtom* aTag) { NS_ENSURE_TRUE(mBuffer,NS_ERROR_NOT_INITIALIZED); nsAutoString container(mContainer); AddEndTag(container,PR_TRUE,PR_FALSE); nsAutoString tag(mNULL); if (nsnull != aTag) aTag->ToString(tag); AddComment(tag); mBuffer->Append(mLF); return NS_OK; } NS_IMETHODIMP nsXIFConverter::BeginContainer(const nsString& aTag) { NS_ENSURE_TRUE(mBuffer,NS_ERROR_NOT_INITIALIZED); nsAutoString container(mContainer); BeginStartTag(container); AddAttribute(mIsa,aTag); FinishStartTag(container,PR_FALSE,PR_FALSE); // Remember if we're inside a script tag: if (aTag.EqualsWithConversion("script") || aTag.EqualsWithConversion("style")) mInScript = PR_TRUE; return NS_OK; } NS_IMETHODIMP nsXIFConverter::EndContainer(const nsString& aTag) { NS_ENSURE_TRUE(mBuffer,NS_ERROR_NOT_INITIALIZED); nsAutoString container(mContainer); AddEndTag(container,PR_TRUE,PR_FALSE); AddComment(aTag); mBuffer->Append(mLF); // Remember if we're exiting a script tag: if (aTag.EqualsWithConversion("script") || aTag.EqualsWithConversion("style")) mInScript = PR_FALSE; return NS_OK; } NS_IMETHODIMP nsXIFConverter::BeginLeaf(const nsString& aTag) { NS_ENSURE_TRUE(mBuffer,NS_ERROR_NOT_INITIALIZED); // XXX: Complete hack to prevent the style leaf // From being created until the style sheet work // is redone. -- gpk 1/27/99 if (aTag.EqualsIgnoreCase("STYLE")) return NS_OK; BeginStartTag(mLeaf); AddAttribute(mIsa,aTag); FinishStartTag(mLeaf,PR_FALSE); return NS_OK; } NS_IMETHODIMP nsXIFConverter::EndLeaf(const nsString& aTag) { NS_ENSURE_TRUE(mBuffer,NS_ERROR_NOT_INITIALIZED); // XXX: Complete hack to prevent the style leaf // From being created until the style sheet work // is redone. -- gpk 1/27/99 if (aTag.EqualsIgnoreCase("STYLE")) return NS_OK; AddEndTag(mLeaf,PR_TRUE,PR_FALSE); AddComment(aTag); mBuffer->Append(mLF); return NS_OK; } NS_IMETHODIMP nsXIFConverter::BeginCSSStyleSheet() { NS_ENSURE_TRUE(mBuffer,NS_ERROR_NOT_INITIALIZED); AddStartTag(mSheet); return NS_OK; } NS_IMETHODIMP nsXIFConverter::EndCSSStyleSheet() { NS_ENSURE_TRUE(mBuffer,NS_ERROR_NOT_INITIALIZED); AddEndTag(mSheet); return NS_OK; } NS_IMETHODIMP nsXIFConverter::BeginCSSRule() { NS_ENSURE_TRUE(mBuffer,NS_ERROR_NOT_INITIALIZED); AddStartTag(mRule); return NS_OK; } NS_IMETHODIMP nsXIFConverter::EndCSSRule() { NS_ENSURE_TRUE(mBuffer,NS_ERROR_NOT_INITIALIZED); AddEndTag(mRule); return NS_OK; } NS_IMETHODIMP nsXIFConverter::BeginCSSSelectors() { NS_ENSURE_TRUE(mBuffer,NS_ERROR_NOT_INITIALIZED); BeginStartTag(mSelector); return NS_OK; } NS_IMETHODIMP nsXIFConverter::EndCSSSelectors() { NS_ENSURE_TRUE(mBuffer,NS_ERROR_NOT_INITIALIZED); FinishStartTag(mSelector,PR_TRUE); return NS_OK; } NS_IMETHODIMP nsXIFConverter::AddCSSSelectors(const nsString& aSelectors) { NS_ENSURE_TRUE(mBuffer,NS_ERROR_NOT_INITIALIZED); AddAttribute(NS_ConvertToString("selectors"),aSelectors); return NS_OK; } NS_IMETHODIMP nsXIFConverter::BeginCSSDeclarationList() { NS_ENSURE_TRUE(mBuffer,NS_ERROR_NOT_INITIALIZED); AddStartTag(NS_ConvertToString("css_declaration_list")); return NS_OK; } NS_IMETHODIMP nsXIFConverter::EndCSSDeclarationList() { NS_ENSURE_TRUE(mBuffer,NS_ERROR_NOT_INITIALIZED); AddEndTag(NS_ConvertToString("css_declaration_list"),PR_TRUE); return NS_OK; } NS_IMETHODIMP nsXIFConverter::BeginCSSDeclaration() { NS_ENSURE_TRUE(mBuffer,NS_ERROR_NOT_INITIALIZED); BeginStartTag(NS_ConvertToString("css_declaration")); return NS_OK; } NS_IMETHODIMP nsXIFConverter::AddCSSDeclaration(const nsString& aName, const nsString& aValue) { NS_ENSURE_TRUE(mBuffer,NS_ERROR_NOT_INITIALIZED); AddAttribute(NS_ConvertToString("property"),aName); AddAttribute(NS_ConvertToString("value"),aValue); return NS_OK; } NS_IMETHODIMP nsXIFConverter::EndCSSDeclaration() { NS_ENSURE_TRUE(mBuffer,NS_ERROR_NOT_INITIALIZED); FinishStartTag(NS_ConvertToString("css_declaration"),PR_TRUE); return NS_OK; } #ifdef DEBUG_XIF // // Leave a temp file for debugging purposes // NS_IMETHODIMP nsXIFConverter::WriteDebugFile() { #if defined(WIN32) const char* filename="c:\\temp\\xif.xif"; #elif defined(XP_UNIX) || defined(XP_BEOS) const char* filename="/tmp/xif.xif"; #elif defined(XP_MAC) // XXX Someone please write some Mac debugging code here! const char* filename="xif.xif"; #else const char* filename="xif.xif"; #endif ofstream out(filename); char* s = mBuffer->ToNewUTF8String(); if (s) { out << s; Recycle(s); } out.close(); return NS_OK; } #else NS_IMETHODIMP nsXIFConverter::WriteDebugFile() { return NS_ERROR_NOT_IMPLEMENTED; } #endif /* DEBUG */