1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/* ***** BEGIN LICENSE BLOCK *****
3
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
5
* The contents of this file are subject to the Mozilla Public License Version
6
* 1.1 (the "License"); you may not use this file except in compliance with
7
* the License. You may obtain a copy of the License at
8
* http://www.mozilla.org/MPL/
10
* Software distributed under the License is distributed on an "AS IS" basis,
11
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12
* for the specific language governing rights and limitations under the
15
* The Original Code is TransforMiiX XSLT processor code.
17
* The Initial Developer of the Original Code is
18
* The MITRE Corporation.
19
* Portions created by the Initial Developer are Copyright (C) 1999
20
* the Initial Developer. All Rights Reserved.
23
* Tom Kneeland <tomk@mitre.org> (Original Author)
25
* Alternatively, the contents of this file may be used under the terms of
26
* either the GNU General Public License Version 2 or later (the "GPL"), or
27
* 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 ***** */
39
#include "txXMLParser.h"
40
#include "txURIUtils.h"
41
#include "txXPathTreeWalker.h"
44
#include "nsIDocument.h"
45
#include "nsIDOMDocument.h"
46
#include "nsSyncLoadService.h"
47
#include "nsNetUtil.h"
48
#include "nsIPrincipal.h"
50
#include "expat_config.h"
52
#include "txXMLUtils.h"
57
* Implementation of an In-Memory DOM based XML parser. The actual XML
58
* parsing is provided by EXPAT.
63
nsresult parse(istream& aInputStream, const nsAString& aUri,
64
txXPathNode** aResultDoc);
65
const nsAString& getErrorString();
70
int StartElement(const XML_Char *aName, const XML_Char **aAtts);
71
int EndElement(const XML_Char* aName);
72
void CharacterData(const XML_Char* aChars, int aLength);
73
void Comment(const XML_Char* aChars);
74
int ProcessingInstruction(const XML_Char *aTarget, const XML_Char *aData);
75
int ExternalEntityRef(const XML_Char *aContext, const XML_Char *aBase,
76
const XML_Char *aSystemId,
77
const XML_Char *aPublicId);
80
void createErrorString();
81
nsString mErrorString;
84
XML_Parser mExpatParser;
89
txParseDocumentFromURI(const nsAString& aHref, const txXPathNode& aLoader,
90
nsAString& aErrMsg, txXPathNode** aResult)
92
NS_ENSURE_ARG_POINTER(aResult);
95
nsCOMPtr<nsIURI> documentURI;
96
nsresult rv = NS_NewURI(getter_AddRefs(documentURI), aHref);
97
NS_ENSURE_SUCCESS(rv, rv);
99
nsIDocument* loaderDocument = txXPathNativeNode::getDocument(aLoader);
101
nsCOMPtr<nsILoadGroup> loadGroup = loaderDocument->GetDocumentLoadGroup();
103
nsCOMPtr<nsIURI> loaderUri;
104
rv = loaderDocument->NodePrincipal()->GetURI(getter_AddRefs(loaderUri));
105
NS_ENSURE_SUCCESS(rv, rv);
107
// For the system principal loaderUri will be null here, which is good
108
// since that means that chrome documents can load any uri.
110
// Raw pointer, we want the resulting txXPathNode to hold a reference to
112
nsIDOMDocument* theDocument = nsnull;
113
rv = nsSyncLoadService::LoadDocument(documentURI, loaderUri, loadGroup,
114
PR_TRUE, &theDocument);
117
aErrMsg.Append(NS_LITERAL_STRING("Document load of ") +
118
aHref + NS_LITERAL_STRING(" failed."));
119
return NS_FAILED(rv) ? rv : NS_ERROR_FAILURE;
122
*aResult = txXPathNativeNode::createXPathNode(theDocument);
124
NS_RELEASE(theDocument);
125
return NS_ERROR_FAILURE;
130
istream* xslInput = URIUtils::getInputStream(aHref, aErrMsg);
132
return NS_ERROR_FAILURE;
134
return txParseFromStream(*xslInput, aHref, aErrMsg, aResult);
140
txParseFromStream(istream& aInputStream, const nsAString& aUri,
141
nsAString& aErrorString, txXPathNode** aResult)
143
NS_ENSURE_ARG_POINTER(aResult);
145
nsresult rv = parser.parse(aInputStream, aUri, aResult);
146
aErrorString = parser.getErrorString();
151
* expat C stub handlers
154
// shortcut macro for redirection into txXMLParser method calls
155
#define TX_XMLPARSER(_userData) static_cast<txXMLParser*>(_userData)
156
#define TX_ENSURE_DATA(_userData) \
159
NS_WARNING("no userData in comment handler"); \
164
PR_STATIC_CALLBACK(void)
165
startElement(void *aUserData, const XML_Char *aName, const XML_Char **aAtts)
167
TX_ENSURE_DATA(aUserData);
168
TX_XMLPARSER(aUserData)->StartElement(aName, aAtts);
171
PR_STATIC_CALLBACK(void)
172
endElement(void *aUserData, const XML_Char* aName)
174
TX_ENSURE_DATA(aUserData);
175
TX_XMLPARSER(aUserData)->EndElement(aName);
178
PR_STATIC_CALLBACK(void)
179
charData(void* aUserData, const XML_Char* aChars, int aLength)
181
TX_ENSURE_DATA(aUserData);
182
TX_XMLPARSER(aUserData)->CharacterData(aChars, aLength);
185
PR_STATIC_CALLBACK(void)
186
commentHandler(void* aUserData, const XML_Char* aChars)
188
TX_ENSURE_DATA(aUserData);
189
TX_XMLPARSER(aUserData)->Comment(aChars);
192
PR_STATIC_CALLBACK(void)
193
piHandler(void *aUserData, const XML_Char *aTarget, const XML_Char *aData)
195
TX_ENSURE_DATA(aUserData);
196
TX_XMLPARSER(aUserData)->ProcessingInstruction(aTarget, aData);
199
PR_STATIC_CALLBACK(int)
200
externalEntityRefHandler(XML_Parser aParser,
201
const XML_Char *aContext,
202
const XML_Char *aBase,
203
const XML_Char *aSystemId,
204
const XML_Char *aPublicId)
206
// aParser is aUserData is the txXMLParser,
207
// we set that in txXMLParser::parse
208
NS_ENSURE_TRUE(aParser, XML_ERROR_NONE);
209
return ((txXMLParser*)aParser)->ExternalEntityRef(aContext, aBase,
210
aSystemId, aPublicId);
215
* Parses the given input stream and returns a DOM Document.
216
* A NULL pointer will be returned if errors occurred
219
txXMLParser::parse(istream& aInputStream, const nsAString& aUri,
220
txXPathNode** aResultDoc)
222
mErrorString.Truncate();
223
*aResultDoc = nsnull;
225
mErrorString.AppendLiteral("unable to parse xml: invalid or unopen stream encountered.");
226
return NS_ERROR_FAILURE;
229
static const XML_Memory_Handling_Suite memsuite = {
230
(void *(*)(size_t))PR_Malloc,
231
(void *(*)(void *, size_t))PR_Realloc,
234
static const PRUnichar expatSeparator = kExpatSeparatorChar;
235
mExpatParser = XML_ParserCreate_MM(nsnull, &memsuite, &expatSeparator);
237
return NS_ERROR_OUT_OF_MEMORY;
239
mDocument = new Document();
241
XML_ParserFree(mExpatParser);
242
return NS_ERROR_OUT_OF_MEMORY;
244
mDocument->documentBaseURI = aUri;
245
mCurrentNode = mDocument;
247
XML_SetReturnNSTriplet(mExpatParser, XML_TRUE);
248
XML_SetUserData(mExpatParser, this);
249
XML_SetElementHandler(mExpatParser, startElement, endElement);
250
XML_SetCharacterDataHandler(mExpatParser, charData);
251
XML_SetProcessingInstructionHandler(mExpatParser, piHandler);
252
XML_SetCommentHandler(mExpatParser, commentHandler);
254
XML_SetParamEntityParsing(mExpatParser, XML_PARAM_ENTITY_PARSING_ALWAYS);
256
XML_SetExternalEntityRefHandler(mExpatParser, externalEntityRefHandler);
257
XML_SetExternalEntityRefHandlerArg(mExpatParser, this);
258
XML_SetBase(mExpatParser,
259
(const XML_Char*)(PromiseFlatString(aUri).get()));
261
const int bufferSize = 1024;
262
char buf[bufferSize];
265
aInputStream.read(buf, bufferSize);
266
done = aInputStream.eof();
268
if (!XML_Parse(mExpatParser, buf, aInputStream.gcount(), done)) {
275
aInputStream.clear();
278
XML_ParserFree(mExpatParser);
279
// ownership to the caller
280
*aResultDoc = txXPathNativeNode::createXPathNode(mDocument);
282
return *aResultDoc ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
286
txXMLParser::getErrorString()
293
txXMLParser::StartElement(const XML_Char *aName, const XML_Char **aAtts)
295
nsCOMPtr<nsIAtom> prefix, localName;
297
XMLUtils::splitExpatName(aName, getter_AddRefs(prefix),
298
getter_AddRefs(localName), &nsID);
299
Element* newElement = mDocument->createElementNS(prefix, localName, nsID);
301
return XML_ERROR_NO_MEMORY;
304
const XML_Char** theAtts = aAtts;
306
XMLUtils::splitExpatName(*theAtts++, getter_AddRefs(prefix),
307
getter_AddRefs(localName), &nsID);
308
nsDependentString attValue(*theAtts++);
309
nsresult rv = newElement->appendAttributeNS(prefix, localName, nsID,
312
return XML_ERROR_NO_MEMORY;
317
if ((idx = XML_GetIdAttributeIndex(mExpatParser)) > -1) {
318
nsDependentString idName((const PRUnichar*)*(aAtts + idx));
319
nsDependentString idValue((const PRUnichar*)*(aAtts + idx + 1));
320
// make sure IDs are unique
321
if (!idValue.IsEmpty()) {
322
mDocument->setElementID(idValue, newElement);
325
mCurrentNode->appendChild(newElement);
326
mCurrentNode = newElement;
328
return XML_ERROR_NONE;
332
txXMLParser::EndElement(const XML_Char* aName)
334
if (mCurrentNode->getParentNode()) {
335
mCurrentNode = mCurrentNode->getParentNode();
337
return XML_ERROR_NONE;
341
txXMLParser::CharacterData(const XML_Char* aChars, int aLength)
343
Node* prevSib = mCurrentNode->getLastChild();
344
const PRUnichar* pChars = static_cast<const PRUnichar*>(aChars);
345
if (prevSib && prevSib->getNodeType() == Node::TEXT_NODE) {
346
static_cast<NodeDefinition*>(prevSib)->appendData(pChars, aLength);
349
// aChars is not null-terminated so we use Substring here.
350
Node* node = mDocument->createTextNode(Substring(pChars,
352
mCurrentNode->appendChild(node);
357
txXMLParser::Comment(const XML_Char* aChars)
359
Node* node = mDocument->createComment(
360
nsDependentString(static_cast<const PRUnichar*>(aChars)));
361
mCurrentNode->appendChild(node);
365
txXMLParser::ProcessingInstruction(const XML_Char *aTarget,
366
const XML_Char *aData)
368
nsCOMPtr<nsIAtom> target = do_GetAtom(aTarget);
369
nsDependentString data((const PRUnichar*)aData);
370
Node* node = mDocument->createProcessingInstruction(target, data);
371
mCurrentNode->appendChild(node);
373
return XML_ERROR_NONE;
377
txXMLParser::ExternalEntityRef(const XML_Char *aContext,
378
const XML_Char *aBase,
379
const XML_Char *aSystemId,
380
const XML_Char *aPublicId)
383
// not supported, this is "http://some.site.net/foo.dtd" stuff
384
return XML_ERROR_EXTERNAL_ENTITY_HANDLING;
387
URIUtils::resolveHref(nsDependentString((PRUnichar*)aSystemId),
388
nsDependentString((PRUnichar*)aBase), absUrl);
389
istream* extInput = URIUtils::getInputStream(absUrl, mErrorString);
391
return XML_ERROR_EXTERNAL_ENTITY_HANDLING;
393
XML_Parser parent = mExpatParser;
395
XML_ExternalEntityParserCreate(mExpatParser, aContext, nsnull);
397
mExpatParser = parent;
399
return XML_ERROR_EXTERNAL_ENTITY_HANDLING;
401
XML_SetBase(mExpatParser, absUrl.get());
403
const int bufSize = 1024;
404
char buffer[bufSize];
408
extInput->read(buffer, bufSize);
409
done = extInput->eof();
411
XML_Parse(mExpatParser, buffer, extInput->gcount(), done))) {
413
mErrorString.Append(PRUnichar('\n'));
419
XML_ParserFree(mExpatParser);
421
mExpatParser = parent;
427
txXMLParser::createErrorString()
429
XML_Error errCode = XML_GetErrorCode(mExpatParser);
430
mErrorString.AppendWithConversion(XML_ErrorString(errCode));
431
mErrorString.AppendLiteral(" at line ");
432
mErrorString.AppendInt(XML_GetCurrentLineNumber(mExpatParser));
433
mErrorString.AppendLiteral(" in ");
434
mErrorString.Append((const PRUnichar*)XML_GetBase(mExpatParser));