Mozilla/mozilla/layout/xml/document/src/nsXMLDocument.cpp
peterl%netscape.com 4671b6225b cleaned up style sheet ordering
git-svn-id: svn://10.0.0.236/trunk@18433 18797224-902f-48f8-a5cc-f745e15eee43
1999-01-23 07:09:06 +00:00

616 lines
15 KiB
C++

/* -*- 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.0 (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 Communicator client 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 "nsXMLDocument.h"
#include "nsWellFormedDTD.h"
#include "nsParserCIID.h"
#include "nsIParser.h"
#include "nsIXMLContent.h"
#include "nsIXMLContentSink.h"
#include "nsIPresShell.h"
#include "nsIPresContext.h"
#include "nsIContentViewerContainer.h"
#include "nsIWebShell.h"
#include "nsIDocumentLoader.h"
#include "nsIHTMLContent.h"
#include "nsHTMLParts.h"
#include "nsIHTMLStyleSheet.h"
#include "nsIHTMLCSSStyleSheet.h"
#include "nsIStyleSet.h"
#include "nsRepository.h"
#include "nsIDOMComment.h"
#include "nsIDOMElement.h"
#include "nsIDOMText.h"
// XXX The XML world depends on the html atoms
#include "nsHTMLAtoms.h"
#ifdef INCLUDE_XUL
#include "nsXULAtoms.h"
#endif
static NS_DEFINE_IID(kIDOMDocumentIID, NS_IDOMDOCUMENT_IID);
static NS_DEFINE_IID(kIDocumentIID, NS_IDOCUMENT_IID);
static NS_DEFINE_IID(kIXMLDocumentIID, NS_IXMLDOCUMENT_IID);
static NS_DEFINE_IID(kIWebShellIID, NS_IWEB_SHELL_IID);
static NS_DEFINE_IID(kIDOMCommentIID, NS_IDOMCOMMENT_IID);
static NS_DEFINE_IID(kIDOMElementIID, NS_IDOMELEMENT_IID);
static NS_DEFINE_IID(kIDOMTextIID, NS_IDOMTEXT_IID);
static NS_DEFINE_IID(kIHTMLContentContainerIID, NS_IHTMLCONTENTCONTAINER_IID);
static NS_DEFINE_IID(kIDOMNodeIID, NS_IDOMNODE_IID);
static NS_DEFINE_IID(kIDOMNodeListIID, NS_IDOMNODELIST_IID);
// ==================================================================
// =
// ==================================================================
nsXMLDocumentChildNodes::nsXMLDocumentChildNodes(nsXMLDocument* aDocument)
{
// We don't reference count our document reference (to avoid circular
// references). We'll be told when the document goes away.
mDocument = aDocument;
}
nsXMLDocumentChildNodes::~nsXMLDocumentChildNodes()
{
}
NS_IMETHODIMP
nsXMLDocumentChildNodes::GetLength(PRUint32* aLength)
{
if (nsnull != mDocument) {
PRUint32 prolog, epilog;
// The length is the sum of the prolog, epilog and
// document element;
mDocument->PrologCount(&prolog);
mDocument->EpilogCount(&epilog);
*aLength = prolog + epilog + 1;
}
else {
*aLength = 0;
}
return NS_OK;
}
NS_IMETHODIMP
nsXMLDocumentChildNodes::Item(PRUint32 aIndex, nsIDOMNode** aReturn)
{
nsresult result = NS_OK;
*aReturn = nsnull;
if (nsnull != mDocument) {
PRUint32 prolog, epilog;
mDocument->PrologCount(&prolog);
if (aIndex < prolog) {
// It's in the prolog
nsIContent* content;
result = mDocument->PrologElementAt(aIndex, &content);
if ((NS_OK == result) && (nsnull != content)) {
result = content->QueryInterface(kIDOMNodeIID, (void**)aReturn);
NS_RELEASE(content);
}
}
else if (aIndex == prolog) {
// It's the document element
nsIDOMElement* element;
result = mDocument->GetDocumentElement(&element);
if (NS_OK == result) {
result = element->QueryInterface(kIDOMNodeIID, (void**)aReturn);
NS_RELEASE(element);
}
}
else {
// It's in the epilog
nsIContent* content;
result = mDocument->EpilogElementAt(aIndex-prolog-1, &content);
if ((NS_OK == result) && (nsnull != content)) {
result = content->QueryInterface(kIDOMNodeIID, (void**)aReturn);
NS_RELEASE(content);
}
}
}
return result;
}
void
nsXMLDocumentChildNodes::DropReference()
{
mDocument = nsnull;
}
// ==================================================================
// =
// ==================================================================
NS_LAYOUT nsresult
NS_NewXMLDocument(nsIDocument** aInstancePtrResult)
{
nsXMLDocument* doc = new nsXMLDocument();
return doc->QueryInterface(kIDocumentIID, (void**) aInstancePtrResult);
}
nsXMLDocument::nsXMLDocument()
{
mParser = nsnull;
mAttrStyleSheet = nsnull;
mInlineStyleSheet = nsnull;
mProlog = nsnull;
mEpilog = nsnull;
mChildNodes = nsnull;
// XXX The XML world depends on the html atoms
nsHTMLAtoms::AddrefAtoms();
#ifdef INCLUDE_XUL
// XUL world lives within XML world until it gets a place of its own
nsXULAtoms::AddrefAtoms();
#endif
}
nsXMLDocument::~nsXMLDocument()
{
PRInt32 i, count;
nsIContent* content;
NS_IF_RELEASE(mParser);
if (nsnull != mAttrStyleSheet) {
mAttrStyleSheet->SetOwningDocument(nsnull);
NS_RELEASE(mAttrStyleSheet);
}
if (nsnull != mInlineStyleSheet) {
mInlineStyleSheet->SetOwningDocument(nsnull);
NS_RELEASE(mInlineStyleSheet);
}
if (nsnull != mProlog) {
count = mProlog->Count();
for (i = 0; i < count; i++) {
content = (nsIContent*)mProlog->ElementAt(i);
NS_RELEASE(content);
}
delete mProlog;
}
if (nsnull != mEpilog) {
count = mEpilog->Count();
for (i = 0; i < count; i++) {
content = (nsIContent*)mEpilog->ElementAt(i);
NS_RELEASE(content);
}
delete mEpilog;
}
NS_IF_RELEASE(mChildNodes);
#ifdef INCLUDE_XUL
nsXULAtoms::ReleaseAtoms();
#endif
}
NS_IMETHODIMP
nsXMLDocument::QueryInterface(REFNSIID aIID,
void** aInstancePtr)
{
if (NULL == aInstancePtr) {
return NS_ERROR_NULL_POINTER;
}
if (aIID.Equals(kIXMLDocumentIID)) {
NS_ADDREF_THIS();
*aInstancePtr = (void**) (nsIXMLDocument *)this;
return NS_OK;
}
if (aIID.Equals(kIHTMLContentContainerIID)) {
NS_ADDREF_THIS();
*aInstancePtr = (void**) (nsIHTMLContentContainer *)this;
return NS_OK;
}
return nsDocument::QueryInterface(aIID, aInstancePtr);
}
nsrefcnt nsXMLDocument::AddRef()
{
return nsDocument::AddRef();
}
nsrefcnt nsXMLDocument::Release()
{
return nsDocument::Release();
}
nsresult
nsXMLDocument::Reset(nsIURL* aURL)
{
nsresult result = nsDocument::Reset(aURL);
if (NS_FAILED(result)) {
return result;
}
if (nsnull != mAttrStyleSheet) {
mAttrStyleSheet->SetOwningDocument(nsnull);
NS_RELEASE(mAttrStyleSheet);
}
if (nsnull != mInlineStyleSheet) {
mInlineStyleSheet->SetOwningDocument(nsnull);
NS_RELEASE(mInlineStyleSheet);
}
result = NS_NewHTMLStyleSheet(&mAttrStyleSheet, aURL, this);
if (NS_OK == result) {
AddStyleSheet(mAttrStyleSheet); // tell the world about our new style sheet
result = NS_NewHTMLCSSStyleSheet(&mInlineStyleSheet, aURL, this);
if (NS_OK == result) {
AddStyleSheet(mInlineStyleSheet); // tell the world about our new style sheet
}
}
return result;
}
NS_IMETHODIMP
nsXMLDocument::StartDocumentLoad(nsIURL *aUrl,
nsIContentViewerContainer* aContainer,
nsIStreamListener **aDocListener,
const char* aCommand)
{
nsresult rv = nsDocument::StartDocumentLoad(aUrl,
aContainer,
aDocListener,
aCommand);
if (NS_FAILED(rv)) {
return rv;
}
nsIWebShell* webShell;
static NS_DEFINE_IID(kCParserIID, NS_IPARSER_IID);
static NS_DEFINE_IID(kCParserCID, NS_PARSER_IID);
rv = nsRepository::CreateInstance(kCParserCID,
nsnull,
kCParserIID,
(void **)&mParser);
if (NS_OK == rv) {
nsIXMLContentSink* sink;
aContainer->QueryInterface(kIWebShellIID, (void**)&webShell);
rv = NS_NewXMLContentSink(&sink, this, aUrl, webShell);
NS_IF_RELEASE(webShell);
if (NS_OK == rv) {
// Set the parser as the stream listener for the document loader...
static NS_DEFINE_IID(kIStreamListenerIID, NS_ISTREAMLISTENER_IID);
rv = mParser->QueryInterface(kIStreamListenerIID, (void**)aDocListener);
if (NS_OK == rv) {
nsIDTD* theDTD=0;
// XXX For now, we'll use the HTML DTD
NS_NewWellFormed_DTD(&theDTD);
mParser->RegisterDTD(theDTD);
mParser->SetCommand(aCommand);
mParser->SetContentSink(sink);
mParser->Parse(aUrl);
}
NS_RELEASE(sink);
}
}
return rv;
}
NS_IMETHODIMP
nsXMLDocument::EndLoad()
{
NS_IF_RELEASE(mParser);
return nsDocument::EndLoad();
}
NS_IMETHODIMP
nsXMLDocument::GetAttributeStyleSheet(nsIHTMLStyleSheet** aResult)
{
NS_PRECONDITION(nsnull != aResult, "null ptr");
if (nsnull == aResult) {
return NS_ERROR_NULL_POINTER;
}
*aResult = mAttrStyleSheet;
if (nsnull == mAttrStyleSheet) {
return NS_ERROR_NOT_AVAILABLE; // probably not the right error...
}
else {
NS_ADDREF(mAttrStyleSheet);
}
return NS_OK;
}
NS_IMETHODIMP
nsXMLDocument::GetInlineStyleSheet(nsIHTMLCSSStyleSheet** aResult)
{
NS_PRECONDITION(nsnull != aResult, "null ptr");
if (nsnull == aResult) {
return NS_ERROR_NULL_POINTER;
}
*aResult = mInlineStyleSheet;
if (nsnull == mInlineStyleSheet) {
return NS_ERROR_NOT_AVAILABLE; // probably not the right error...
}
else {
NS_ADDREF(mInlineStyleSheet);
}
return NS_OK;
}
void nsXMLDocument::InternalAddStyleSheet(nsIStyleSheet* aSheet) // subclass hook for sheet ordering
{
if (aSheet == mAttrStyleSheet) { // always first
mStyleSheets.InsertElementAt(aSheet, 0);
}
else if (aSheet == mInlineStyleSheet) { // always last
mStyleSheets.AppendElement(aSheet);
}
else {
if (mInlineStyleSheet == mStyleSheets.ElementAt(mStyleSheets.Count() - 1)) {
// keep attr sheet last
mStyleSheets.InsertElementAt(aSheet, mStyleSheets.Count() - 1);
}
else {
mStyleSheets.AppendElement(aSheet);
}
}
}
// nsIDOMNode interface
NS_IMETHODIMP
nsXMLDocument::GetChildNodes(nsIDOMNodeList** aChildNodes)
{
if (nsnull == mChildNodes) {
mChildNodes = new nsXMLDocumentChildNodes(this);
if (nsnull == mChildNodes) {
return NS_ERROR_OUT_OF_MEMORY;
}
NS_ADDREF(mChildNodes);
}
return mChildNodes->QueryInterface(kIDOMNodeListIID, (void**)aChildNodes);
}
NS_IMETHODIMP
nsXMLDocument::GetFirstChild(nsIDOMNode** aFirstChild)
{
nsresult result = NS_OK;
if ((nsnull != mProlog) && (0 != mProlog->Count())) {
nsIContent* content;
result = PrologElementAt(0, &content);
if ((NS_OK == result) && (nsnull != content)) {
result = content->QueryInterface(kIDOMNodeIID, (void**)aFirstChild);
NS_RELEASE(content);
}
}
else {
nsIDOMElement* element;
result = GetDocumentElement(&element);
if (NS_OK == result) {
result = element->QueryInterface(kIDOMNodeIID, (void**)aFirstChild);
NS_RELEASE(element);
}
}
return result;
}
NS_IMETHODIMP
nsXMLDocument::GetLastChild(nsIDOMNode** aLastChild)
{
nsresult result = NS_OK;
if ((nsnull != mEpilog) && (0 != mEpilog->Count())) {
nsIContent* content;
result = EpilogElementAt(mEpilog->Count()-1, &content);
if ((NS_OK == result) && (nsnull != content)) {
result = content->QueryInterface(kIDOMNodeIID, (void**)aLastChild);
NS_RELEASE(content);
}
}
else {
nsIDOMElement* element;
result = GetDocumentElement(&element);
if (NS_OK == result) {
result = element->QueryInterface(kIDOMNodeIID, (void**)aLastChild);
NS_RELEASE(element);
}
}
return result;
}
NS_IMETHODIMP
nsXMLDocument::InsertBefore(nsIDOMNode* aNewChild,
nsIDOMNode* aRefChild,
nsIDOMNode** aReturn)
{
// XXX TBI
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsXMLDocument::ReplaceChild(nsIDOMNode* aNewChild,
nsIDOMNode* aOldChild,
nsIDOMNode** aReturn)
{
// XXX TBI
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsXMLDocument::RemoveChild(nsIDOMNode* aOldChild, nsIDOMNode** aReturn)
{
// XXX TBI
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsXMLDocument::AppendChild(nsIDOMNode* aNewChild, nsIDOMNode** aReturn)
{
// XXX TBI
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsXMLDocument::HasChildNodes(PRBool* aReturn)
{
*aReturn = PR_TRUE;
return NS_OK;
}
// nsIDOMDocument interface
NS_IMETHODIMP
nsXMLDocument::GetDoctype(nsIDOMDocumentType** aDocumentType)
{
// XXX TBI
*aDocumentType = nsnull;
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsXMLDocument::CreateCDATASection(const nsString& aData, nsIDOMCDATASection** aReturn)
{
// XXX TBI
*aReturn = nsnull;
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsXMLDocument::CreateEntityReference(const nsString& aName, nsIDOMEntityReference** aReturn)
{
// XXX TBI
*aReturn = nsnull;
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsXMLDocument::CreateProcessingInstruction(const nsString& aTarget, const nsString& aData, nsIDOMProcessingInstruction** aReturn)
{
// XXX TBI
*aReturn = nsnull;
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsXMLDocument::CreateElement(const nsString& aTagName,
nsIDOMElement** aReturn)
{
// XXX Should actually check parse namespace, determine
// current namespace scope and, potentially, create new
// HTML content form tags with a HTML prefix.
nsIXMLContent* content;
nsIAtom* tag = NS_NewAtom(aTagName);
nsresult rv = NS_NewXMLElement(&content, tag);
NS_RELEASE(tag);
if (NS_OK != rv) {
return rv;
}
rv = content->QueryInterface(kIDOMElementIID, (void**)aReturn);
return rv;
}
// nsIXMLDocument interface
NS_IMETHODIMP
nsXMLDocument::PrologElementAt(PRUint32 aIndex, nsIContent** aContent)
{
if (nsnull == mProlog) {
*aContent = nsnull;
}
else {
*aContent = (nsIContent *)mProlog->ElementAt((PRInt32)aIndex);
NS_ADDREF(*aContent);
}
return NS_OK;
}
NS_IMETHODIMP
nsXMLDocument::PrologCount(PRUint32* aCount)
{
if (nsnull == mProlog) {
*aCount = 0;
}
else {
*aCount = (PRUint32)mProlog->Count();
}
return NS_OK;
}
NS_IMETHODIMP
nsXMLDocument::AppendToProlog(nsIContent* aContent)
{
if (nsnull == mProlog) {
mProlog = new nsVoidArray();
}
mProlog->AppendElement((void *)aContent);
NS_ADDREF(aContent);
return NS_OK;
}
NS_IMETHODIMP
nsXMLDocument::EpilogElementAt(PRUint32 aIndex, nsIContent** aContent)
{
if (nsnull == mEpilog) {
*aContent = nsnull;
}
else {
*aContent = (nsIContent *)mEpilog->ElementAt((PRInt32)aIndex);
NS_ADDREF(*aContent);
}
return NS_OK;
}
NS_IMETHODIMP
nsXMLDocument::EpilogCount(PRUint32* aCount)
{
if (nsnull == mEpilog) {
*aCount = 0;
}
else {
*aCount = (PRUint32)mEpilog->Count();
}
return NS_OK;
}
NS_IMETHODIMP
nsXMLDocument::AppendToEpilog(nsIContent* aContent)
{
if (nsnull == mEpilog) {
mEpilog = new nsVoidArray();
}
mEpilog->AppendElement((void *)aContent);
NS_ADDREF(aContent);
return NS_OK;
}