1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
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 mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
23 * Henri Sivonen <hsivonen@iki.fi>
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 ***** */
39 #include "nsExpatDriver.h"
40 #include "nsIParser.h"
42 #include "nsParserCIID.h"
43 #include "CParserContext.h"
44 #include "nsIExpatSink.h"
45 #include "nsIExtendedExpatSink.h"
46 #include "nsIContentSink.h"
47 #include "nsParserMsgUtils.h"
49 #include "nsIUnicharInputStream.h"
50 #include "nsISimpleUnicharStreamFactory.h"
51 #include "nsNetUtil.h"
54 #include "nsTextFormatter.h"
55 #include "nsDirectoryServiceDefs.h"
57 #include "nsIConsoleService.h"
58 #include "nsIScriptError.h"
59 #include "nsIContentPolicy.h"
60 #include "nsContentPolicyUtils.h"
61 #include "nsContentErrors.h"
62 #include "nsXPCOMCIDInternal.h"
63 #include "nsUnicharInputStream.h"
65 #define kExpatSeparatorChar 0xFFFF
67 static const PRUnichar kUTF16
[] = { 'U', 'T', 'F', '-', '1', '6', '\0' };
70 static PRLogModuleInfo
*gExpatDriverLog
= PR_NewLogModule("expatdriver");
73 /***************************** EXPAT CALL BACKS ******************************/
74 // The callback handlers that get called from the expat parser.
77 Driver_HandleXMLDeclaration(void *aUserData
,
78 const XML_Char
*aVersion
,
79 const XML_Char
*aEncoding
,
82 NS_ASSERTION(aUserData
, "expat driver should exist");
84 nsExpatDriver
* driver
= static_cast<nsExpatDriver
*>(aUserData
);
85 driver
->HandleXMLDeclaration(aVersion
, aEncoding
, aStandalone
);
90 Driver_HandleStartElement(void *aUserData
,
91 const XML_Char
*aName
,
92 const XML_Char
**aAtts
)
94 NS_ASSERTION(aUserData
, "expat driver should exist");
96 static_cast<nsExpatDriver
*>(aUserData
)->HandleStartElement(aName
,
102 Driver_HandleEndElement(void *aUserData
,
103 const XML_Char
*aName
)
105 NS_ASSERTION(aUserData
, "expat driver should exist");
107 static_cast<nsExpatDriver
*>(aUserData
)->HandleEndElement(aName
);
112 Driver_HandleCharacterData(void *aUserData
,
113 const XML_Char
*aData
,
116 NS_ASSERTION(aUserData
, "expat driver should exist");
118 nsExpatDriver
* driver
= static_cast<nsExpatDriver
*>(aUserData
);
119 driver
->HandleCharacterData(aData
, PRUint32(aLength
));
124 Driver_HandleComment(void *aUserData
,
125 const XML_Char
*aName
)
127 NS_ASSERTION(aUserData
, "expat driver should exist");
129 static_cast<nsExpatDriver
*>(aUserData
)->HandleComment(aName
);
134 Driver_HandleProcessingInstruction(void *aUserData
,
135 const XML_Char
*aTarget
,
136 const XML_Char
*aData
)
138 NS_ASSERTION(aUserData
, "expat driver should exist");
140 nsExpatDriver
* driver
= static_cast<nsExpatDriver
*>(aUserData
);
141 driver
->HandleProcessingInstruction(aTarget
, aData
);
146 Driver_HandleDefault(void *aUserData
,
147 const XML_Char
*aData
,
150 NS_ASSERTION(aUserData
, "expat driver should exist");
152 nsExpatDriver
* driver
= static_cast<nsExpatDriver
*>(aUserData
);
153 driver
->HandleDefault(aData
, PRUint32(aLength
));
158 Driver_HandleStartCdataSection(void *aUserData
)
160 NS_ASSERTION(aUserData
, "expat driver should exist");
162 static_cast<nsExpatDriver
*>(aUserData
)->HandleStartCdataSection();
167 Driver_HandleEndCdataSection(void *aUserData
)
169 NS_ASSERTION(aUserData
, "expat driver should exist");
171 static_cast<nsExpatDriver
*>(aUserData
)->HandleEndCdataSection();
176 Driver_HandleStartDoctypeDecl(void *aUserData
,
177 const XML_Char
*aDoctypeName
,
178 const XML_Char
*aSysid
,
179 const XML_Char
*aPubid
,
180 int aHasInternalSubset
)
182 NS_ASSERTION(aUserData
, "expat driver should exist");
184 static_cast<nsExpatDriver
*>(aUserData
)->
185 HandleStartDoctypeDecl(aDoctypeName
, aSysid
, aPubid
, !!aHasInternalSubset
);
190 Driver_HandleEndDoctypeDecl(void *aUserData
)
192 NS_ASSERTION(aUserData
, "expat driver should exist");
194 static_cast<nsExpatDriver
*>(aUserData
)->HandleEndDoctypeDecl();
199 Driver_HandleExternalEntityRef(void *aExternalEntityRefHandler
,
200 const XML_Char
*aOpenEntityNames
,
201 const XML_Char
*aBase
,
202 const XML_Char
*aSystemId
,
203 const XML_Char
*aPublicId
)
205 NS_ASSERTION(aExternalEntityRefHandler
, "expat driver should exist");
206 if (!aExternalEntityRefHandler
) {
210 nsExpatDriver
* driver
= static_cast<nsExpatDriver
*>
211 (aExternalEntityRefHandler
);
213 return driver
->HandleExternalEntityRef(aOpenEntityNames
, aBase
, aSystemId
,
218 Driver_HandleStartNamespaceDecl(void *aUserData
,
219 const XML_Char
*aPrefix
,
220 const XML_Char
*aUri
)
222 NS_ASSERTION(aUserData
, "expat driver should exist");
224 static_cast<nsExpatDriver
*>(aUserData
)->
225 HandleStartNamespaceDecl(aPrefix
, aUri
);
230 Driver_HandleEndNamespaceDecl(void *aUserData
,
231 const XML_Char
*aPrefix
)
233 NS_ASSERTION(aUserData
, "expat driver should exist");
235 static_cast<nsExpatDriver
*>(aUserData
)->
236 HandleEndNamespaceDecl(aPrefix
);
241 Driver_HandleNotationDecl(void *aUserData
,
242 const XML_Char
*aNotationName
,
243 const XML_Char
*aBase
,
244 const XML_Char
*aSysid
,
245 const XML_Char
*aPubid
)
247 NS_ASSERTION(aUserData
, "expat driver should exist");
249 static_cast<nsExpatDriver
*>(aUserData
)->
250 HandleNotationDecl(aNotationName
, aBase
, aSysid
, aPubid
);
255 Driver_HandleUnparsedEntityDecl(void *aUserData
,
256 const XML_Char
*aEntityName
,
257 const XML_Char
*aBase
,
258 const XML_Char
*aSysid
,
259 const XML_Char
*aPubid
,
260 const XML_Char
*aNotationName
)
262 NS_ASSERTION(aUserData
, "expat driver should exist");
264 static_cast<nsExpatDriver
*>(aUserData
)->
265 HandleUnparsedEntityDecl(aEntityName
, aBase
, aSysid
, aPubid
,
271 /***************************** END CALL BACKS ********************************/
273 /***************************** CATALOG UTILS *********************************/
275 // Initially added for bug 113400 to switch from the remote "XHTML 1.0 plus
276 // MathML 2.0" DTD to the the lightweight customized version that Mozilla uses.
277 // Since Mozilla is not validating, no need to fetch a *huge* file at each
279 // XXX The cleanest solution here would be to fix Bug 98413: Implement XML
281 struct nsCatalogData
{
282 const char* mPublicID
;
283 const char* mLocalDTD
;
284 const char* mAgentSheet
;
287 // The order of this table is guestimated to be in the optimum order
288 static const nsCatalogData kCatalogTable
[] = {
289 { "-//W3C//DTD XHTML 1.0 Transitional//EN", "xhtml11.dtd", nsnull
},
290 { "-//W3C//DTD XHTML 1.1//EN", "xhtml11.dtd", nsnull
},
291 { "-//W3C//DTD XHTML 1.0 Strict//EN", "xhtml11.dtd", nsnull
},
292 { "-//W3C//DTD XHTML 1.0 Frameset//EN", "xhtml11.dtd", nsnull
},
293 { "-//W3C//DTD XHTML Basic 1.0//EN", "xhtml11.dtd", nsnull
},
294 { "-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN", "mathml.dtd", "resource://gre/res/mathml.css" },
295 { "-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN", "mathml.dtd", "resource://gre/res/mathml.css" },
296 { "-//W3C//DTD MathML 2.0//EN", "mathml.dtd", "resource://gre/res/mathml.css" },
297 { "-//WAPFORUM//DTD XHTML Mobile 1.0//EN", "xhtml11.dtd", nsnull
},
298 { nsnull
, nsnull
, nsnull
}
301 static const nsCatalogData
*
302 LookupCatalogData(const PRUnichar
* aPublicID
)
304 nsDependentString
publicID(aPublicID
);
306 // linear search for now since the number of entries is going to
307 // be negligible, and the fix for bug 98413 would get rid of this
309 const nsCatalogData
* data
= kCatalogTable
;
310 while (data
->mPublicID
) {
311 if (publicID
.EqualsASCII(data
->mPublicID
)) {
320 // aCatalogData can be null. If not null, it provides a hook to additional
321 // built-in knowledge on the resource that we are trying to load. Returns true
322 // if the local DTD specified in the catalog data exists or if the filename
323 // contained within the url exists in the special DTD directory. If either of
324 // this exists, aResult is set to the file: url that points to the DTD file
325 // found in the local DTD directory.
327 IsLoadableDTD(const nsCatalogData
* aCatalogData
, nsIURI
* aDTD
,
330 NS_ASSERTION(aDTD
, "Null parameter.");
332 nsCAutoString fileName
;
334 // remap the DTD to a known local DTD
335 fileName
.Assign(aCatalogData
->mLocalDTD
);
338 if (fileName
.IsEmpty()) {
339 // Try to see if the user has installed the DTD file -- we extract the
340 // filename.ext of the DTD here. Hence, for any DTD for which we have
341 // no predefined mapping, users just have to copy the DTD file to our
342 // special DTD directory and it will be picked.
343 nsCOMPtr
<nsIURL
> dtdURL
= do_QueryInterface(aDTD
);
348 dtdURL
->GetFileName(fileName
);
349 if (fileName
.IsEmpty()) {
354 nsCOMPtr
<nsIFile
> dtdPath
;
355 NS_GetSpecialDirectory(NS_GRE_DIR
, getter_AddRefs(dtdPath
));
360 nsCOMPtr
<nsILocalFile
> lfile
= do_QueryInterface(dtdPath
);
362 // append res/dtd/<fileName>
363 // can't do AppendRelativeNativePath("res/dtd/" + fileName)
364 // as that won't work on all platforms.
365 lfile
->AppendNative(NS_LITERAL_CSTRING("res"));
366 lfile
->AppendNative(NS_LITERAL_CSTRING("dtd"));
367 lfile
->AppendNative(fileName
);
370 dtdPath
->Exists(&exists
);
375 // The DTD was found in the local DTD directory.
376 // Set aDTD to a file: url pointing to the local DTD
377 NS_NewFileURI(aResult
, dtdPath
);
379 return *aResult
!= nsnull
;
382 /***************************** END CATALOG UTILS *****************************/
384 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsExpatDriver
)
385 NS_INTERFACE_MAP_ENTRY(nsITokenizer
)
386 NS_INTERFACE_MAP_ENTRY(nsIDTD
)
387 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIDTD
)
390 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsExpatDriver
)
391 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsExpatDriver
)
393 NS_IMPL_CYCLE_COLLECTION_2(nsExpatDriver
, mSink
, mExtendedSink
)
395 nsExpatDriver::nsExpatDriver()
396 : mExpatParser(nsnull
),
398 mInInternalSubset(PR_FALSE
),
399 mInExternalDTD(PR_FALSE
),
400 mIsFinalChunk(PR_FALSE
),
401 mInternalState(NS_OK
),
407 nsExpatDriver::~nsExpatDriver()
410 XML_ParserFree(mExpatParser
);
415 nsExpatDriver::HandleStartElement(const PRUnichar
*aValue
,
416 const PRUnichar
**aAtts
)
418 NS_ASSERTION(mSink
, "content sink not found!");
420 // Calculate the total number of elements in aAtts.
421 // XML_GetSpecifiedAttributeCount will only give us the number of specified
422 // attrs (twice that number, actually), so we have to check for default attrs
424 PRUint32 attrArrayLength
;
425 for (attrArrayLength
= XML_GetSpecifiedAttributeCount(mExpatParser
);
426 aAtts
[attrArrayLength
];
427 attrArrayLength
+= 2) {
428 // Just looping till we find out what the length is
432 nsresult rv
= mSink
->
433 HandleStartElement(aValue
, aAtts
, attrArrayLength
,
434 XML_GetIdAttributeIndex(mExpatParser
),
435 XML_GetCurrentLineNumber(mExpatParser
));
443 nsExpatDriver::HandleEndElement(const PRUnichar
*aValue
)
445 NS_ASSERTION(mSink
, "content sink not found!");
446 NS_ASSERTION(mInternalState
!= NS_ERROR_HTMLPARSER_BLOCK
,
447 "Shouldn't block from HandleStartElement.");
449 if (mSink
&& mInternalState
!= NS_ERROR_HTMLPARSER_STOPPARSING
) {
450 nsresult rv
= mSink
->HandleEndElement(aValue
);
458 nsExpatDriver::HandleCharacterData(const PRUnichar
*aValue
,
459 const PRUint32 aLength
)
461 NS_ASSERTION(mSink
, "content sink not found!");
464 mCDataText
.Append(aValue
, aLength
);
467 nsresult rv
= mSink
->HandleCharacterData(aValue
, aLength
);
475 nsExpatDriver::HandleComment(const PRUnichar
*aValue
)
477 NS_ASSERTION(mSink
, "content sink not found!");
479 if (mInExternalDTD
) {
480 // Ignore comments from external DTDs
484 if (mInInternalSubset
) {
485 mInternalSubset
.AppendLiteral("<!--");
486 mInternalSubset
.Append(aValue
);
487 mInternalSubset
.AppendLiteral("-->");
490 nsresult rv
= mSink
->HandleComment(aValue
);
498 nsExpatDriver::HandleProcessingInstruction(const PRUnichar
*aTarget
,
499 const PRUnichar
*aData
)
501 NS_ASSERTION(mSink
, "content sink not found!");
503 if (mInExternalDTD
) {
504 // Ignore PIs in external DTDs for now. Eventually we want to
505 // pass them to the sink in a way that doesn't put them in the DOM
509 if (mInInternalSubset
) {
510 mInternalSubset
.AppendLiteral("<?");
511 mInternalSubset
.Append(aTarget
);
512 mInternalSubset
.Append(' ');
513 mInternalSubset
.Append(aData
);
514 mInternalSubset
.AppendLiteral("?>");
517 nsresult rv
= mSink
->HandleProcessingInstruction(aTarget
, aData
);
525 nsExpatDriver::HandleXMLDeclaration(const PRUnichar
*aVersion
,
526 const PRUnichar
*aEncoding
,
530 nsresult rv
= mSink
->HandleXMLDeclaration(aVersion
, aEncoding
, aStandalone
);
538 nsExpatDriver::HandleDefault(const PRUnichar
*aValue
,
539 const PRUint32 aLength
)
541 NS_ASSERTION(mSink
, "content sink not found!");
543 if (mInExternalDTD
) {
544 // Ignore newlines in external DTDs
548 if (mInInternalSubset
) {
549 mInternalSubset
.Append(aValue
, aLength
);
553 nsresult rv
= mInternalState
;
554 for (i
= 0; i
< aLength
&& NS_SUCCEEDED(rv
); ++i
) {
555 if (aValue
[i
] == '\n' || aValue
[i
] == '\r') {
556 rv
= mSink
->HandleCharacterData(&aValue
[i
], 1);
566 nsExpatDriver::HandleStartCdataSection()
574 nsExpatDriver::HandleEndCdataSection()
576 NS_ASSERTION(mSink
, "content sink not found!");
580 nsresult rv
= mSink
->HandleCDataSection(mCDataText
.get(),
581 mCDataText
.Length());
584 mCDataText
.Truncate();
590 nsExpatDriver::HandleStartNamespaceDecl(const PRUnichar
* aPrefix
,
591 const PRUnichar
* aUri
)
594 nsresult rv
= mExtendedSink
->HandleStartNamespaceDecl(aPrefix
, aUri
);
601 nsExpatDriver::HandleEndNamespaceDecl(const PRUnichar
* aPrefix
)
603 if (mExtendedSink
&& mInternalState
!= NS_ERROR_HTMLPARSER_STOPPARSING
) {
604 nsresult rv
= mExtendedSink
->HandleEndNamespaceDecl(aPrefix
);
611 nsExpatDriver::HandleNotationDecl(const PRUnichar
* aNotationName
,
612 const PRUnichar
* aBase
,
613 const PRUnichar
* aSysid
,
614 const PRUnichar
* aPubid
)
617 nsresult rv
= mExtendedSink
->HandleNotationDecl(aNotationName
, aSysid
,
625 nsExpatDriver::HandleUnparsedEntityDecl(const PRUnichar
* aEntityName
,
626 const PRUnichar
* aBase
,
627 const PRUnichar
* aSysid
,
628 const PRUnichar
* aPubid
,
629 const PRUnichar
* aNotationName
)
632 nsresult rv
= mExtendedSink
->HandleUnparsedEntityDecl(aEntityName
,
642 nsExpatDriver::HandleStartDoctypeDecl(const PRUnichar
* aDoctypeName
,
643 const PRUnichar
* aSysid
,
644 const PRUnichar
* aPubid
,
645 PRBool aHasInternalSubset
)
647 mDoctypeName
= aDoctypeName
;
652 nsresult rv
= mExtendedSink
->HandleStartDTD(aDoctypeName
, aSysid
, aPubid
);
656 if (aHasInternalSubset
) {
657 // Consuming a huge internal subset translates to numerous
658 // allocations. In an effort to avoid too many allocations
659 // setting mInternalSubset's capacity to be 1K ( just a guesstimate! ).
660 mInInternalSubset
= PR_TRUE
;
661 mInternalSubset
.SetCapacity(1024);
663 // Distinguish missing internal subset from an empty one
664 mInternalSubset
.SetIsVoid(PR_TRUE
);
671 nsExpatDriver::HandleEndDoctypeDecl()
673 NS_ASSERTION(mSink
, "content sink not found!");
675 mInInternalSubset
= PR_FALSE
;
678 // let the sink know any additional knowledge that we have about the
679 // document (currently, from bug 124570, we only expect to pass additional
680 // agent sheets needed to layout the XML vocabulary of the document)
681 nsCOMPtr
<nsIURI
> data
;
682 if (mCatalogData
&& mCatalogData
->mAgentSheet
) {
683 NS_NewURI(getter_AddRefs(data
), mCatalogData
->mAgentSheet
);
686 // Note: mInternalSubset already doesn't include the [] around it.
687 nsresult rv
= mSink
->HandleDoctypeDecl(mInternalSubset
, mDoctypeName
,
688 mSystemID
, mPublicID
, data
);
692 mInternalSubset
.SetCapacity(0);
698 ExternalDTDStreamReaderFunc(nsIUnicharInputStream
* aIn
,
700 const PRUnichar
* aFromSegment
,
703 PRUint32
*aWriteCount
)
705 // Pass the buffer to expat for parsing.
706 if (XML_Parse((XML_Parser
)aClosure
, (const char *)aFromSegment
,
707 aCount
* sizeof(PRUnichar
), 0) == XML_STATUS_OK
) {
708 *aWriteCount
= aCount
;
715 return NS_ERROR_FAILURE
;
719 nsExpatDriver::HandleExternalEntityRef(const PRUnichar
*openEntityNames
,
720 const PRUnichar
*base
,
721 const PRUnichar
*systemId
,
722 const PRUnichar
*publicId
)
724 if (mInInternalSubset
&& !mInExternalDTD
&& openEntityNames
) {
725 mInternalSubset
.Append(PRUnichar('%'));
726 mInternalSubset
.Append(nsDependentString(openEntityNames
));
727 mInternalSubset
.Append(PRUnichar(';'));
730 // Load the external entity into a buffer.
731 nsCOMPtr
<nsIInputStream
> in
;
733 nsresult rv
= OpenInputStreamFromExternalDTD(publicId
, systemId
, base
,
734 getter_AddRefs(in
), absURL
);
735 NS_ENSURE_SUCCESS(rv
, 1);
737 nsCOMPtr
<nsIUnicharInputStream
> uniIn
;
738 rv
= nsSimpleUnicharStreamFactory::GetInstance()->
739 CreateInstanceFromUTF8Stream(in
, getter_AddRefs(uniIn
));
740 NS_ENSURE_SUCCESS(rv
, 1);
744 XML_Parser entParser
= XML_ExternalEntityParserCreate(mExpatParser
, 0,
747 XML_SetBase(entParser
, absURL
.get());
749 mInExternalDTD
= PR_TRUE
;
753 rv
= uniIn
->ReadSegments(ExternalDTDStreamReaderFunc
, entParser
,
754 PRUint32(-1), &totalRead
);
755 } while (NS_SUCCEEDED(rv
) && totalRead
> 0);
757 result
= XML_Parse(entParser
, nsnull
, 0, 1);
759 mInExternalDTD
= PR_FALSE
;
761 XML_ParserFree(entParser
);
769 nsExpatDriver::OpenInputStreamFromExternalDTD(const PRUnichar
* aFPIStr
,
770 const PRUnichar
* aURLStr
,
771 const PRUnichar
* aBaseURL
,
772 nsIInputStream
** aStream
,
775 nsCOMPtr
<nsIURI
> baseURI
;
776 nsresult rv
= NS_NewURI(getter_AddRefs(baseURI
),
777 NS_ConvertUTF16toUTF8(aBaseURL
));
778 NS_ENSURE_SUCCESS(rv
, rv
);
780 nsCOMPtr
<nsIURI
> uri
;
781 rv
= NS_NewURI(getter_AddRefs(uri
), NS_ConvertUTF16toUTF8(aURLStr
), nsnull
,
783 NS_ENSURE_SUCCESS(rv
, rv
);
785 // check if it is alright to load this uri
786 PRBool isChrome
= PR_FALSE
;
787 uri
->SchemeIs("chrome", &isChrome
);
789 // since the url is not a chrome url, check to see if we can map the DTD
790 // to a known local DTD, or if a DTD file of the same name exists in the
791 // special DTD directory
793 // see if the Formal Public Identifier (FPI) maps to a catalog entry
794 mCatalogData
= LookupCatalogData(aFPIStr
);
797 nsCOMPtr
<nsIURI
> localURI
;
798 if (!IsLoadableDTD(mCatalogData
, uri
, getter_AddRefs(localURI
))) {
799 return NS_ERROR_NOT_IMPLEMENTED
;
805 nsCOMPtr
<nsIContentSink
> sink
= do_QueryInterface(mSink
);
806 nsCOMPtr
<nsIDocument
> doc
;
808 doc
= do_QueryInterface(sink
->GetTarget());
809 PRInt16 shouldLoad
= nsIContentPolicy::ACCEPT
;
810 rv
= NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_DTD
,
812 (doc
? doc
->NodePrincipal() : nsnull
),
814 EmptyCString(), //mime guess
817 if (NS_FAILED(rv
)) return rv
;
818 if (NS_CP_REJECTED(shouldLoad
)) {
819 // Disallowed by content policy
820 return NS_ERROR_CONTENT_BLOCKED
;
823 rv
= NS_OpenURI(aStream
, uri
);
825 nsCAutoString absURL
;
826 uri
->GetSpec(absURL
);
828 CopyUTF8toUTF16(absURL
, aAbsURL
);
834 CreateErrorText(const PRUnichar
* aDescription
,
835 const PRUnichar
* aSourceURL
,
836 const PRUint32 aLineNumber
,
837 const PRUint32 aColNumber
,
838 nsString
& aErrorString
)
840 aErrorString
.Truncate();
844 nsParserMsgUtils::GetLocalizedStringByName(XMLPARSER_PROPERTIES
,
845 "XMLParsingError", msg
);
846 NS_ENSURE_SUCCESS(rv
, rv
);
848 // XML Parsing Error: %1$S\nLocation: %2$S\nLine Number %3$u, Column %4$u:
849 PRUnichar
*message
= nsTextFormatter::smprintf(msg
.get(), aDescription
,
850 aSourceURL
, aLineNumber
,
853 return NS_ERROR_OUT_OF_MEMORY
;
856 aErrorString
.Assign(message
);
857 nsTextFormatter::smprintf_free(message
);
863 AppendErrorPointer(const PRInt32 aColNumber
,
864 const PRUnichar
*aSourceLine
,
865 nsString
& aSourceString
)
867 aSourceString
.Append(PRUnichar('\n'));
869 // Last character will be '^'.
870 PRInt32 last
= aColNumber
- 1;
872 PRUint32 minuses
= 0;
873 for (i
= 0; i
< last
; ++i
) {
874 if (aSourceLine
[i
] == '\t') {
875 // Since this uses |white-space: pre;| a tab stop equals 8 spaces.
876 PRUint32 add
= 8 - (minuses
% 8);
877 aSourceString
.AppendASCII("--------", add
);
881 aSourceString
.Append(PRUnichar('-'));
885 aSourceString
.Append(PRUnichar('^'));
891 nsExpatDriver::HandleError()
893 PRInt32 code
= XML_GetErrorCode(mExpatParser
);
894 NS_ASSERTION(code
> XML_ERROR_NONE
, "unexpected XML error code");
896 // Map Expat error code to an error string
897 // XXX Deal with error returns.
898 nsAutoString description
;
899 nsParserMsgUtils::GetLocalizedStringByID(XMLPARSER_PROPERTIES
, code
,
902 if (code
== XML_ERROR_TAG_MISMATCH
) {
904 * Expat can send the following:
906 * namespaceURI<separator>localName
907 * namespaceURI<separator>localName<separator>prefix
909 * and we use 0xFFFF for the <separator>.
912 const PRUnichar
*mismatch
= MOZ_XML_GetMismatchedTag(mExpatParser
);
913 const PRUnichar
*uriEnd
= nsnull
;
914 const PRUnichar
*nameEnd
= nsnull
;
915 const PRUnichar
*pos
;
916 for (pos
= mismatch
; *pos
; ++pos
) {
917 if (*pos
== kExpatSeparatorChar
) {
927 nsAutoString tagName
;
928 if (uriEnd
&& nameEnd
) {
930 tagName
.Append(nameEnd
+ 1, pos
- nameEnd
- 1);
931 tagName
.Append(PRUnichar(':'));
933 const PRUnichar
*nameStart
= uriEnd
? uriEnd
+ 1 : mismatch
;
934 tagName
.Append(nameStart
, (nameEnd
? nameEnd
: pos
) - nameStart
);
937 nsParserMsgUtils::GetLocalizedStringByName(XMLPARSER_PROPERTIES
,
940 // . Expected: </%S>.
941 PRUnichar
*message
= nsTextFormatter::smprintf(msg
.get(), tagName
.get());
943 return NS_ERROR_OUT_OF_MEMORY
;
946 description
.Append(message
);
948 nsTextFormatter::smprintf_free(message
);
951 // Adjust the column number so that it is one based rather than zero based.
952 PRUint32 colNumber
= XML_GetCurrentColumnNumber(mExpatParser
) + 1;
953 PRUint32 lineNumber
= XML_GetCurrentLineNumber(mExpatParser
);
955 nsAutoString errorText
;
956 CreateErrorText(description
.get(), XML_GetBase(mExpatParser
), lineNumber
,
957 colNumber
, errorText
);
959 NS_ASSERTION(mSink
, "no sink?");
961 nsAutoString
sourceText(mLastLine
);
962 AppendErrorPointer(colNumber
, mLastLine
.get(), sourceText
);
964 // Try to create and initialize the script error.
965 nsCOMPtr
<nsIScriptError
> serr(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID
));
966 nsresult rv
= NS_ERROR_FAILURE
;
968 rv
= serr
->Init(description
.get(),
971 lineNumber
, colNumber
,
972 nsIScriptError::errorFlag
, "malformed-xml");
975 // If it didn't initialize, we can't do any logging.
976 PRBool shouldReportError
= NS_SUCCEEDED(rv
);
978 if (mSink
&& shouldReportError
) {
979 rv
= mSink
->ReportError(errorText
.get(),
984 shouldReportError
= PR_TRUE
;
988 if (shouldReportError
) {
989 nsCOMPtr
<nsIConsoleService
> cs
990 (do_GetService(NS_CONSOLESERVICE_CONTRACTID
));
992 cs
->LogMessage(serr
);
996 return NS_ERROR_HTMLPARSER_STOPPARSING
;
1000 nsExpatDriver::ParseBuffer(const PRUnichar
*aBuffer
,
1003 PRUint32
*aConsumed
)
1005 NS_ASSERTION((aBuffer
&& aLength
!= 0) || (!aBuffer
&& aLength
== 0), "?");
1006 NS_ASSERTION(mInternalState
!= NS_OK
|| aIsFinal
|| aBuffer
,
1007 "Useless call, we won't call Expat");
1008 NS_PRECONDITION(!BlockedOrInterrupted() || !aBuffer
,
1009 "Non-null buffer when resuming");
1010 NS_PRECONDITION(XML_GetCurrentByteIndex(mExpatParser
) % sizeof(PRUnichar
) == 0,
1011 "Consumed part of a PRUnichar?");
1013 if (mExpatParser
&& (mInternalState
== NS_OK
|| BlockedOrInterrupted())) {
1014 PRInt32 parserBytesBefore
= XML_GetCurrentByteIndex(mExpatParser
);
1015 NS_ASSERTION(parserBytesBefore
>= 0, "Unexpected value");
1018 if (BlockedOrInterrupted()) {
1019 mInternalState
= NS_OK
; // Resume in case we're blocked.
1020 status
= XML_ResumeParser(mExpatParser
);
1023 status
= XML_Parse(mExpatParser
,
1024 reinterpret_cast<const char*>(aBuffer
),
1025 aLength
* sizeof(PRUnichar
), aIsFinal
);
1028 PRInt32 parserBytesConsumed
= XML_GetCurrentByteIndex(mExpatParser
);
1030 NS_ASSERTION(parserBytesConsumed
>= 0, "Unexpected value");
1031 NS_ASSERTION(parserBytesConsumed
>= parserBytesBefore
,
1032 "How'd this happen?");
1033 NS_ASSERTION(parserBytesConsumed
% sizeof(PRUnichar
) == 0,
1034 "Consumed part of a PRUnichar?");
1036 // Consumed something.
1037 *aConsumed
= (parserBytesConsumed
- parserBytesBefore
) / sizeof(PRUnichar
);
1038 NS_ASSERTION(*aConsumed
<= aLength
+ mExpatBuffered
,
1039 "Too many bytes consumed?");
1041 NS_ASSERTION(status
!= XML_STATUS_SUSPENDED
|| BlockedOrInterrupted(),
1042 "Inconsistent expat suspension state.");
1044 if (status
== XML_STATUS_ERROR
) {
1045 mInternalState
= NS_ERROR_HTMLPARSER_STOPPARSING
;
1054 nsExpatDriver::ConsumeToken(nsScanner
& aScanner
, PRBool
& aFlushTokens
)
1056 // We keep the scanner pointing to the position where Expat will start
1058 nsScannerIterator currentExpatPosition
;
1059 aScanner
.CurrentPosition(currentExpatPosition
);
1061 // This is the start of the first buffer that we need to pass to Expat.
1062 nsScannerIterator start
= currentExpatPosition
;
1063 start
.advance(mExpatBuffered
);
1065 // This is the end of the last buffer (at this point, more data could come in
1067 nsScannerIterator end
;
1068 aScanner
.EndReading(end
);
1070 PR_LOG(gExpatDriverLog
, PR_LOG_DEBUG
,
1071 ("Remaining in expat's buffer: %i, remaining in scanner: %i.",
1072 mExpatBuffered
, Distance(start
, end
)));
1074 PRBool flush
= mIsFinalChunk
;
1076 // We want to call Expat if we have more buffers, or if we know there won't
1077 // be more buffers (and so we want to flush the remaining data), or if we're
1078 // currently blocked and there's data in Expat's buffer.
1079 while (start
!= end
|| flush
||
1080 (BlockedOrInterrupted() && mExpatBuffered
> 0)) {
1081 PRBool noMoreBuffers
= start
== end
&& mIsFinalChunk
;
1082 PRBool blocked
= BlockedOrInterrupted();
1084 // If we're resuming and we know there won't be more data we want to
1085 // flush the remaining data after we resumed the parser (so loop once
1087 flush
= blocked
&& noMoreBuffers
;
1089 const PRUnichar
*buffer
;
1091 if (blocked
|| noMoreBuffers
) {
1092 // If we're blocked we just resume Expat so we don't need a buffer, if
1093 // there aren't any more buffers we pass a null buffer to Expat.
1098 PR_LOG(gExpatDriverLog
, PR_LOG_DEBUG
,
1099 ("Resuming Expat, will parse data remaining in Expat's "
1100 "buffer.\nContent of Expat's buffer:\n-----\n%s\n-----\n",
1101 NS_ConvertUTF16toUTF8(currentExpatPosition
.get(),
1102 mExpatBuffered
).get()));
1105 NS_ASSERTION(mExpatBuffered
== Distance(currentExpatPosition
, end
),
1106 "Didn't pass all the data to Expat?");
1107 PR_LOG(gExpatDriverLog
, PR_LOG_DEBUG
,
1108 ("Last call to Expat, will parse data remaining in Expat's "
1109 "buffer.\nContent of Expat's buffer:\n-----\n%s\n-----\n",
1110 NS_ConvertUTF16toUTF8(currentExpatPosition
.get(),
1111 mExpatBuffered
).get()));
1115 buffer
= start
.get();
1116 length
= PRUint32(start
.size_forward());
1118 PR_LOG(gExpatDriverLog
, PR_LOG_DEBUG
,
1119 ("Calling Expat, will parse data remaining in Expat's buffer and "
1120 "new data.\nContent of Expat's buffer:\n-----\n%s\n-----\nNew "
1121 "data:\n-----\n%s\n-----\n",
1122 NS_ConvertUTF16toUTF8(currentExpatPosition
.get(),
1123 mExpatBuffered
).get(),
1124 NS_ConvertUTF16toUTF8(start
.get(), length
).get()));
1128 ParseBuffer(buffer
, length
, noMoreBuffers
, &consumed
);
1130 nsScannerIterator oldExpatPosition
= currentExpatPosition
;
1131 currentExpatPosition
.advance(consumed
);
1133 // We consumed some data, we want to store the last line of data that
1134 // was consumed in case we run into an error (to show the line in which
1135 // the error occurred).
1137 // The length of the last line that Expat has parsed.
1138 XML_Size lastLineLength
= XML_GetCurrentColumnNumber(mExpatParser
);
1140 if (lastLineLength
<= consumed
) {
1141 // The length of the last line was less than what expat consumed, so
1142 // there was at least one line break in the consumed data. Store the
1143 // last line until the point where we stopped parsing.
1144 nsScannerIterator startLastLine
= currentExpatPosition
;
1145 startLastLine
.advance(-((ptrdiff_t)lastLineLength
));
1146 CopyUnicodeTo(startLastLine
, currentExpatPosition
, mLastLine
);
1149 // There was no line break in the consumed data, append the consumed
1151 AppendUnicodeTo(oldExpatPosition
, currentExpatPosition
, mLastLine
);
1155 mExpatBuffered
+= length
- consumed
;
1157 if (BlockedOrInterrupted()) {
1158 PR_LOG(gExpatDriverLog
, PR_LOG_DEBUG
,
1159 ("Blocked or interrupted parser (probably for loading linked "
1160 "stylesheets or scripts)."));
1162 aScanner
.SetPosition(currentExpatPosition
, PR_TRUE
);
1165 return mInternalState
;
1168 if (NS_FAILED(mInternalState
)) {
1169 if (XML_GetErrorCode(mExpatParser
) != XML_ERROR_NONE
) {
1170 NS_ASSERTION(mInternalState
== NS_ERROR_HTMLPARSER_STOPPARSING
,
1171 "Unexpected error");
1173 // Look for the next newline after the last one we consumed
1174 nsScannerIterator lastLine
= currentExpatPosition
;
1175 while (lastLine
!= end
) {
1176 length
= PRUint32(lastLine
.size_forward());
1177 PRUint32 endOffset
= 0;
1178 const PRUnichar
*buffer
= lastLine
.get();
1179 while (endOffset
< length
&& buffer
[endOffset
] != '\n' &&
1180 buffer
[endOffset
] != '\r') {
1183 mLastLine
.Append(Substring(buffer
, buffer
+ endOffset
));
1184 if (endOffset
< length
) {
1185 // We found a newline.
1189 lastLine
.advance(length
);
1195 return mInternalState
;
1198 // Either we have more buffers, or we were blocked (and we'll flush in the
1199 // next iteration), or we should have emptied Expat's buffer.
1200 NS_ASSERTION(!noMoreBuffers
|| blocked
||
1201 (mExpatBuffered
== 0 && currentExpatPosition
== end
),
1202 "Unreachable data left in Expat's buffer");
1204 start
.advance(length
);
1207 aScanner
.SetPosition(currentExpatPosition
, PR_TRUE
);
1210 PR_LOG(gExpatDriverLog
, PR_LOG_DEBUG
,
1211 ("Remaining in expat's buffer: %i, remaining in scanner: %i.",
1212 mExpatBuffered
, Distance(currentExpatPosition
, end
)));
1214 return NS_SUCCEEDED(mInternalState
) ? kEOF
: NS_OK
;
1218 nsExpatDriver::WillBuildModel(const CParserContext
& aParserContext
,
1219 nsITokenizer
* aTokenizer
,
1220 nsIContentSink
* aSink
)
1222 mSink
= do_QueryInterface(aSink
);
1224 NS_ERROR("nsExpatDriver didn't get an nsIExpatSink");
1225 // Make sure future calls to us bail out as needed
1226 mInternalState
= NS_ERROR_UNEXPECTED
;
1227 return mInternalState
;
1230 static const XML_Memory_Handling_Suite memsuite
=
1232 (void *(*)(size_t))PR_Malloc
,
1233 (void *(*)(void *, size_t))PR_Realloc
,
1237 static const PRUnichar kExpatSeparator
[] = { kExpatSeparatorChar
, '\0' };
1239 mExpatParser
= XML_ParserCreate_MM(kUTF16
, &memsuite
, kExpatSeparator
);
1240 NS_ENSURE_TRUE(mExpatParser
, NS_ERROR_FAILURE
);
1242 XML_SetReturnNSTriplet(mExpatParser
, XML_TRUE
);
1245 XML_SetParamEntityParsing(mExpatParser
, XML_PARAM_ENTITY_PARSING_ALWAYS
);
1248 mURISpec
= aParserContext
.mScanner
->GetFilename();
1250 XML_SetBase(mExpatParser
, mURISpec
.get());
1252 // Set up the callbacks
1253 XML_SetXmlDeclHandler(mExpatParser
, Driver_HandleXMLDeclaration
);
1254 XML_SetElementHandler(mExpatParser
, Driver_HandleStartElement
,
1255 Driver_HandleEndElement
);
1256 XML_SetCharacterDataHandler(mExpatParser
, Driver_HandleCharacterData
);
1257 XML_SetProcessingInstructionHandler(mExpatParser
,
1258 Driver_HandleProcessingInstruction
);
1259 XML_SetDefaultHandlerExpand(mExpatParser
, Driver_HandleDefault
);
1260 XML_SetExternalEntityRefHandler(mExpatParser
,
1261 (XML_ExternalEntityRefHandler
)
1262 Driver_HandleExternalEntityRef
);
1263 XML_SetExternalEntityRefHandlerArg(mExpatParser
, this);
1264 XML_SetCommentHandler(mExpatParser
, Driver_HandleComment
);
1265 XML_SetCdataSectionHandler(mExpatParser
, Driver_HandleStartCdataSection
,
1266 Driver_HandleEndCdataSection
);
1268 XML_SetParamEntityParsing(mExpatParser
,
1269 XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE
);
1270 XML_SetDoctypeDeclHandler(mExpatParser
, Driver_HandleStartDoctypeDecl
,
1271 Driver_HandleEndDoctypeDecl
);
1273 // If the sink is an nsIExtendedExpatSink,
1274 // register some addtional handlers.
1275 mExtendedSink
= do_QueryInterface(mSink
);
1276 if (mExtendedSink
) {
1277 XML_SetNamespaceDeclHandler(mExpatParser
,
1278 Driver_HandleStartNamespaceDecl
,
1279 Driver_HandleEndNamespaceDecl
);
1280 XML_SetUnparsedEntityDeclHandler(mExpatParser
,
1281 Driver_HandleUnparsedEntityDecl
);
1282 XML_SetNotationDeclHandler(mExpatParser
,
1283 Driver_HandleNotationDecl
);
1286 // Set up the user data.
1287 XML_SetUserData(mExpatParser
, this);
1289 return aSink
->WillBuildModel();
1293 nsExpatDriver::BuildModel(nsIParser
* aParser
,
1294 nsITokenizer
* aTokenizer
,
1295 nsITokenObserver
* anObserver
,
1296 nsIContentSink
* aSink
)
1298 return mInternalState
;
1302 nsExpatDriver::DidBuildModel(nsresult anErrorCode
,
1305 nsIContentSink
* aSink
)
1307 // Check for mSink is intentional. This would make sure
1308 // that DidBuildModel() is called only once on the sink.
1309 nsresult result
= NS_OK
;
1311 result
= aSink
->DidBuildModel();
1315 mExtendedSink
= nsnull
;
1321 nsExpatDriver::WillTokenize(PRBool aIsFinalChunk
,
1322 nsTokenAllocator
* aTokenAllocator
)
1324 mIsFinalChunk
= aIsFinalChunk
;
1329 nsExpatDriver::WillResumeParse(nsIContentSink
* aSink
)
1331 return aSink
? aSink
->WillResume() : NS_OK
;
1335 nsExpatDriver::WillInterruptParse(nsIContentSink
* aSink
)
1337 return aSink
? aSink
->WillInterrupt() : NS_OK
;
1341 nsExpatDriver::DidTokenize(PRBool aIsFinalChunk
)
1346 NS_IMETHODIMP_(void)
1347 nsExpatDriver::Terminate()
1349 // XXX - not sure what happens to the unparsed data.
1351 XML_StopParser(mExpatParser
, XML_FALSE
);
1353 mInternalState
= NS_ERROR_HTMLPARSER_STOPPARSING
;
1356 NS_IMETHODIMP_(PRInt32
)
1357 nsExpatDriver::GetType()
1359 return NS_IPARSER_FLAG_XML
;
1362 /*************************** Unused methods **********************************/
1364 NS_IMETHODIMP_(CToken
*)
1365 nsExpatDriver::PushTokenFront(CToken
* aToken
)
1370 NS_IMETHODIMP_(CToken
*)
1371 nsExpatDriver::PushToken(CToken
* aToken
)
1376 NS_IMETHODIMP_(CToken
*)
1377 nsExpatDriver::PopToken(void)
1382 NS_IMETHODIMP_(CToken
*)
1383 nsExpatDriver::PeekToken(void)
1388 NS_IMETHODIMP_(CToken
*)
1389 nsExpatDriver::GetTokenAt(PRInt32 anIndex
)
1394 NS_IMETHODIMP_(PRInt32
)
1395 nsExpatDriver::GetCount(void)
1400 NS_IMETHODIMP_(nsTokenAllocator
*)
1401 nsExpatDriver::GetTokenAllocator(void)
1406 NS_IMETHODIMP_(void)
1407 nsExpatDriver::PrependTokens(nsDeque
& aDeque
)
1412 nsExpatDriver::CopyState(nsITokenizer
* aTokenizer
)
1418 nsExpatDriver::HandleToken(CToken
* aToken
,nsIParser
* aParser
)
1423 NS_IMETHODIMP_(PRBool
)
1424 nsExpatDriver::IsContainer(PRInt32 aTag
) const
1429 NS_IMETHODIMP_(PRBool
)
1430 nsExpatDriver::CanContain(PRInt32 aParent
,PRInt32 aChild
) const
1436 nsExpatDriver::MaybeStopParser(nsresult aState
)
1438 if (NS_FAILED(aState
)) {
1439 // If we had a failure we want to override NS_ERROR_HTMLPARSER_INTERRUPTED
1440 // and we want to override NS_ERROR_HTMLPARSER_BLOCK but not with
1441 // NS_ERROR_HTMLPARSER_INTERRUPTED.
1442 if (NS_SUCCEEDED(mInternalState
) ||
1443 mInternalState
== NS_ERROR_HTMLPARSER_INTERRUPTED
||
1444 (mInternalState
== NS_ERROR_HTMLPARSER_BLOCK
&&
1445 aState
!= NS_ERROR_HTMLPARSER_INTERRUPTED
)) {
1446 mInternalState
= aState
;
1449 // If we get an error then we need to stop Expat (by calling XML_StopParser
1450 // with PR_FALSE as the last argument). If the parser should be blocked or
1451 // interrupted we need to pause Expat (by calling XML_StopParser with
1452 // PR_TRUE as the last argument).
1453 XML_StopParser(mExpatParser
, BlockedOrInterrupted());
1455 else if (NS_SUCCEEDED(mInternalState
)) {
1456 // Only clobber mInternalState with the success code if we didn't block or
1457 // interrupt before.
1458 mInternalState
= aState
;