1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsExpatDriver.h"
8 #include "nsParserCIID.h"
9 #include "CParserContext.h"
10 #include "nsIExpatSink.h"
11 #include "nsIContentSink.h"
12 #include "nsParserMsgUtils.h"
14 #include "nsIUnicharInputStream.h"
15 #include "nsIProtocolHandler.h"
16 #include "nsNetUtil.h"
17 #include "nsTextFormatter.h"
18 #include "nsDirectoryServiceDefs.h"
20 #include "nsIConsoleService.h"
21 #include "nsIScriptError.h"
22 #include "nsIContentPolicy.h"
23 #include "nsContentPolicyUtils.h"
25 #include "nsXPCOMCIDInternal.h"
26 #include "nsUnicharInputStream.h"
27 #include "nsContentUtils.h"
28 #include "mozilla/NullPrincipal.h"
30 #include "mozilla/Logging.h"
32 using mozilla::fallible
;
33 using mozilla::LogLevel
;
34 using mozilla::MakeStringSpan
;
35 using mozilla::dom::Document
;
37 #define kExpatSeparatorChar 0xFFFF
39 static const char16_t kUTF16
[] = {'U', 'T', 'F', '-', '1', '6', '\0'};
41 static mozilla::LazyLogModule
gExpatDriverLog("expatdriver");
43 /***************************** EXPAT CALL BACKS ******************************/
44 // The callback handlers that get called from the expat parser.
46 static void Driver_HandleXMLDeclaration(void* aUserData
,
47 const XML_Char
* aVersion
,
48 const XML_Char
* aEncoding
,
50 NS_ASSERTION(aUserData
, "expat driver should exist");
52 nsExpatDriver
* driver
= static_cast<nsExpatDriver
*>(aUserData
);
53 driver
->HandleXMLDeclaration(aVersion
, aEncoding
, aStandalone
);
57 static void Driver_HandleCharacterData(void* aUserData
, const XML_Char
* aData
,
59 NS_ASSERTION(aUserData
, "expat driver should exist");
61 nsExpatDriver
* driver
= static_cast<nsExpatDriver
*>(aUserData
);
62 driver
->HandleCharacterData(aData
, uint32_t(aLength
));
66 static void Driver_HandleComment(void* aUserData
, const XML_Char
* aName
) {
67 NS_ASSERTION(aUserData
, "expat driver should exist");
69 static_cast<nsExpatDriver
*>(aUserData
)->HandleComment(aName
);
73 static void Driver_HandleProcessingInstruction(void* aUserData
,
74 const XML_Char
* aTarget
,
75 const XML_Char
* aData
) {
76 NS_ASSERTION(aUserData
, "expat driver should exist");
78 nsExpatDriver
* driver
= static_cast<nsExpatDriver
*>(aUserData
);
79 driver
->HandleProcessingInstruction(aTarget
, aData
);
83 static void Driver_HandleDefault(void* aUserData
, const XML_Char
* aData
,
85 NS_ASSERTION(aUserData
, "expat driver should exist");
87 nsExpatDriver
* driver
= static_cast<nsExpatDriver
*>(aUserData
);
88 driver
->HandleDefault(aData
, uint32_t(aLength
));
92 static void Driver_HandleStartCdataSection(void* aUserData
) {
93 NS_ASSERTION(aUserData
, "expat driver should exist");
95 static_cast<nsExpatDriver
*>(aUserData
)->HandleStartCdataSection();
99 static void Driver_HandleEndCdataSection(void* aUserData
) {
100 NS_ASSERTION(aUserData
, "expat driver should exist");
102 static_cast<nsExpatDriver
*>(aUserData
)->HandleEndCdataSection();
106 static void Driver_HandleStartDoctypeDecl(void* aUserData
,
107 const XML_Char
* aDoctypeName
,
108 const XML_Char
* aSysid
,
109 const XML_Char
* aPubid
,
110 int aHasInternalSubset
) {
111 NS_ASSERTION(aUserData
, "expat driver should exist");
113 static_cast<nsExpatDriver
*>(aUserData
)->HandleStartDoctypeDecl(
114 aDoctypeName
, aSysid
, aPubid
, !!aHasInternalSubset
);
118 static void Driver_HandleEndDoctypeDecl(void* aUserData
) {
119 NS_ASSERTION(aUserData
, "expat driver should exist");
121 static_cast<nsExpatDriver
*>(aUserData
)->HandleEndDoctypeDecl();
125 static int Driver_HandleExternalEntityRef(void* aExternalEntityRefHandler
,
126 const XML_Char
* aOpenEntityNames
,
127 const XML_Char
* aBase
,
128 const XML_Char
* aSystemId
,
129 const XML_Char
* aPublicId
) {
130 NS_ASSERTION(aExternalEntityRefHandler
, "expat driver should exist");
131 if (!aExternalEntityRefHandler
) {
135 nsExpatDriver
* driver
=
136 static_cast<nsExpatDriver
*>(aExternalEntityRefHandler
);
138 return driver
->HandleExternalEntityRef(aOpenEntityNames
, aBase
, aSystemId
,
142 /***************************** END CALL BACKS ********************************/
144 /***************************** CATALOG UTILS *********************************/
146 // Initially added for bug 113400 to switch from the remote "XHTML 1.0 plus
147 // MathML 2.0" DTD to the the lightweight customized version that Mozilla uses.
148 // Since Mozilla is not validating, no need to fetch a *huge* file at each
150 // XXX The cleanest solution here would be to fix Bug 98413: Implement XML
152 struct nsCatalogData
{
153 const char* mPublicID
;
154 const char* mLocalDTD
;
155 const char* mAgentSheet
;
158 // The order of this table is guestimated to be in the optimum order
159 static const nsCatalogData kCatalogTable
[] = {
160 {"-//W3C//DTD XHTML 1.0 Transitional//EN", "htmlmathml-f.ent", nullptr},
161 {"-//W3C//DTD XHTML 1.1//EN", "htmlmathml-f.ent", nullptr},
162 {"-//W3C//DTD XHTML 1.0 Strict//EN", "htmlmathml-f.ent", nullptr},
163 {"-//W3C//DTD XHTML 1.0 Frameset//EN", "htmlmathml-f.ent", nullptr},
164 {"-//W3C//DTD XHTML Basic 1.0//EN", "htmlmathml-f.ent", nullptr},
165 {"-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN", "htmlmathml-f.ent", nullptr},
166 {"-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN",
167 "htmlmathml-f.ent", nullptr},
168 {"-//W3C//DTD MathML 2.0//EN", "htmlmathml-f.ent", nullptr},
169 {"-//WAPFORUM//DTD XHTML Mobile 1.0//EN", "htmlmathml-f.ent", nullptr},
170 {nullptr, nullptr, nullptr}};
172 static const nsCatalogData
* LookupCatalogData(const char16_t
* aPublicID
) {
173 nsDependentString
publicID(aPublicID
);
175 // linear search for now since the number of entries is going to
176 // be negligible, and the fix for bug 98413 would get rid of this
178 const nsCatalogData
* data
= kCatalogTable
;
179 while (data
->mPublicID
) {
180 if (publicID
.EqualsASCII(data
->mPublicID
)) {
189 // This function provides a resource URI to a local DTD
190 // in resource://gre/res/dtd/ which may or may not exist.
191 // If aCatalogData is provided, it is used to remap the
192 // DTD instead of taking the filename from the URI.
193 static void GetLocalDTDURI(const nsCatalogData
* aCatalogData
, nsIURI
* aDTD
,
195 NS_ASSERTION(aDTD
, "Null parameter.");
197 nsAutoCString fileName
;
199 // remap the DTD to a known local DTD
200 fileName
.Assign(aCatalogData
->mLocalDTD
);
203 if (fileName
.IsEmpty()) {
204 // Try to see if the user has installed the DTD file -- we extract the
205 // filename.ext of the DTD here. Hence, for any DTD for which we have
206 // no predefined mapping, users just have to copy the DTD file to our
207 // special DTD directory and it will be picked.
208 nsCOMPtr
<nsIURL
> dtdURL
= do_QueryInterface(aDTD
);
213 dtdURL
->GetFileName(fileName
);
214 if (fileName
.IsEmpty()) {
219 nsAutoCString
respath("resource://gre/res/dtd/");
221 NS_NewURI(aResult
, respath
);
224 /***************************** END CATALOG UTILS *****************************/
226 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsExpatDriver
)
227 NS_INTERFACE_MAP_ENTRY(nsITokenizer
)
228 NS_INTERFACE_MAP_ENTRY(nsIDTD
)
229 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIDTD
)
232 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsExpatDriver
)
233 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsExpatDriver
)
235 NS_IMPL_CYCLE_COLLECTION(nsExpatDriver
, mSink
)
237 nsExpatDriver::nsExpatDriver()
238 : mExpatParser(nullptr),
240 mInInternalSubset(false),
241 mInExternalDTD(false),
242 mMadeFinalCallToExpat(false),
243 mIsFinalChunk(false),
244 mInternalState(NS_OK
),
246 mCatalogData(nullptr),
249 nsExpatDriver::~nsExpatDriver() {
251 XML_ParserFree(mExpatParser
);
256 void nsExpatDriver::HandleStartElement(void* aUserData
, const char16_t
* aName
,
257 const char16_t
** aAtts
) {
258 nsExpatDriver
* self
= static_cast<nsExpatDriver
*>(aUserData
);
260 NS_ASSERTION(self
->mSink
, "content sink not found!");
262 // Calculate the total number of elements in aAtts.
263 // XML_GetSpecifiedAttributeCount will only give us the number of specified
264 // attrs (twice that number, actually), so we have to check for default attrs
266 uint32_t attrArrayLength
;
267 for (attrArrayLength
= XML_GetSpecifiedAttributeCount(self
->mExpatParser
);
268 aAtts
[attrArrayLength
]; attrArrayLength
+= 2) {
269 // Just looping till we find out what the length is
273 nsresult rv
= self
->mSink
->HandleStartElement(
274 aName
, aAtts
, attrArrayLength
,
275 XML_GetCurrentLineNumber(self
->mExpatParser
),
276 XML_GetCurrentColumnNumber(self
->mExpatParser
));
277 self
->MaybeStopParser(rv
);
282 void nsExpatDriver::HandleStartElementForSystemPrincipal(
283 void* aUserData
, const char16_t
* aName
, const char16_t
** aAtts
) {
284 nsExpatDriver
* self
= static_cast<nsExpatDriver
*>(aUserData
);
286 if (!MOZ_XML_ProcessingEntityValue(self
->mExpatParser
)) {
287 HandleStartElement(aUserData
, aName
, aAtts
);
289 nsCOMPtr
<Document
> doc
=
290 do_QueryInterface(self
->mOriginalSink
->GetTarget());
292 // Adjust the column number so that it is one based rather than zero based.
293 uint32_t colNumber
= XML_GetCurrentColumnNumber(self
->mExpatParser
) + 1;
294 uint32_t lineNumber
= XML_GetCurrentLineNumber(self
->mExpatParser
);
297 RefPtr
<nsAtom
> prefix
, localName
;
298 nsContentUtils::SplitExpatName(aName
, getter_AddRefs(prefix
),
299 getter_AddRefs(localName
), &nameSpaceID
);
302 error
.AppendLiteral("Ignoring element <");
304 error
.Append(prefix
->GetUTF16String());
307 error
.Append(localName
->GetUTF16String());
308 error
.AppendLiteral("> created from entity value.");
310 nsContentUtils::ReportToConsoleNonLocalized(
311 error
, nsIScriptError::warningFlag
, NS_LITERAL_CSTRING("XML Document"),
312 doc
, nullptr, EmptyString(), lineNumber
, colNumber
);
317 void nsExpatDriver::HandleEndElement(void* aUserData
, const char16_t
* aName
) {
318 nsExpatDriver
* self
= static_cast<nsExpatDriver
*>(aUserData
);
320 NS_ASSERTION(self
->mSink
, "content sink not found!");
321 NS_ASSERTION(self
->mInternalState
!= NS_ERROR_HTMLPARSER_BLOCK
,
322 "Shouldn't block from HandleStartElement.");
324 if (self
->mSink
&& self
->mInternalState
!= NS_ERROR_HTMLPARSER_STOPPARSING
) {
325 nsresult rv
= self
->mSink
->HandleEndElement(aName
);
326 self
->MaybeStopParser(rv
);
331 void nsExpatDriver::HandleEndElementForSystemPrincipal(void* aUserData
,
332 const char16_t
* aName
) {
333 nsExpatDriver
* self
= static_cast<nsExpatDriver
*>(aUserData
);
335 if (!MOZ_XML_ProcessingEntityValue(self
->mExpatParser
)) {
336 HandleEndElement(aUserData
, aName
);
340 nsresult
nsExpatDriver::HandleCharacterData(const char16_t
* aValue
,
341 const uint32_t aLength
) {
342 NS_ASSERTION(mSink
, "content sink not found!");
345 if (!mCDataText
.Append(aValue
, aLength
, fallible
)) {
346 MaybeStopParser(NS_ERROR_OUT_OF_MEMORY
);
349 nsresult rv
= mSink
->HandleCharacterData(aValue
, aLength
);
356 nsresult
nsExpatDriver::HandleComment(const char16_t
* aValue
) {
357 NS_ASSERTION(mSink
, "content sink not found!");
359 if (mInExternalDTD
) {
360 // Ignore comments from external DTDs
364 if (mInInternalSubset
) {
365 mInternalSubset
.AppendLiteral("<!--");
366 mInternalSubset
.Append(aValue
);
367 mInternalSubset
.AppendLiteral("-->");
369 nsresult rv
= mSink
->HandleComment(aValue
);
376 nsresult
nsExpatDriver::HandleProcessingInstruction(const char16_t
* aTarget
,
377 const char16_t
* aData
) {
378 NS_ASSERTION(mSink
, "content sink not found!");
380 if (mInExternalDTD
) {
381 // Ignore PIs in external DTDs for now. Eventually we want to
382 // pass them to the sink in a way that doesn't put them in the DOM
386 if (mInInternalSubset
) {
387 mInternalSubset
.AppendLiteral("<?");
388 mInternalSubset
.Append(aTarget
);
389 mInternalSubset
.Append(' ');
390 mInternalSubset
.Append(aData
);
391 mInternalSubset
.AppendLiteral("?>");
393 nsresult rv
= mSink
->HandleProcessingInstruction(aTarget
, aData
);
400 nsresult
nsExpatDriver::HandleXMLDeclaration(const char16_t
* aVersion
,
401 const char16_t
* aEncoding
,
402 int32_t aStandalone
) {
404 nsresult rv
= mSink
->HandleXMLDeclaration(aVersion
, aEncoding
, aStandalone
);
411 nsresult
nsExpatDriver::HandleDefault(const char16_t
* aValue
,
412 const uint32_t aLength
) {
413 NS_ASSERTION(mSink
, "content sink not found!");
415 if (mInExternalDTD
) {
416 // Ignore newlines in external DTDs
420 if (mInInternalSubset
) {
421 mInternalSubset
.Append(aValue
, aLength
);
424 nsresult rv
= mInternalState
;
425 for (i
= 0; i
< aLength
&& NS_SUCCEEDED(rv
); ++i
) {
426 if (aValue
[i
] == '\n' || aValue
[i
] == '\r') {
427 rv
= mSink
->HandleCharacterData(&aValue
[i
], 1);
436 nsresult
nsExpatDriver::HandleStartCdataSection() {
442 nsresult
nsExpatDriver::HandleEndCdataSection() {
443 NS_ASSERTION(mSink
, "content sink not found!");
448 mSink
->HandleCDataSection(mCDataText
.get(), mCDataText
.Length());
451 mCDataText
.Truncate();
456 nsresult
nsExpatDriver::HandleStartDoctypeDecl(const char16_t
* aDoctypeName
,
457 const char16_t
* aSysid
,
458 const char16_t
* aPubid
,
459 bool aHasInternalSubset
) {
460 mDoctypeName
= aDoctypeName
;
464 if (aHasInternalSubset
) {
465 // Consuming a huge internal subset translates to numerous
466 // allocations. In an effort to avoid too many allocations
467 // setting mInternalSubset's capacity to be 1K ( just a guesstimate! ).
468 mInInternalSubset
= true;
469 mInternalSubset
.SetCapacity(1024);
471 // Distinguish missing internal subset from an empty one
472 mInternalSubset
.SetIsVoid(true);
478 nsresult
nsExpatDriver::HandleEndDoctypeDecl() {
479 NS_ASSERTION(mSink
, "content sink not found!");
481 mInInternalSubset
= false;
484 // let the sink know any additional knowledge that we have about the
485 // document (currently, from bug 124570, we only expect to pass additional
486 // agent sheets needed to layout the XML vocabulary of the document)
487 nsCOMPtr
<nsIURI
> data
;
489 if (mCatalogData
&& mCatalogData
->mAgentSheet
) {
490 NS_NewURI(getter_AddRefs(data
), mCatalogData
->mAgentSheet
);
494 // The unused support for "catalog style sheets" was removed. It doesn't
495 // look like we'll ever fix bug 98413 either.
496 MOZ_ASSERT(!mCatalogData
|| !mCatalogData
->mAgentSheet
,
497 "Need to add back support for catalog style sheets");
499 // Note: mInternalSubset already doesn't include the [] around it.
500 nsresult rv
= mSink
->HandleDoctypeDecl(mInternalSubset
, mDoctypeName
,
501 mSystemID
, mPublicID
, data
);
505 mInternalSubset
.Truncate();
510 static nsresult
ExternalDTDStreamReaderFunc(nsIUnicharInputStream
* aIn
,
512 const char16_t
* aFromSegment
,
513 uint32_t aToOffset
, uint32_t aCount
,
514 uint32_t* aWriteCount
) {
515 // Pass the buffer to expat for parsing.
516 if (XML_Parse((XML_Parser
)aClosure
, (const char*)aFromSegment
,
517 aCount
* sizeof(char16_t
), 0) == XML_STATUS_OK
) {
518 *aWriteCount
= aCount
;
525 return NS_ERROR_FAILURE
;
528 int nsExpatDriver::HandleExternalEntityRef(const char16_t
* openEntityNames
,
529 const char16_t
* base
,
530 const char16_t
* systemId
,
531 const char16_t
* publicId
) {
532 if (mInInternalSubset
&& !mInExternalDTD
&& openEntityNames
) {
533 mInternalSubset
.Append(char16_t('%'));
534 mInternalSubset
.Append(nsDependentString(openEntityNames
));
535 mInternalSubset
.Append(char16_t(';'));
538 // Load the external entity into a buffer.
539 nsCOMPtr
<nsIInputStream
> in
;
541 nsresult rv
= OpenInputStreamFromExternalDTD(publicId
, systemId
, base
,
542 getter_AddRefs(in
), absURL
);
545 nsCString
message("Failed to open external DTD: publicId \"");
546 AppendUTF16toUTF8(MakeStringSpan(publicId
), message
);
547 message
+= "\" systemId \"";
548 AppendUTF16toUTF8(MakeStringSpan(systemId
), message
);
549 message
+= "\" base \"";
550 AppendUTF16toUTF8(MakeStringSpan(base
), message
);
551 message
+= "\" URL \"";
552 AppendUTF16toUTF8(absURL
, message
);
554 NS_WARNING(message
.get());
559 nsCOMPtr
<nsIUnicharInputStream
> uniIn
;
560 rv
= NS_NewUnicharInputStream(in
, getter_AddRefs(uniIn
));
561 NS_ENSURE_SUCCESS(rv
, 1);
565 XML_Parser entParser
=
566 XML_ExternalEntityParserCreate(mExpatParser
, 0, kUTF16
);
568 XML_SetBase(entParser
, absURL
.get());
570 mInExternalDTD
= true;
574 rv
= uniIn
->ReadSegments(ExternalDTDStreamReaderFunc
, entParser
,
575 uint32_t(-1), &totalRead
);
576 } while (NS_SUCCEEDED(rv
) && totalRead
> 0);
578 result
= XML_Parse(entParser
, nullptr, 0, 1);
580 mInExternalDTD
= false;
582 XML_ParserFree(entParser
);
589 nsresult
nsExpatDriver::OpenInputStreamFromExternalDTD(const char16_t
* aFPIStr
,
590 const char16_t
* aURLStr
,
591 const char16_t
* aBaseURL
,
592 nsIInputStream
** aStream
,
593 nsAString
& aAbsURL
) {
594 nsCOMPtr
<nsIURI
> baseURI
;
596 NS_NewURI(getter_AddRefs(baseURI
), NS_ConvertUTF16toUTF8(aBaseURL
));
597 NS_ENSURE_SUCCESS(rv
, rv
);
599 nsCOMPtr
<nsIURI
> uri
;
600 rv
= NS_NewURI(getter_AddRefs(uri
), NS_ConvertUTF16toUTF8(aURLStr
), nullptr,
602 NS_ENSURE_SUCCESS(rv
, rv
);
604 // make sure the URI is allowed to be loaded in sync
605 bool isUIResource
= false;
606 rv
= NS_URIChainHasFlags(uri
, nsIProtocolHandler::URI_IS_UI_RESOURCE
,
608 NS_ENSURE_SUCCESS(rv
, rv
);
610 nsCOMPtr
<nsIURI
> localURI
;
612 // Check to see if we can map the DTD to a known local DTD, or if a DTD
613 // file of the same name exists in the special DTD directory
615 // see if the Formal Public Identifier (FPI) maps to a catalog entry
616 mCatalogData
= LookupCatalogData(aFPIStr
);
617 GetLocalDTDURI(mCatalogData
, uri
, getter_AddRefs(localURI
));
620 return NS_ERROR_NOT_IMPLEMENTED
;
624 nsCOMPtr
<nsIChannel
> channel
;
627 rv
= NS_NewChannel(getter_AddRefs(channel
), uri
,
628 nsContentUtils::GetSystemPrincipal(),
629 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL
,
630 nsIContentPolicy::TYPE_DTD
);
633 mSink
== nsCOMPtr
<nsIExpatSink
>(do_QueryInterface(mOriginalSink
)),
634 "In nsExpatDriver::OpenInputStreamFromExternalDTD: "
635 "mOriginalSink not the same object as mSink?");
636 nsCOMPtr
<nsIPrincipal
> loadingPrincipal
;
638 nsCOMPtr
<Document
> doc
;
639 doc
= do_QueryInterface(mOriginalSink
->GetTarget());
641 loadingPrincipal
= doc
->NodePrincipal();
644 if (!loadingPrincipal
) {
646 mozilla::NullPrincipal::CreateWithoutOriginAttributes();
648 rv
= NS_NewChannel(getter_AddRefs(channel
), uri
, loadingPrincipal
,
649 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS
|
650 nsILoadInfo::SEC_ALLOW_CHROME
,
651 nsIContentPolicy::TYPE_DTD
);
653 NS_ENSURE_SUCCESS(rv
, rv
);
655 nsAutoCString absURL
;
656 rv
= uri
->GetSpec(absURL
);
657 NS_ENSURE_SUCCESS(rv
, rv
);
658 CopyUTF8toUTF16(absURL
, aAbsURL
);
660 channel
->SetContentType(NS_LITERAL_CSTRING("application/xml"));
661 return channel
->Open(aStream
);
664 static nsresult
CreateErrorText(const char16_t
* aDescription
,
665 const char16_t
* aSourceURL
,
666 const uint32_t aLineNumber
,
667 const uint32_t aColNumber
,
668 nsString
& aErrorString
) {
669 aErrorString
.Truncate();
672 nsresult rv
= nsParserMsgUtils::GetLocalizedStringByName(
673 XMLPARSER_PROPERTIES
, "XMLParsingError", msg
);
674 NS_ENSURE_SUCCESS(rv
, rv
);
676 // XML Parsing Error: %1$S\nLocation: %2$S\nLine Number %3$u, Column %4$u:
677 nsTextFormatter::ssprintf(aErrorString
, msg
.get(), aDescription
, aSourceURL
,
678 aLineNumber
, aColNumber
);
682 static nsresult
AppendErrorPointer(const int32_t aColNumber
,
683 const char16_t
* aSourceLine
,
684 nsString
& aSourceString
) {
685 aSourceString
.Append(char16_t('\n'));
687 // Last character will be '^'.
688 int32_t last
= aColNumber
- 1;
690 uint32_t minuses
= 0;
691 for (i
= 0; i
< last
; ++i
) {
692 if (aSourceLine
[i
] == '\t') {
693 // Since this uses |white-space: pre;| a tab stop equals 8 spaces.
694 uint32_t add
= 8 - (minuses
% 8);
695 aSourceString
.AppendASCII("--------", add
);
698 aSourceString
.Append(char16_t('-'));
702 aSourceString
.Append(char16_t('^'));
707 nsresult
nsExpatDriver::HandleError() {
708 int32_t code
= XML_GetErrorCode(mExpatParser
);
709 NS_ASSERTION(code
> XML_ERROR_NONE
, "unexpected XML error code");
711 // Map Expat error code to an error string
712 // XXX Deal with error returns.
713 nsAutoString description
;
714 nsParserMsgUtils::GetLocalizedStringByID(XMLPARSER_PROPERTIES
, code
,
717 if (code
== XML_ERROR_TAG_MISMATCH
) {
719 * Expat can send the following:
721 * namespaceURI<separator>localName
722 * namespaceURI<separator>localName<separator>prefix
724 * and we use 0xFFFF for the <separator>.
727 const char16_t
* mismatch
= MOZ_XML_GetMismatchedTag(mExpatParser
);
728 const char16_t
* uriEnd
= nullptr;
729 const char16_t
* nameEnd
= nullptr;
731 for (pos
= mismatch
; *pos
; ++pos
) {
732 if (*pos
== kExpatSeparatorChar
) {
741 nsAutoString tagName
;
742 if (uriEnd
&& nameEnd
) {
744 tagName
.Append(nameEnd
+ 1, pos
- nameEnd
- 1);
745 tagName
.Append(char16_t(':'));
747 const char16_t
* nameStart
= uriEnd
? uriEnd
+ 1 : mismatch
;
748 tagName
.Append(nameStart
, (nameEnd
? nameEnd
: pos
) - nameStart
);
751 nsParserMsgUtils::GetLocalizedStringByName(XMLPARSER_PROPERTIES
, "Expected",
754 // . Expected: </%S>.
755 nsAutoString message
;
756 nsTextFormatter::ssprintf(message
, msg
.get(), tagName
.get());
757 description
.Append(message
);
760 // Adjust the column number so that it is one based rather than zero based.
761 uint32_t colNumber
= XML_GetCurrentColumnNumber(mExpatParser
) + 1;
762 uint32_t lineNumber
= XML_GetCurrentLineNumber(mExpatParser
);
764 nsAutoString errorText
;
765 CreateErrorText(description
.get(), XML_GetBase(mExpatParser
), lineNumber
,
766 colNumber
, errorText
);
768 nsAutoString
sourceText(mLastLine
);
769 AppendErrorPointer(colNumber
, mLastLine
.get(), sourceText
);
771 // Try to create and initialize the script error.
772 nsCOMPtr
<nsIScriptError
> serr(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID
));
773 nsresult rv
= NS_ERROR_FAILURE
;
775 rv
= serr
->InitWithWindowID(errorText
, mURISpec
, mLastLine
, lineNumber
,
776 colNumber
, nsIScriptError::errorFlag
,
777 "malformed-xml", mInnerWindowID
);
780 // If it didn't initialize, we can't do any logging.
781 bool shouldReportError
= NS_SUCCEEDED(rv
);
783 // mSink might be null here if our parser was terminated.
784 if (mSink
&& shouldReportError
) {
785 rv
= mSink
->ReportError(errorText
.get(), sourceText
.get(), serr
,
788 shouldReportError
= true;
792 // mOriginalSink might be null here if our parser was terminated.
794 nsCOMPtr
<Document
> doc
= do_QueryInterface(mOriginalSink
->GetTarget());
795 if (doc
&& doc
->SuppressParserErrorConsoleMessages()) {
796 shouldReportError
= false;
800 if (shouldReportError
) {
801 nsCOMPtr
<nsIConsoleService
> cs(do_GetService(NS_CONSOLESERVICE_CONTRACTID
));
803 cs
->LogMessage(serr
);
807 return NS_ERROR_HTMLPARSER_STOPPARSING
;
810 void nsExpatDriver::ParseBuffer(const char16_t
* aBuffer
, uint32_t aLength
,
811 bool aIsFinal
, uint32_t* aConsumed
) {
812 NS_ASSERTION((aBuffer
&& aLength
!= 0) || (!aBuffer
&& aLength
== 0), "?");
813 NS_ASSERTION(mInternalState
!= NS_OK
|| aIsFinal
|| aBuffer
,
814 "Useless call, we won't call Expat");
815 MOZ_ASSERT(!BlockedOrInterrupted() || !aBuffer
,
816 "Non-null buffer when resuming");
817 MOZ_ASSERT(XML_GetCurrentByteIndex(mExpatParser
) % sizeof(char16_t
) == 0,
818 "Consumed part of a char16_t?");
820 if (mExpatParser
&& (mInternalState
== NS_OK
|| BlockedOrInterrupted())) {
821 int32_t parserBytesBefore
= XML_GetCurrentByteIndex(mExpatParser
);
822 NS_ASSERTION(parserBytesBefore
>= 0, "Unexpected value");
825 if (BlockedOrInterrupted()) {
826 mInternalState
= NS_OK
; // Resume in case we're blocked.
827 status
= XML_ResumeParser(mExpatParser
);
829 status
= XML_Parse(mExpatParser
, reinterpret_cast<const char*>(aBuffer
),
830 aLength
* sizeof(char16_t
), aIsFinal
);
833 int32_t parserBytesConsumed
= XML_GetCurrentByteIndex(mExpatParser
);
835 NS_ASSERTION(parserBytesConsumed
>= 0, "Unexpected value");
836 NS_ASSERTION(parserBytesConsumed
>= parserBytesBefore
,
837 "How'd this happen?");
838 NS_ASSERTION(parserBytesConsumed
% sizeof(char16_t
) == 0,
839 "Consumed part of a char16_t?");
841 // Consumed something.
842 *aConsumed
= (parserBytesConsumed
- parserBytesBefore
) / sizeof(char16_t
);
843 NS_ASSERTION(*aConsumed
<= aLength
+ mExpatBuffered
,
844 "Too many bytes consumed?");
846 NS_ASSERTION(status
!= XML_STATUS_SUSPENDED
|| BlockedOrInterrupted(),
847 "Inconsistent expat suspension state.");
849 if (status
== XML_STATUS_ERROR
) {
850 mInternalState
= NS_ERROR_HTMLPARSER_STOPPARSING
;
858 nsExpatDriver::ConsumeToken(nsScanner
& aScanner
, bool& aFlushTokens
) {
859 // We keep the scanner pointing to the position where Expat will start
861 nsScannerIterator currentExpatPosition
;
862 aScanner
.CurrentPosition(currentExpatPosition
);
864 // This is the start of the first buffer that we need to pass to Expat.
865 nsScannerIterator start
= currentExpatPosition
;
866 start
.advance(mExpatBuffered
);
868 // This is the end of the last buffer (at this point, more data could come in
870 nsScannerIterator end
;
871 aScanner
.EndReading(end
);
873 MOZ_LOG(gExpatDriverLog
, LogLevel::Debug
,
874 ("Remaining in expat's buffer: %i, remaining in scanner: %zu.",
875 mExpatBuffered
, Distance(start
, end
)));
877 // We want to call Expat if we have more buffers, or if we know there won't
878 // be more buffers (and so we want to flush the remaining data), or if we're
879 // currently blocked and there's data in Expat's buffer.
880 while (start
!= end
|| (mIsFinalChunk
&& !mMadeFinalCallToExpat
) ||
881 (BlockedOrInterrupted() && mExpatBuffered
> 0)) {
882 bool noMoreBuffers
= start
== end
&& mIsFinalChunk
;
883 bool blocked
= BlockedOrInterrupted();
885 const char16_t
* buffer
;
887 if (blocked
|| noMoreBuffers
) {
888 // If we're blocked we just resume Expat so we don't need a buffer, if
889 // there aren't any more buffers we pass a null buffer to Expat.
895 gExpatDriverLog
, LogLevel::Debug
,
896 ("Resuming Expat, will parse data remaining in Expat's "
897 "buffer.\nContent of Expat's buffer:\n-----\n%s\n-----\n",
898 NS_ConvertUTF16toUTF8(currentExpatPosition
.get(), mExpatBuffered
)
901 NS_ASSERTION(mExpatBuffered
== Distance(currentExpatPosition
, end
),
902 "Didn't pass all the data to Expat?");
904 gExpatDriverLog
, LogLevel::Debug
,
905 ("Last call to Expat, will parse data remaining in Expat's "
906 "buffer.\nContent of Expat's buffer:\n-----\n%s\n-----\n",
907 NS_ConvertUTF16toUTF8(currentExpatPosition
.get(), mExpatBuffered
)
911 buffer
= start
.get();
912 length
= uint32_t(start
.size_forward());
914 MOZ_LOG(gExpatDriverLog
, LogLevel::Debug
,
915 ("Calling Expat, will parse data remaining in Expat's buffer and "
916 "new data.\nContent of Expat's buffer:\n-----\n%s\n-----\nNew "
917 "data:\n-----\n%s\n-----\n",
918 NS_ConvertUTF16toUTF8(currentExpatPosition
.get(), mExpatBuffered
)
920 NS_ConvertUTF16toUTF8(start
.get(), length
).get()));
924 ParseBuffer(buffer
, length
, noMoreBuffers
, &consumed
);
926 nsScannerIterator oldExpatPosition
= currentExpatPosition
;
927 currentExpatPosition
.advance(consumed
);
929 // We consumed some data, we want to store the last line of data that
930 // was consumed in case we run into an error (to show the line in which
931 // the error occurred).
933 // The length of the last line that Expat has parsed.
934 XML_Size lastLineLength
= XML_GetCurrentColumnNumber(mExpatParser
);
936 if (lastLineLength
<= consumed
) {
937 // The length of the last line was less than what expat consumed, so
938 // there was at least one line break in the consumed data. Store the
939 // last line until the point where we stopped parsing.
940 nsScannerIterator startLastLine
= currentExpatPosition
;
941 startLastLine
.advance(-((ptrdiff_t)lastLineLength
));
942 if (!CopyUnicodeTo(startLastLine
, currentExpatPosition
, mLastLine
)) {
943 return (mInternalState
= NS_ERROR_OUT_OF_MEMORY
);
946 // There was no line break in the consumed data, append the consumed
948 if (!AppendUnicodeTo(oldExpatPosition
, currentExpatPosition
,
950 return (mInternalState
= NS_ERROR_OUT_OF_MEMORY
);
955 mExpatBuffered
+= length
- consumed
;
957 if (BlockedOrInterrupted()) {
958 MOZ_LOG(gExpatDriverLog
, LogLevel::Debug
,
959 ("Blocked or interrupted parser (probably for loading linked "
960 "stylesheets or scripts)."));
962 aScanner
.SetPosition(currentExpatPosition
, true);
965 return mInternalState
;
968 if (noMoreBuffers
&& mExpatBuffered
== 0) {
969 mMadeFinalCallToExpat
= true;
972 if (NS_FAILED(mInternalState
)) {
973 if (XML_GetErrorCode(mExpatParser
) != XML_ERROR_NONE
) {
974 NS_ASSERTION(mInternalState
== NS_ERROR_HTMLPARSER_STOPPARSING
,
977 // Look for the next newline after the last one we consumed
978 nsScannerIterator lastLine
= currentExpatPosition
;
979 while (lastLine
!= end
) {
980 length
= uint32_t(lastLine
.size_forward());
981 uint32_t endOffset
= 0;
982 const char16_t
* buffer
= lastLine
.get();
983 while (endOffset
< length
&& buffer
[endOffset
] != '\n' &&
984 buffer
[endOffset
] != '\r') {
987 mLastLine
.Append(Substring(buffer
, buffer
+ endOffset
));
988 if (endOffset
< length
) {
989 // We found a newline.
993 lastLine
.advance(length
);
999 return mInternalState
;
1002 // Either we have more buffers, or we were blocked (and we'll flush in the
1003 // next iteration), or we should have emptied Expat's buffer.
1004 NS_ASSERTION(!noMoreBuffers
|| blocked
||
1005 (mExpatBuffered
== 0 && currentExpatPosition
== end
),
1006 "Unreachable data left in Expat's buffer");
1008 start
.advance(length
);
1010 // It's possible for start to have passed end if we received more data
1011 // (e.g. if we spun the event loop in an inline script). Reload end now
1013 aScanner
.EndReading(end
);
1016 aScanner
.SetPosition(currentExpatPosition
, true);
1019 MOZ_LOG(gExpatDriverLog
, LogLevel::Debug
,
1020 ("Remaining in expat's buffer: %i, remaining in scanner: %zu.",
1021 mExpatBuffered
, Distance(currentExpatPosition
, end
)));
1023 return NS_SUCCEEDED(mInternalState
) ? NS_ERROR_HTMLPARSER_EOF
: NS_OK
;
1027 nsExpatDriver::WillBuildModel(const CParserContext
& aParserContext
,
1028 nsITokenizer
* aTokenizer
, nsIContentSink
* aSink
) {
1029 mSink
= do_QueryInterface(aSink
);
1031 NS_ERROR("nsExpatDriver didn't get an nsIExpatSink");
1032 // Make sure future calls to us bail out as needed
1033 mInternalState
= NS_ERROR_UNEXPECTED
;
1034 return mInternalState
;
1037 mOriginalSink
= aSink
;
1039 static const XML_Memory_Handling_Suite memsuite
= {malloc
, realloc
, free
};
1041 static const char16_t kExpatSeparator
[] = {kExpatSeparatorChar
, '\0'};
1043 mExpatParser
= XML_ParserCreate_MM(kUTF16
, &memsuite
, kExpatSeparator
);
1044 NS_ENSURE_TRUE(mExpatParser
, NS_ERROR_FAILURE
);
1046 XML_SetReturnNSTriplet(mExpatParser
, XML_TRUE
);
1049 XML_SetParamEntityParsing(mExpatParser
, XML_PARAM_ENTITY_PARSING_ALWAYS
);
1052 mURISpec
= aParserContext
.mScanner
->GetFilename();
1054 XML_SetBase(mExpatParser
, mURISpec
.get());
1056 nsCOMPtr
<Document
> doc
= do_QueryInterface(mOriginalSink
->GetTarget());
1058 nsCOMPtr
<nsPIDOMWindowOuter
> win
= doc
->GetWindow();
1059 nsCOMPtr
<nsPIDOMWindowInner
> inner
;
1061 inner
= win
->GetCurrentInnerWindow();
1063 bool aHasHadScriptHandlingObject
;
1064 nsIScriptGlobalObject
* global
=
1065 doc
->GetScriptHandlingObject(aHasHadScriptHandlingObject
);
1067 inner
= do_QueryInterface(global
);
1071 mInnerWindowID
= inner
->WindowID();
1075 // Set up the callbacks
1076 XML_SetXmlDeclHandler(mExpatParser
, Driver_HandleXMLDeclaration
);
1077 if (doc
&& doc
->NodePrincipal()->IsSystemPrincipal()) {
1078 XML_SetElementHandler(mExpatParser
, HandleStartElementForSystemPrincipal
,
1079 HandleEndElementForSystemPrincipal
);
1081 XML_SetElementHandler(mExpatParser
, HandleStartElement
, HandleEndElement
);
1083 XML_SetCharacterDataHandler(mExpatParser
, Driver_HandleCharacterData
);
1084 XML_SetProcessingInstructionHandler(mExpatParser
,
1085 Driver_HandleProcessingInstruction
);
1086 XML_SetDefaultHandlerExpand(mExpatParser
, Driver_HandleDefault
);
1087 XML_SetExternalEntityRefHandler(
1089 (XML_ExternalEntityRefHandler
)Driver_HandleExternalEntityRef
);
1090 XML_SetExternalEntityRefHandlerArg(mExpatParser
, this);
1091 XML_SetCommentHandler(mExpatParser
, Driver_HandleComment
);
1092 XML_SetCdataSectionHandler(mExpatParser
, Driver_HandleStartCdataSection
,
1093 Driver_HandleEndCdataSection
);
1095 XML_SetParamEntityParsing(mExpatParser
,
1096 XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE
);
1097 XML_SetDoctypeDeclHandler(mExpatParser
, Driver_HandleStartDoctypeDecl
,
1098 Driver_HandleEndDoctypeDecl
);
1100 // Set up the user data.
1101 XML_SetUserData(mExpatParser
, this);
1103 return mInternalState
;
1107 nsExpatDriver::BuildModel(nsITokenizer
* aTokenizer
, nsIContentSink
* aSink
) {
1108 return mInternalState
;
1112 nsExpatDriver::DidBuildModel(nsresult anErrorCode
) {
1113 mOriginalSink
= nullptr;
1119 nsExpatDriver::WillTokenize(bool aIsFinalChunk
) {
1120 mIsFinalChunk
= aIsFinalChunk
;
1124 NS_IMETHODIMP_(void)
1125 nsExpatDriver::Terminate() {
1126 // XXX - not sure what happens to the unparsed data.
1128 XML_StopParser(mExpatParser
, XML_FALSE
);
1130 mInternalState
= NS_ERROR_HTMLPARSER_STOPPARSING
;
1133 NS_IMETHODIMP_(int32_t)
1134 nsExpatDriver::GetType() { return NS_IPARSER_FLAG_XML
; }
1136 NS_IMETHODIMP_(nsDTDMode
)
1137 nsExpatDriver::GetMode() const { return eDTDMode_full_standards
; }
1139 /*************************** Unused methods **********************************/
1141 NS_IMETHODIMP_(bool)
1142 nsExpatDriver::IsContainer(int32_t aTag
) const { return true; }
1144 NS_IMETHODIMP_(bool)
1145 nsExpatDriver::CanContain(int32_t aParent
, int32_t aChild
) const {
1149 void nsExpatDriver::MaybeStopParser(nsresult aState
) {
1150 if (NS_FAILED(aState
)) {
1151 // If we had a failure we want to override NS_ERROR_HTMLPARSER_INTERRUPTED
1152 // and we want to override NS_ERROR_HTMLPARSER_BLOCK but not with
1153 // NS_ERROR_HTMLPARSER_INTERRUPTED.
1154 if (NS_SUCCEEDED(mInternalState
) ||
1155 mInternalState
== NS_ERROR_HTMLPARSER_INTERRUPTED
||
1156 (mInternalState
== NS_ERROR_HTMLPARSER_BLOCK
&&
1157 aState
!= NS_ERROR_HTMLPARSER_INTERRUPTED
)) {
1158 mInternalState
= (aState
== NS_ERROR_HTMLPARSER_INTERRUPTED
||
1159 aState
== NS_ERROR_HTMLPARSER_BLOCK
)
1161 : NS_ERROR_HTMLPARSER_STOPPARSING
;
1164 // If we get an error then we need to stop Expat (by calling XML_StopParser
1165 // with false as the last argument). If the parser should be blocked or
1166 // interrupted we need to pause Expat (by calling XML_StopParser with
1167 // true as the last argument).
1168 XML_StopParser(mExpatParser
, BlockedOrInterrupted());
1169 } else if (NS_SUCCEEDED(mInternalState
)) {
1170 // Only clobber mInternalState with the success code if we didn't block or
1171 // interrupt before.
1172 mInternalState
= aState
;