From 4ab4b7060111055c836118f96c03fadf8603743a Mon Sep 17 00:00:00 2001 From: "axel%pike.org" Date: Wed, 5 Mar 2003 13:48:55 +0000 Subject: [PATCH] bug 190098, txXMLParser should load DTDs on standalone, pretty much a rewrite of how parsing to a DOM works on standalone, copying what nsExpatDriver does, optimize id patterns, too, r=sicking, sr=peterv --- .../transformiix/macbuild/transformiix.xml | 10 +- .../transformiix/source/lib/Makefile.in | 2 +- .../source/xml/dom/standalone/Document.cpp | 26 +- .../source/xml/dom/standalone/Element.cpp | 19 + .../source/xml/dom/standalone/dom.h | 35 +- .../source/xml/parser/Makefile.in | 4 +- .../source/xml/parser/XMLParser.cpp | 0 .../source/xml/parser/XMLParser.h | 0 .../source/xml/parser/txXMLParser.cpp | 413 ++++++++++++++++++ .../source/xml/parser/txXMLParser.h | 56 +++ .../source/xslt/ProcessorState.cpp | 18 +- .../source/xslt/txStandaloneXSLTProcessor.cpp | 12 +- .../source/xslt/txXSLTPatterns.cpp | 52 +-- .../transformiix/source/xslt/txXSLTPatterns.h | 4 +- 14 files changed, 590 insertions(+), 61 deletions(-) delete mode 100644 extensions/transformiix/source/xml/parser/XMLParser.cpp delete mode 100644 extensions/transformiix/source/xml/parser/XMLParser.h create mode 100644 extensions/transformiix/source/xml/parser/txXMLParser.cpp create mode 100644 extensions/transformiix/source/xml/parser/txXMLParser.h diff --git a/extensions/transformiix/macbuild/transformiix.xml b/extensions/transformiix/macbuild/transformiix.xml index da96637dbca..f32b6afb929 100644 --- a/extensions/transformiix/macbuild/transformiix.xml +++ b/extensions/transformiix/macbuild/transformiix.xml @@ -1316,7 +1316,7 @@ Name - XMLParser.cpp + txXMLParser.cpp MacOS Text @@ -1795,7 +1795,7 @@ Name - XMLParser.cpp + txXMLParser.cpp MacOS @@ -3180,7 +3180,7 @@ Name - XMLParser.cpp + txXMLParser.cpp MacOS Text @@ -3667,7 +3667,7 @@ Name - XMLParser.cpp + txXMLParser.cpp MacOS @@ -3945,7 +3945,7 @@ transformiixDebug.shlb Name - XMLParser.cpp + txXMLParser.cpp MacOS diff --git a/extensions/transformiix/source/lib/Makefile.in b/extensions/transformiix/source/lib/Makefile.in index 857918af2a7..fbe9c9c3667 100644 --- a/extensions/transformiix/source/lib/Makefile.in +++ b/extensions/transformiix/source/lib/Makefile.in @@ -81,7 +81,7 @@ OBJS = ../base/Double.$(OBJ_SUFFIX) \ ../xpath/VariableRefExpr.$(OBJ_SUFFIX) \ ../xml/XMLUtils.$(OBJ_SUFFIX) \ ../xml/XMLDOMUtils.$(OBJ_SUFFIX) \ - ../xml/parser/XMLParser.$(OBJ_SUFFIX) \ + ../xml/parser/txXMLParser.$(OBJ_SUFFIX) \ ../xslt/txOutputFormat.$(OBJ_SUFFIX) \ ../xslt/ProcessorState.$(OBJ_SUFFIX) \ ../xslt/txHTMLOutput.$(OBJ_SUFFIX) \ diff --git a/extensions/transformiix/source/xml/dom/standalone/Document.cpp b/extensions/transformiix/source/xml/dom/standalone/Document.cpp index 220fcfc0752..7ce5146ab29 100644 --- a/extensions/transformiix/source/xml/dom/standalone/Document.cpp +++ b/extensions/transformiix/source/xml/dom/standalone/Document.cpp @@ -39,6 +39,7 @@ // Document::Document() : NodeDefinition(Node::DOCUMENT_NODE, nsString(), NULL) { + mIDMap.Init(0); documentElement = nsnull; } @@ -118,15 +119,32 @@ ProcessingInstruction* // //Return an Element by ID, introduced by DOM2 // +DHASH_WRAPPER(txIDMap, txIDEntry, const nsAString&) + Element* Document::getElementById(const nsAString& aID) { - /* This would need knowledge of the DTD, and we don't have it. - * If we knew that we deal with HTML4 or XHTML1 we could check - * for the "id" attribute, but we don't, so return NULL - */ + txIDEntry* entry = mIDMap.GetEntry(aID); + if (entry) + return entry->mElement; return nsnull; } +/** + * private setter for element ID + */ +PRBool +Document::setElementID(const nsAString& aID, Element* aElement) +{ + txIDEntry* id = mIDMap.AddEntry(aID); + // make sure IDs are unique + if (id->mElement) { + return PR_FALSE; + } + id->mElement = aElement; + id->mElement->setIDValue(aID); + return PR_TRUE; +} + Node* Document::appendChild(Node* newChild) { unsigned short nodeType = newChild->getNodeType(); diff --git a/extensions/transformiix/source/xml/dom/standalone/Element.cpp b/extensions/transformiix/source/xml/dom/standalone/Element.cpp index 2075a803447..f70b6662cec 100644 --- a/extensions/transformiix/source/xml/dom/standalone/Element.cpp +++ b/extensions/transformiix/source/xml/dom/standalone/Element.cpp @@ -254,3 +254,22 @@ MBool Element::hasAttr(nsIAtom* aLocalName, PRInt32 aNSID) } return MB_FALSE; } + +/** + * ID accessors. Getter used for id() patterns, private setter for parser + */ +PRBool +Element::getIDValue(nsAString& aValue) +{ + if (mIDValue.IsEmpty()) { + return PR_FALSE; + } + aValue = mIDValue; + return PR_TRUE; +} + +void +Element::setIDValue(const nsAString& aValue) +{ + mIDValue = aValue; +} diff --git a/extensions/transformiix/source/xml/dom/standalone/dom.h b/extensions/transformiix/source/xml/dom/standalone/dom.h index 1e7457c26f9..3e826666160 100644 --- a/extensions/transformiix/source/xml/dom/standalone/dom.h +++ b/extensions/transformiix/source/xml/dom/standalone/dom.h @@ -41,6 +41,7 @@ #include "List.h" #include "nsIAtom.h" #include "baseutils.h" +#include "nsDoubleHashtable.h" #include "nsString.h" #include "nsVoidArray.h" @@ -359,6 +360,29 @@ class DocumentFragment : public NodeDefinition // //Definition and Implementation of a Document. // + +/** + * nsDoubleHashtable definitions for IDs + * + * It may be possible to share the key value with the element, + * but that may leave entries without keys, as the entries + * are constructed from the key value and the setting of mElement + * happens late. As pldhash.h ain't clear on this, we store the + * key by inheriting from PLDHashStringEntry. + */ +class txIDEntry : public PLDHashStringEntry +{ +public: + txIDEntry(const void* aKey) : PLDHashStringEntry(aKey), mElement(nsnull) + { + } + ~txIDEntry() + { + } + Element* mElement; +}; +DECL_DHASH_WRAPPER(txIDMap, txIDEntry, const nsAString&) + class Document : public NodeDefinition { public: @@ -393,10 +417,14 @@ class Document : public NodeDefinition void namespaceIDToURI(PRInt32 aNamespaceID, nsAString& aNamespaceURI); private: + PRBool setElementID(const nsAString& aID, Element* aElement); + Element* documentElement; // This class is friend to be able to set the documentBaseURI - friend class XMLParser; + // and IDs. + friend class txXMLParser; + txIDMap mIDMap; nsString documentBaseURI; }; @@ -424,13 +452,18 @@ class Element : public NodeDefinition MBool getAttr(nsIAtom* aLocalName, PRInt32 aNSID, nsAString& aValue); MBool hasAttr(nsIAtom* aLocalName, PRInt32 aNSID); + // ID getter + PRBool getIDValue(nsAString& aValue); + private: friend class Document; + void setIDValue(const nsAString& aValue); Element(const nsAString& tagName, Document* owner); Element(const nsAString& aNamespaceURI, const nsAString& aTagName, Document* aOwner); AttrMap mAttributes; + nsString mIDValue; nsCOMPtr mLocalName; PRInt32 mNamespaceID; }; diff --git a/extensions/transformiix/source/xml/parser/Makefile.in b/extensions/transformiix/source/xml/parser/Makefile.in index 95fd01aa37e..a8c9b0d529c 100644 --- a/extensions/transformiix/source/xml/parser/Makefile.in +++ b/extensions/transformiix/source/xml/parser/Makefile.in @@ -46,10 +46,12 @@ REQUIRES = string \ $(NULL) endif -CPPSRCS = XMLParser.cpp +CPPSRCS = txXMLParser.cpp include $(topsrcdir)/config/rules.mk +DEFINES += -DXML_DTD -DXML_UNICODE + INCLUDES += -I$(srcdir)/../../base -I$(srcdir)/../dom libs:: $(OBJS) diff --git a/extensions/transformiix/source/xml/parser/XMLParser.cpp b/extensions/transformiix/source/xml/parser/XMLParser.cpp deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/extensions/transformiix/source/xml/parser/XMLParser.h b/extensions/transformiix/source/xml/parser/XMLParser.h deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/extensions/transformiix/source/xml/parser/txXMLParser.cpp b/extensions/transformiix/source/xml/parser/txXMLParser.cpp new file mode 100644 index 00000000000..48017377fd4 --- /dev/null +++ b/extensions/transformiix/source/xml/parser/txXMLParser.cpp @@ -0,0 +1,413 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * The contents of this file are subject to the Mozilla 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/MPL/ + * + * 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 TransforMiiX XSLT processor. + * + * The Initial Developer of the Original Code is The MITRE Corporation. + * Portions created by MITRE are Copyright (C) 1999 The MITRE Corporation. + * + * Portions created by Keith Visco as a Non MITRE employee, + * (C) 1999 Keith Visco. All Rights Reserved. + * + * Contributor(s): + * Tom Kneeland, tomk@mitre.org + * -- original author. + */ + +#include "txXMLParser.h" +#include "txURIUtils.h" +#ifndef TX_EXE +#include "nsIDocument.h" +#include "nsIDOMDocument.h" +#include "nsISyncLoadDOMService.h" +#include "nsNetUtil.h" +#else +#include "xmlparse.h" +#endif + +#ifdef TX_EXE +/** + * Implementation of an In-Memory DOM based XML parser. The actual XML + * parsing is provided by EXPAT. + */ +class txXMLParser +{ + public: + nsresult parse(istream& aInputStream, const nsAString& aUri, + Document** aResultDoc); + const nsAString& getErrorString(); + + /** + * Expat handlers + */ + int StartElement(const XML_Char *aName, const XML_Char **aAtts); + int EndElement(const XML_Char* aName); + void CharacterData(const XML_Char* aChars, int aLength); + void Comment(const XML_Char* aChars); + int ProcessingInstruction(const XML_Char *aTarget, const XML_Char *aData); + int ExternalEntityRef(const XML_Char *aContext, const XML_Char *aBase, + const XML_Char *aSystemId, + const XML_Char *aPublicId); + + protected: + void createErrorString(); + nsString mErrorString; + Document* mDocument; + Node* mCurrentNode; + XML_Parser mExpatParser; +}; +#endif + +nsresult +txParseDocumentFromURI(const nsAString& aHref, const nsAString& aReferrer, + Document* aLoader, nsAString& aErrMsg, + Document** aResult) +{ + NS_ENSURE_ARG_POINTER(aResult); + *aResult = nsnull; +#ifndef TX_EXE + nsCOMPtr documentURI; + nsresult rv = NS_NewURI(getter_AddRefs(documentURI), aHref); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr theDocument; + nsCOMPtr loaderDocument = + do_QueryInterface(aLoader->getNSObj()); + nsCOMPtr loadGroup; + nsCOMPtr loaderUri; + loaderDocument->GetDocumentLoadGroup(getter_AddRefs(loadGroup)); + loaderDocument->GetDocumentURL(getter_AddRefs(loaderUri)); + NS_ENSURE_TRUE(loaderUri, NS_ERROR_FAILURE); + + nsCOMPtr channel; + rv = NS_NewChannel(getter_AddRefs(channel), documentURI, nsnull, + loadGroup); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr http = do_QueryInterface(channel); + if (http) { + nsCOMPtr refUri; + NS_NewURI(getter_AddRefs(refUri), aReferrer); + if (refUri) { + http->SetReferrer(refUri); + } + http->SetRequestHeader(NS_LITERAL_CSTRING("Accept"), + NS_LITERAL_CSTRING("text/xml,application/xml,application/xhtml+xml,*/*;q=0.1"), + PR_FALSE); + + + } + nsCOMPtr loader = + do_GetService("@mozilla.org/content/syncload-dom-service;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + rv = loader->LoadDocument(channel, loaderUri, getter_AddRefs(theDocument)); + if (NS_FAILED(rv) || !theDocument) { + aErrMsg.Append(NS_LITERAL_STRING("Document load of ") + + aHref + NS_LITERAL_STRING(" failed.")); + return rv; + } + + *aResult = new Document(theDocument); + if (!*aResult) { + return NS_ERROR_FAILURE; + } + return NS_OK; +#else + istream* xslInput = URIUtils::getInputStream(aHref, aErrMsg); + if (!xslInput) { + return NS_ERROR_FAILURE; + } + return txParseFromStream(*xslInput, aHref, aErrMsg, aResult); +#endif +} + +#ifdef TX_EXE +nsresult +txParseFromStream(istream& aInputStream, const nsAString& aUri, + nsAString& aErrorString, Document** aResult) +{ + NS_ENSURE_ARG_POINTER(aResult); + txXMLParser parser; + nsresult rv = parser.parse(aInputStream, aUri, aResult); + aErrorString = parser.getErrorString(); + return rv; +} + +/** + * expat C stub handlers + */ + +// shortcut macro for redirection into txXMLParser method calls +#define TX_XMLPARSER(_userData) NS_STATIC_CAST(txXMLParser*, _userData) + +PR_STATIC_CALLBACK(int) +startElement(void *aUserData, const XML_Char *aName, const XML_Char **aAtts) +{ + NS_ENSURE_TRUE(aUserData, XML_ERROR_NONE); + return TX_XMLPARSER(aUserData)->StartElement(aName, aAtts); +} + +PR_STATIC_CALLBACK(int) +endElement(void *aUserData, const XML_Char* aName) +{ + NS_ENSURE_TRUE(aUserData, XML_ERROR_NONE); + return TX_XMLPARSER(aUserData)->EndElement(aName); +} + +PR_STATIC_CALLBACK(void) +charData(void* aUserData, const XML_Char* aChars, int aLength) +{ + if (!aUserData) { + NS_WARNING("no userData in charData handler"); + return; + } + TX_XMLPARSER(aUserData)->CharacterData(aChars, aLength); +} + +PR_STATIC_CALLBACK(void) +commentHandler(void* aUserData, const XML_Char* aChars) +{ + if (!aUserData) { + NS_WARNING("no userData in comment handler"); + return; + } + TX_XMLPARSER(aUserData)->Comment(aChars); +} + +PR_STATIC_CALLBACK(int) +piHandler(void *aUserData, const XML_Char *aTarget, const XML_Char *aData) +{ + NS_ENSURE_TRUE(aUserData, XML_ERROR_NONE); + return TX_XMLPARSER(aUserData)->ProcessingInstruction(aTarget, aData); +} + +PR_STATIC_CALLBACK(int) +externalEntityRefHandler(XML_Parser aParser, + const XML_Char *aContext, + const XML_Char *aBase, + const XML_Char *aSystemId, + const XML_Char *aPublicId) +{ + // aParser is aUserData is the txXMLParser, + // we set that in txXMLParser::parse + NS_ENSURE_TRUE(aParser, XML_ERROR_NONE); + return TX_XMLPARSER(aParser)->ExternalEntityRef(aContext, aBase, + aSystemId, aPublicId); +} + + +/** + * Parses the given input stream and returns a DOM Document. + * A NULL pointer will be returned if errors occurred + */ +nsresult +txXMLParser::parse(istream& aInputStream, const nsAString& aUri, + Document** aResultDoc) +{ + mErrorString.Truncate(); + *aResultDoc = nsnull; + if (!aInputStream) { + mErrorString.Append(NS_LITERAL_STRING("unable to parse xml: invalid or unopen stream encountered.")); + return NS_ERROR_FAILURE; + } + mExpatParser = XML_ParserCreate(nsnull); + if (!mExpatParser) { + return NS_ERROR_OUT_OF_MEMORY; + } + mDocument = new Document(); + if (!mDocument) { + XML_ParserFree(mExpatParser); + return NS_ERROR_OUT_OF_MEMORY; + } + mDocument->documentBaseURI = aUri; + mCurrentNode = mDocument; + + XML_SetUserData(mExpatParser, this); + XML_SetElementHandler(mExpatParser, startElement, endElement); + XML_SetCharacterDataHandler(mExpatParser, charData); + XML_SetProcessingInstructionHandler(mExpatParser, piHandler); + XML_SetCommentHandler(mExpatParser, commentHandler); +#ifdef XML_DTD + XML_SetParamEntityParsing(mExpatParser, XML_PARAM_ENTITY_PARSING_ALWAYS); +#endif + XML_SetExternalEntityRefHandler(mExpatParser, externalEntityRefHandler); + XML_SetExternalEntityRefHandlerArg(mExpatParser, this); + XML_SetBase(mExpatParser, + (const XML_Char*)(PromiseFlatString(aUri).get())); + + const int bufferSize = 1024; + char buf[bufferSize]; + PRBool done; + do { + aInputStream.read(buf, bufferSize); + done = aInputStream.eof(); + + if (!XML_Parse(mExpatParser, buf, aInputStream.gcount(), done)) { + createErrorString(); + done = MB_TRUE; + delete mDocument; + mDocument = nsnull; + } + } while (!done); + aInputStream.clear(); + + // clean up + XML_ParserFree(mExpatParser); + // ownership to the caller + *aResultDoc = mDocument; + mDocument = nsnull; + return NS_OK; +} + +const nsAString& +txXMLParser::getErrorString() +{ + return mErrorString; +} + + +int +txXMLParser::StartElement(const XML_Char *aName, const XML_Char **aAtts) +{ + const XML_Char** theAtts = aAtts; + + Element* newElement = + mDocument->createElement(nsDependentString((const PRUnichar*)aName)); + if (!newElement) { + return XML_ERROR_NO_MEMORY; + } + + while (*theAtts) { + nsDependentString attName((const PRUnichar*)*theAtts++); + nsDependentString attValue((const PRUnichar*)*theAtts++); + newElement->setAttribute(attName, attValue); + } + + int idx; + if ((idx = XML_GetIdAttributeIndex(mExpatParser)) > -1) { + nsDependentString idName((const PRUnichar*)*(aAtts + idx)); + nsDependentString idValue((const PRUnichar*)*(aAtts + idx + 1)); + // make sure IDs are unique + if (!idValue.IsEmpty()) { + mDocument->setElementID(idValue, newElement); + } + } + mCurrentNode->appendChild(newElement); + mCurrentNode = newElement; + + return XML_ERROR_NONE; +} + +int +txXMLParser::EndElement(const XML_Char* aName) +{ + if (mCurrentNode->getParentNode()) { + mCurrentNode = mCurrentNode->getParentNode(); + } + return XML_ERROR_NONE; +} + +void +txXMLParser::CharacterData(const XML_Char* aChars, int aLength) +{ + Node* prevSib = mCurrentNode->getLastChild(); + const PRUnichar* pChars = NS_STATIC_CAST(const PRUnichar*, aChars); + if (prevSib && prevSib->getNodeType() == Node::TEXT_NODE) { + NS_STATIC_CAST(NodeDefinition*, prevSib)->appendData(pChars, aLength); + } + else { + // aChars is not null-terminated so we use Substring here. + Node* node = mDocument->createTextNode(Substring(pChars, + pChars + aLength)); + mCurrentNode->appendChild(node); + } +} + +void +txXMLParser::Comment(const XML_Char* aChars) +{ + Node* node = mDocument->createComment( + nsDependentString(NS_STATIC_CAST(const PRUnichar*, aChars))); + mCurrentNode->appendChild(node); +} + +int +txXMLParser::ProcessingInstruction(const XML_Char *aTarget, + const XML_Char *aData) +{ + nsDependentString target((const PRUnichar*)aTarget); + nsDependentString data((const PRUnichar*)aData); + Node* node = mDocument->createProcessingInstruction(target, data); + mCurrentNode->appendChild(node); + + return XML_ERROR_NONE; +} + +int +txXMLParser::ExternalEntityRef(const XML_Char *aContext, + const XML_Char *aBase, + const XML_Char *aSystemId, + const XML_Char *aPublicId) +{ + if (aPublicId) { + // not supported, this is "http://some.site.net/foo.dtd" stuff + return XML_ERROR_EXTERNAL_ENTITY_HANDLING; + } + nsAutoString absUrl; + URIUtils::resolveHref(nsDependentString((PRUnichar*)aSystemId), + nsDependentString((PRUnichar*)aBase), absUrl); + istream* extInput = URIUtils::getInputStream(absUrl, mErrorString); + if (!extInput) { + return XML_ERROR_EXTERNAL_ENTITY_HANDLING; + } + XML_Parser parent = mExpatParser; + mExpatParser = + XML_ExternalEntityParserCreate(mExpatParser, aContext, nsnull); + if (!mExpatParser) { + mExpatParser = parent; + delete extInput; + return XML_ERROR_EXTERNAL_ENTITY_HANDLING; + } + XML_SetBase(mExpatParser, absUrl.get()); + + const int bufSize = 1024; + char buffer[bufSize]; + int result; + PRBool done; + do { + extInput->read(buffer, bufSize); + done = extInput->eof(); + if (!(result = + XML_Parse(mExpatParser, buffer, extInput->gcount(), done))) { + createErrorString(); + mErrorString.Append(PRUnichar('\n')); + done = MB_TRUE; + } + } while (!done); + + delete extInput; + XML_ParserFree(mExpatParser); + + mExpatParser = parent; + + return result; +} + +void +txXMLParser::createErrorString() +{ + XML_Error errCode = XML_GetErrorCode(mExpatParser); + mErrorString.AppendWithConversion(XML_ErrorString(errCode)); + mErrorString.Append(NS_LITERAL_STRING(" at line ")); + mErrorString.AppendInt(XML_GetCurrentLineNumber(mExpatParser)); + mErrorString.Append(NS_LITERAL_STRING(" in ")); + mErrorString.Append((const PRUnichar*)XML_GetBase(mExpatParser)); +} +#endif diff --git a/extensions/transformiix/source/xml/parser/txXMLParser.h b/extensions/transformiix/source/xml/parser/txXMLParser.h new file mode 100644 index 00000000000..76c15be3327 --- /dev/null +++ b/extensions/transformiix/source/xml/parser/txXMLParser.h @@ -0,0 +1,56 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * The contents of this file are subject to the Mozilla 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/MPL/ + * + * 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 TransforMiiX XSLT processor. + * + * The Initial Developer of the Original Code is The MITRE Corporation. + * Portions created by MITRE are Copyright (C) 1999 The MITRE Corporation. + * + * Portions created by Keith Visco as a Non MITRE employee, + * (C) 1999 Keith Visco. All Rights Reserved. + * + * Contributor(s): + * Tom Kneeland + * -- original author. + */ + +#ifndef MITRE_XMLPARSER_H +#define MITRE_XMLPARSER_H + +#include "dom.h" + +#ifdef TX_EXE +#include +#endif + +/** + * API to load XML files into DOM datastructures. + * Parsing is either done by expat, or by expat via the synchloaderservice + */ + +/** + * Parse a document from the aHref location, with referrer URI on behalf + * of the document aLoader. + */ +extern "C" nsresult +txParseDocumentFromURI(const nsAString& aHref, const nsAString& aReferrer, + Document* aLoader, nsAString& aErrMsg, + Document** aResult); + +#ifdef TX_EXE +/** + * Parse a document from the given stream + */ +extern "C" nsresult +txParseFromStream(istream& aInputStream, const nsAString& aUri, + nsAString& aErrorString, Document** aResult); +#endif +#endif diff --git a/extensions/transformiix/source/xslt/ProcessorState.cpp b/extensions/transformiix/source/xslt/ProcessorState.cpp index bb94a5a15e4..61119ae2f17 100644 --- a/extensions/transformiix/source/xslt/ProcessorState.cpp +++ b/extensions/transformiix/source/xslt/ProcessorState.cpp @@ -39,7 +39,7 @@ #include "XMLUtils.h" #include "XMLDOMUtils.h" #include "ExprResult.h" -#include "XMLParser.h" +#include "txXMLParser.h" #include "TxLog.h" #include "txAtoms.h" #include "txSingleNodeContext.h" @@ -445,14 +445,16 @@ Node* ProcessorState::retrieveDocument(const nsAString& uri, if (!xmlDoc) { // open URI - nsAutoString errMsg; - XMLParser xmlParser; + nsAutoString errMsg, refUri; + // XXX we should get the referrer from the actual node + // triggering the load, but this will do for the time being + mLoadedDocuments.mStyleDocument->getBaseURI(refUri); + nsresult rv; + rv = txParseDocumentFromURI(docUrl, refUri, + mLoadedDocuments.mStyleDocument, errMsg, + &xmlDoc); - xmlDoc = xmlParser.getDocumentFromURI(docUrl, - mLoadedDocuments.mStyleDocument, - errMsg); - - if (!xmlDoc) { + if (NS_FAILED(rv) || !xmlDoc) { receiveError(NS_LITERAL_STRING("Couldn't load document '") + docUrl + NS_LITERAL_STRING("': ") + errMsg, NS_ERROR_XSLT_INVALID_URL); diff --git a/extensions/transformiix/source/xslt/txStandaloneXSLTProcessor.cpp b/extensions/transformiix/source/xslt/txStandaloneXSLTProcessor.cpp index 0517566a320..b28081f3540 100644 --- a/extensions/transformiix/source/xslt/txStandaloneXSLTProcessor.cpp +++ b/extensions/transformiix/source/xslt/txStandaloneXSLTProcessor.cpp @@ -46,7 +46,7 @@ #include "txTextOutput.h" #include "txUnknownHandler.h" #include "txURIUtils.h" -#include "XMLParser.h" +#include "txXMLParser.h" /** * Output Handler Factory @@ -378,11 +378,13 @@ txStandaloneXSLTProcessor::parsePath(const nsACString& aPath, ErrorObserver& aEr return 0; } // parse source - XMLParser xmlParser; - Document* xmlDoc = xmlParser.parse(xmlInput, path); + Document* xmlDoc; + nsAutoString errors; + nsresult rv = txParseFromStream(xmlInput, path, errors, &xmlDoc); xmlInput.close(); - if (!xmlDoc) { - aErr.receiveError(NS_LITERAL_STRING("Parsing error in ") + path); + if (NS_FAILED(rv) || !xmlDoc) { + aErr.receiveError(NS_LITERAL_STRING("Parsing error \"") + errors + + NS_LITERAL_STRING("\"")); } return xmlDoc; } diff --git a/extensions/transformiix/source/xslt/txXSLTPatterns.cpp b/extensions/transformiix/source/xslt/txXSLTPatterns.cpp index 0d1d58dbca8..cb61860efda 100644 --- a/extensions/transformiix/source/xslt/txXSLTPatterns.cpp +++ b/extensions/transformiix/source/xslt/txXSLTPatterns.cpp @@ -36,16 +36,16 @@ * * ***** END LICENSE BLOCK ***** */ +#include "nsReadableUtils.h" +#include "ProcessorState.h" #include "txXSLTPatterns.h" #include "txNodeSetContext.h" #include "txForwardContext.h" +#include "XMLUtils.h" #include "XSLTFunctions.h" -#include "ProcessorState.h" #ifndef TX_EXE -#include "nsReadableUtils.h" #include "nsIContent.h" #include "nsINodeInfo.h" -#include "XMLUtils.h" #endif /* @@ -320,9 +320,6 @@ void txRootPattern::toString(nsAString& aDest) */ txIdPattern::txIdPattern(const nsAString& aString) { -#ifdef TX_EXE - mIds = aString; -#else nsAString::const_iterator pos, begin, end; aString.BeginReading(begin); aString.EndReading(end); @@ -331,13 +328,11 @@ txIdPattern::txIdPattern(const nsAString& aString) while (pos != end && XMLUtils::isWhitespace(*pos)) ++pos; begin = pos; - if (!mIds.IsEmpty()) - mIds += PRUnichar(' '); while (pos != end && !XMLUtils::isWhitespace(*pos)) ++pos; - mIds += Substring(begin, pos); + // this can fail, XXX move to a Init(aString) method + mIds.AppendString(Substring(begin, pos)); } -#endif } txIdPattern::~txIdPattern() @@ -346,15 +341,16 @@ txIdPattern::~txIdPattern() MBool txIdPattern::matches(Node* aNode, txIMatchContext* aContext) { -#ifdef TX_EXE - return MB_FALSE; // not implemented -#else if (aNode->getNodeType() != Node::ELEMENT_NODE) { return MB_FALSE; } // Get a ID attribute, if there is - + nsAutoString value; +#ifdef TX_EXE + if (!((Element*)aNode)->getIDValue(value)) + return MB_FALSE; +#else nsCOMPtr content = do_QueryInterface(aNode->getNSObj()); NS_ASSERTION(content, "a Element without nsIContent"); if (!content) { @@ -370,31 +366,12 @@ MBool txIdPattern::matches(Node* aNode, txIMatchContext* aContext) if (!idAttr) { return MB_FALSE; // no ID for this element defined, can't match } - nsAutoString value; nsresult rv = content->GetAttr(kNameSpaceID_None, idAttr, value); if (rv != NS_CONTENT_ATTR_HAS_VALUE) { return MB_FALSE; // no ID attribute given } - nsAString::const_iterator pos, begin, end; - mIds.BeginReading(begin); - mIds.EndReading(end); - pos = begin; - const PRUnichar space = PRUnichar(' '); - PRBool found = FindCharInReadable(space, pos, end); - - while (found) { - if (value.Equals(Substring(begin, pos))) { - return MB_TRUE; - } - ++pos; // skip ' ' - begin = pos; - found = FindCharInReadable(PRUnichar(' '), pos, end); - } - if (value.Equals(Substring(begin, pos))) { - return MB_TRUE; - } - return MB_FALSE; #endif // TX_EXE + return mIds.IndexOf(value) > -1; } double txIdPattern::getDefaultPriority() @@ -408,7 +385,12 @@ void txIdPattern::toString(nsAString& aDest) aDest.Append(NS_LITERAL_STRING("txIdPattern{")); #endif aDest.Append(NS_LITERAL_STRING("id('")); - aDest.Append(mIds); + PRUint32 k, count = mIds.Count() - 1; + for (k = 0; k < count; ++k) { + aDest.Append(*mIds[k]); + aDest.Append(PRUnichar(' ')); + } + aDest.Append(*mIds[count]); aDest.Append(NS_LITERAL_STRING("')")); #ifdef DEBUG aDest.Append(PRUnichar('}')); diff --git a/extensions/transformiix/source/xslt/txXSLTPatterns.h b/extensions/transformiix/source/xslt/txXSLTPatterns.h index 713f4598306..69fd2c8363f 100644 --- a/extensions/transformiix/source/xslt/txXSLTPatterns.h +++ b/extensions/transformiix/source/xslt/txXSLTPatterns.h @@ -41,6 +41,8 @@ #include "Expr.h" #include "XMLUtils.h" +#include "nsVoidArray.h" + class ProcessorState; class txPattern : public TxObject @@ -166,7 +168,7 @@ public: TX_DECL_PATTERN; private: - nsString mIds; + nsStringArray mIds; }; class txKeyPattern : public txPattern