Bug 1700051: part 33) Move `AdjustSoftBeginAndBuildSoftText` to `SoftText`. r=smaug
[gecko.git] / parser / htmlparser / nsExpatDriver.cpp
bloba9dd31ae6ec10f1875b405f8fabf6c9ddcc5a5fb
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"
7 #include "nsCOMPtr.h"
8 #include "nsParserCIID.h"
9 #include "CParserContext.h"
10 #include "nsIExpatSink.h"
11 #include "nsIContentSink.h"
12 #include "nsIDocShell.h"
13 #include "nsParserMsgUtils.h"
14 #include "nsIURL.h"
15 #include "nsIUnicharInputStream.h"
16 #include "nsIProtocolHandler.h"
17 #include "nsNetUtil.h"
18 #include "nsTextFormatter.h"
19 #include "nsDirectoryServiceDefs.h"
20 #include "nsCRT.h"
21 #include "nsIConsoleService.h"
22 #include "nsIScriptError.h"
23 #include "nsIScriptGlobalObject.h"
24 #include "nsIContentPolicy.h"
25 #include "nsComponentManagerUtils.h"
26 #include "nsContentPolicyUtils.h"
27 #include "nsError.h"
28 #include "nsXPCOMCIDInternal.h"
29 #include "nsUnicharInputStream.h"
30 #include "nsContentUtils.h"
31 #include "mozilla/BasePrincipal.h"
32 #include "mozilla/IntegerTypeTraits.h"
33 #include "mozilla/NullPrincipal.h"
34 #include "mozilla/Telemetry.h"
35 #include "mozilla/TelemetryComms.h"
37 #include "mozilla/Logging.h"
39 using mozilla::fallible;
40 using mozilla::LogLevel;
41 using mozilla::MakeStringSpan;
42 using mozilla::dom::Document;
44 #define kExpatSeparatorChar 0xFFFF
46 static const char16_t kUTF16[] = {'U', 'T', 'F', '-', '1', '6', '\0'};
48 static mozilla::LazyLogModule gExpatDriverLog("expatdriver");
50 // Use the same maximum tree depth as Chromium (see
51 // https://chromium.googlesource.com/chromium/src/+/f464165c1dedff1c955d3c051c5a9a1c6a0e8f6b/third_party/WebKit/Source/core/xml/parser/XMLDocumentParser.cpp#85).
52 static const uint16_t sMaxXMLTreeDepth = 5000;
54 /***************************** EXPAT CALL BACKS ******************************/
55 // The callback handlers that get called from the expat parser.
57 static void Driver_HandleXMLDeclaration(void* aUserData,
58 const XML_Char* aVersion,
59 const XML_Char* aEncoding,
60 int aStandalone) {
61 NS_ASSERTION(aUserData, "expat driver should exist");
62 if (aUserData) {
63 nsExpatDriver* driver = static_cast<nsExpatDriver*>(aUserData);
64 driver->HandleXMLDeclaration(aVersion, aEncoding, aStandalone);
68 static void Driver_HandleCharacterData(void* aUserData, const XML_Char* aData,
69 int aLength) {
70 NS_ASSERTION(aUserData, "expat driver should exist");
71 if (aUserData) {
72 nsExpatDriver* driver = static_cast<nsExpatDriver*>(aUserData);
73 driver->HandleCharacterData(aData, uint32_t(aLength));
77 static void Driver_HandleComment(void* aUserData, const XML_Char* aName) {
78 NS_ASSERTION(aUserData, "expat driver should exist");
79 if (aUserData) {
80 static_cast<nsExpatDriver*>(aUserData)->HandleComment(aName);
84 static void Driver_HandleProcessingInstruction(void* aUserData,
85 const XML_Char* aTarget,
86 const XML_Char* aData) {
87 NS_ASSERTION(aUserData, "expat driver should exist");
88 if (aUserData) {
89 nsExpatDriver* driver = static_cast<nsExpatDriver*>(aUserData);
90 driver->HandleProcessingInstruction(aTarget, aData);
94 static void Driver_HandleDefault(void* aUserData, const XML_Char* aData,
95 int aLength) {
96 NS_ASSERTION(aUserData, "expat driver should exist");
97 if (aUserData) {
98 nsExpatDriver* driver = static_cast<nsExpatDriver*>(aUserData);
99 driver->HandleDefault(aData, uint32_t(aLength));
103 static void Driver_HandleStartCdataSection(void* aUserData) {
104 NS_ASSERTION(aUserData, "expat driver should exist");
105 if (aUserData) {
106 static_cast<nsExpatDriver*>(aUserData)->HandleStartCdataSection();
110 static void Driver_HandleEndCdataSection(void* aUserData) {
111 NS_ASSERTION(aUserData, "expat driver should exist");
112 if (aUserData) {
113 static_cast<nsExpatDriver*>(aUserData)->HandleEndCdataSection();
117 static void Driver_HandleStartDoctypeDecl(void* aUserData,
118 const XML_Char* aDoctypeName,
119 const XML_Char* aSysid,
120 const XML_Char* aPubid,
121 int aHasInternalSubset) {
122 NS_ASSERTION(aUserData, "expat driver should exist");
123 if (aUserData) {
124 static_cast<nsExpatDriver*>(aUserData)->HandleStartDoctypeDecl(
125 aDoctypeName, aSysid, aPubid, !!aHasInternalSubset);
129 static void Driver_HandleEndDoctypeDecl(void* aUserData) {
130 NS_ASSERTION(aUserData, "expat driver should exist");
131 if (aUserData) {
132 static_cast<nsExpatDriver*>(aUserData)->HandleEndDoctypeDecl();
136 static int Driver_HandleExternalEntityRef(void* aExternalEntityRefHandler,
137 const XML_Char* aOpenEntityNames,
138 const XML_Char* aBase,
139 const XML_Char* aSystemId,
140 const XML_Char* aPublicId) {
141 NS_ASSERTION(aExternalEntityRefHandler, "expat driver should exist");
142 if (!aExternalEntityRefHandler) {
143 return 1;
146 nsExpatDriver* driver =
147 static_cast<nsExpatDriver*>(aExternalEntityRefHandler);
149 return driver->HandleExternalEntityRef(aOpenEntityNames, aBase, aSystemId,
150 aPublicId);
153 /***************************** END CALL BACKS ********************************/
155 /***************************** CATALOG UTILS *********************************/
157 // Initially added for bug 113400 to switch from the remote "XHTML 1.0 plus
158 // MathML 2.0" DTD to the the lightweight customized version that Mozilla uses.
159 // Since Mozilla is not validating, no need to fetch a *huge* file at each
160 // click.
161 // XXX The cleanest solution here would be to fix Bug 98413: Implement XML
162 // Catalogs.
163 struct nsCatalogData {
164 const char* mPublicID;
165 const char* mLocalDTD;
166 const char* mAgentSheet;
169 // The order of this table is guestimated to be in the optimum order
170 static const nsCatalogData kCatalogTable[] = {
171 {"-//W3C//DTD XHTML 1.0 Transitional//EN", "htmlmathml-f.ent", nullptr},
172 {"-//W3C//DTD XHTML 1.1//EN", "htmlmathml-f.ent", nullptr},
173 {"-//W3C//DTD XHTML 1.0 Strict//EN", "htmlmathml-f.ent", nullptr},
174 {"-//W3C//DTD XHTML 1.0 Frameset//EN", "htmlmathml-f.ent", nullptr},
175 {"-//W3C//DTD XHTML Basic 1.0//EN", "htmlmathml-f.ent", nullptr},
176 {"-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN", "htmlmathml-f.ent", nullptr},
177 {"-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN",
178 "htmlmathml-f.ent", nullptr},
179 {"-//W3C//DTD MathML 2.0//EN", "htmlmathml-f.ent", nullptr},
180 {"-//WAPFORUM//DTD XHTML Mobile 1.0//EN", "htmlmathml-f.ent", nullptr},
181 {nullptr, nullptr, nullptr}};
183 static const nsCatalogData* LookupCatalogData(const char16_t* aPublicID) {
184 nsDependentString publicID(aPublicID);
186 // linear search for now since the number of entries is going to
187 // be negligible, and the fix for bug 98413 would get rid of this
188 // code anyway
189 const nsCatalogData* data = kCatalogTable;
190 while (data->mPublicID) {
191 if (publicID.EqualsASCII(data->mPublicID)) {
192 return data;
194 ++data;
197 return nullptr;
200 // This function provides a resource URI to a local DTD
201 // in resource://gre/res/dtd/ which may or may not exist.
202 // If aCatalogData is provided, it is used to remap the
203 // DTD instead of taking the filename from the URI. aDTD
204 // may be null in some cases that are relying on
205 // aCatalogData working for them.
206 static void GetLocalDTDURI(const nsCatalogData* aCatalogData, nsIURI* aDTD,
207 nsIURI** aResult) {
208 nsAutoCString fileName;
209 if (aCatalogData) {
210 // remap the DTD to a known local DTD
211 fileName.Assign(aCatalogData->mLocalDTD);
214 if (fileName.IsEmpty()) {
215 // Try to see if the user has installed the DTD file -- we extract the
216 // filename.ext of the DTD here. Hence, for any DTD for which we have
217 // no predefined mapping, users just have to copy the DTD file to our
218 // special DTD directory and it will be picked.
219 nsCOMPtr<nsIURL> dtdURL = do_QueryInterface(aDTD);
220 if (!dtdURL) {
221 // Not a URL with a filename, or maybe it was null. Either way, nothing
222 // else we can do here.
223 return;
226 dtdURL->GetFileName(fileName);
227 if (fileName.IsEmpty()) {
228 return;
232 nsAutoCString respath("resource://gre/res/dtd/");
233 respath += fileName;
234 NS_NewURI(aResult, respath);
237 /***************************** END CATALOG UTILS *****************************/
239 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsExpatDriver)
240 NS_INTERFACE_MAP_ENTRY(nsITokenizer)
241 NS_INTERFACE_MAP_ENTRY(nsIDTD)
242 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDTD)
243 NS_INTERFACE_MAP_END
245 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsExpatDriver)
246 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsExpatDriver)
248 NS_IMPL_CYCLE_COLLECTION(nsExpatDriver, mSink)
250 nsExpatDriver::nsExpatDriver()
251 : mExpatParser(nullptr),
252 mInCData(false),
253 mInInternalSubset(false),
254 mInExternalDTD(false),
255 mMadeFinalCallToExpat(false),
256 mIsFinalChunk(false),
257 mInternalState(NS_OK),
258 mExpatBuffered(0),
259 mTagDepth(0),
260 mCatalogData(nullptr),
261 mInnerWindowID(0) {}
263 nsExpatDriver::~nsExpatDriver() {
264 if (mExpatParser) {
265 XML_ParserFree(mExpatParser);
269 /* static */
270 void nsExpatDriver::HandleStartElement(void* aUserData, const char16_t* aName,
271 const char16_t** aAtts) {
272 nsExpatDriver* self = static_cast<nsExpatDriver*>(aUserData);
274 NS_ASSERTION(self->mSink, "content sink not found!");
276 // Calculate the total number of elements in aAtts.
277 // XML_GetSpecifiedAttributeCount will only give us the number of specified
278 // attrs (twice that number, actually), so we have to check for default attrs
279 // ourselves.
280 uint32_t attrArrayLength;
281 for (attrArrayLength = XML_GetSpecifiedAttributeCount(self->mExpatParser);
282 aAtts[attrArrayLength]; attrArrayLength += 2) {
283 // Just looping till we find out what the length is
286 if (self->mSink) {
287 // We store the tagdepth in a PRUint16, so make sure the limit fits in a
288 // PRUint16.
289 static_assert(
290 sMaxXMLTreeDepth <=
291 std::numeric_limits<decltype(nsExpatDriver::mTagDepth)>::max());
293 if (++self->mTagDepth > sMaxXMLTreeDepth) {
294 self->MaybeStopParser(NS_ERROR_HTMLPARSER_HIERARCHYTOODEEP);
295 return;
298 nsresult rv = self->mSink->HandleStartElement(
299 aName, aAtts, attrArrayLength,
300 XML_GetCurrentLineNumber(self->mExpatParser),
301 XML_GetCurrentColumnNumber(self->mExpatParser));
302 self->MaybeStopParser(rv);
306 /* static */
307 void nsExpatDriver::HandleStartElementForSystemPrincipal(
308 void* aUserData, const char16_t* aName, const char16_t** aAtts) {
309 nsExpatDriver* self = static_cast<nsExpatDriver*>(aUserData);
311 if (!MOZ_XML_ProcessingEntityValue(self->mExpatParser)) {
312 HandleStartElement(aUserData, aName, aAtts);
313 } else {
314 nsCOMPtr<Document> doc =
315 do_QueryInterface(self->mOriginalSink->GetTarget());
317 // Adjust the column number so that it is one based rather than zero based.
318 uint32_t colNumber = XML_GetCurrentColumnNumber(self->mExpatParser) + 1;
319 uint32_t lineNumber = XML_GetCurrentLineNumber(self->mExpatParser);
321 int32_t nameSpaceID;
322 RefPtr<nsAtom> prefix, localName;
323 nsContentUtils::SplitExpatName(aName, getter_AddRefs(prefix),
324 getter_AddRefs(localName), &nameSpaceID);
326 nsAutoString error;
327 error.AppendLiteral("Ignoring element <");
328 if (prefix) {
329 error.Append(prefix->GetUTF16String());
330 error.Append(':');
332 error.Append(localName->GetUTF16String());
333 error.AppendLiteral("> created from entity value.");
335 nsContentUtils::ReportToConsoleNonLocalized(
336 error, nsIScriptError::warningFlag, "XML Document"_ns, doc, nullptr,
337 u""_ns, lineNumber, colNumber);
341 /* static */
342 void nsExpatDriver::HandleEndElement(void* aUserData, const char16_t* aName) {
343 nsExpatDriver* self = static_cast<nsExpatDriver*>(aUserData);
345 NS_ASSERTION(self->mSink, "content sink not found!");
346 NS_ASSERTION(self->mInternalState != NS_ERROR_HTMLPARSER_BLOCK,
347 "Shouldn't block from HandleStartElement.");
349 if (self->mSink && self->mInternalState != NS_ERROR_HTMLPARSER_STOPPARSING) {
350 nsresult rv = self->mSink->HandleEndElement(aName);
351 --self->mTagDepth;
352 self->MaybeStopParser(rv);
356 /* static */
357 void nsExpatDriver::HandleEndElementForSystemPrincipal(void* aUserData,
358 const char16_t* aName) {
359 nsExpatDriver* self = static_cast<nsExpatDriver*>(aUserData);
361 if (!MOZ_XML_ProcessingEntityValue(self->mExpatParser)) {
362 HandleEndElement(aUserData, aName);
366 nsresult nsExpatDriver::HandleCharacterData(const char16_t* aValue,
367 const uint32_t aLength) {
368 NS_ASSERTION(mSink, "content sink not found!");
370 if (mInCData) {
371 if (!mCDataText.Append(aValue, aLength, fallible)) {
372 MaybeStopParser(NS_ERROR_OUT_OF_MEMORY);
374 } else if (mSink) {
375 nsresult rv = mSink->HandleCharacterData(aValue, aLength);
376 MaybeStopParser(rv);
379 return NS_OK;
382 nsresult nsExpatDriver::HandleComment(const char16_t* aValue) {
383 NS_ASSERTION(mSink, "content sink not found!");
385 if (mInExternalDTD) {
386 // Ignore comments from external DTDs
387 return NS_OK;
390 if (mInInternalSubset) {
391 mInternalSubset.AppendLiteral("<!--");
392 mInternalSubset.Append(aValue);
393 mInternalSubset.AppendLiteral("-->");
394 } else if (mSink) {
395 nsresult rv = mSink->HandleComment(aValue);
396 MaybeStopParser(rv);
399 return NS_OK;
402 nsresult nsExpatDriver::HandleProcessingInstruction(const char16_t* aTarget,
403 const char16_t* aData) {
404 NS_ASSERTION(mSink, "content sink not found!");
406 if (mInExternalDTD) {
407 // Ignore PIs in external DTDs for now. Eventually we want to
408 // pass them to the sink in a way that doesn't put them in the DOM
409 return NS_OK;
412 if (mInInternalSubset) {
413 mInternalSubset.AppendLiteral("<?");
414 mInternalSubset.Append(aTarget);
415 mInternalSubset.Append(' ');
416 mInternalSubset.Append(aData);
417 mInternalSubset.AppendLiteral("?>");
418 } else if (mSink) {
419 nsresult rv = mSink->HandleProcessingInstruction(aTarget, aData);
420 MaybeStopParser(rv);
423 return NS_OK;
426 nsresult nsExpatDriver::HandleXMLDeclaration(const char16_t* aVersion,
427 const char16_t* aEncoding,
428 int32_t aStandalone) {
429 if (mSink) {
430 nsresult rv = mSink->HandleXMLDeclaration(aVersion, aEncoding, aStandalone);
431 MaybeStopParser(rv);
434 return NS_OK;
437 nsresult nsExpatDriver::HandleDefault(const char16_t* aValue,
438 const uint32_t aLength) {
439 NS_ASSERTION(mSink, "content sink not found!");
441 if (mInExternalDTD) {
442 // Ignore newlines in external DTDs
443 return NS_OK;
446 if (mInInternalSubset) {
447 mInternalSubset.Append(aValue, aLength);
448 } else if (mSink) {
449 uint32_t i;
450 nsresult rv = mInternalState;
451 for (i = 0; i < aLength && NS_SUCCEEDED(rv); ++i) {
452 if (aValue[i] == '\n' || aValue[i] == '\r') {
453 rv = mSink->HandleCharacterData(&aValue[i], 1);
456 MaybeStopParser(rv);
459 return NS_OK;
462 nsresult nsExpatDriver::HandleStartCdataSection() {
463 mInCData = true;
465 return NS_OK;
468 nsresult nsExpatDriver::HandleEndCdataSection() {
469 NS_ASSERTION(mSink, "content sink not found!");
471 mInCData = false;
472 if (mSink) {
473 nsresult rv =
474 mSink->HandleCDataSection(mCDataText.get(), mCDataText.Length());
475 MaybeStopParser(rv);
477 mCDataText.Truncate();
479 return NS_OK;
482 nsresult nsExpatDriver::HandleStartDoctypeDecl(const char16_t* aDoctypeName,
483 const char16_t* aSysid,
484 const char16_t* aPubid,
485 bool aHasInternalSubset) {
486 mDoctypeName = aDoctypeName;
487 mSystemID = aSysid;
488 mPublicID = aPubid;
490 if (aHasInternalSubset) {
491 // Consuming a huge internal subset translates to numerous
492 // allocations. In an effort to avoid too many allocations
493 // setting mInternalSubset's capacity to be 1K ( just a guesstimate! ).
494 mInInternalSubset = true;
495 mInternalSubset.SetCapacity(1024);
496 } else {
497 // Distinguish missing internal subset from an empty one
498 mInternalSubset.SetIsVoid(true);
501 return NS_OK;
504 nsresult nsExpatDriver::HandleEndDoctypeDecl() {
505 NS_ASSERTION(mSink, "content sink not found!");
507 mInInternalSubset = false;
509 if (mSink) {
510 // let the sink know any additional knowledge that we have about the
511 // document (currently, from bug 124570, we only expect to pass additional
512 // agent sheets needed to layout the XML vocabulary of the document)
513 nsCOMPtr<nsIURI> data;
514 #if 0
515 if (mCatalogData && mCatalogData->mAgentSheet) {
516 NS_NewURI(getter_AddRefs(data), mCatalogData->mAgentSheet);
518 #endif
520 // The unused support for "catalog style sheets" was removed. It doesn't
521 // look like we'll ever fix bug 98413 either.
522 MOZ_ASSERT(!mCatalogData || !mCatalogData->mAgentSheet,
523 "Need to add back support for catalog style sheets");
525 // Note: mInternalSubset already doesn't include the [] around it.
526 nsresult rv = mSink->HandleDoctypeDecl(mInternalSubset, mDoctypeName,
527 mSystemID, mPublicID, data);
528 MaybeStopParser(rv);
531 mInternalSubset.Truncate();
533 return NS_OK;
536 static nsresult ExternalDTDStreamReaderFunc(nsIUnicharInputStream* aIn,
537 void* aClosure,
538 const char16_t* aFromSegment,
539 uint32_t aToOffset, uint32_t aCount,
540 uint32_t* aWriteCount) {
541 // Pass the buffer to expat for parsing.
542 if (XML_Parse((XML_Parser)aClosure, (const char*)aFromSegment,
543 aCount * sizeof(char16_t), 0) == XML_STATUS_OK) {
544 *aWriteCount = aCount;
546 return NS_OK;
549 *aWriteCount = 0;
551 return NS_ERROR_FAILURE;
554 int nsExpatDriver::HandleExternalEntityRef(const char16_t* openEntityNames,
555 const char16_t* base,
556 const char16_t* systemId,
557 const char16_t* publicId) {
558 if (mInInternalSubset && !mInExternalDTD && openEntityNames) {
559 mInternalSubset.Append(char16_t('%'));
560 mInternalSubset.Append(nsDependentString(openEntityNames));
561 mInternalSubset.Append(char16_t(';'));
564 // Load the external entity into a buffer.
565 nsCOMPtr<nsIInputStream> in;
566 nsAutoString absURL;
567 nsresult rv = OpenInputStreamFromExternalDTD(publicId, systemId, base,
568 getter_AddRefs(in), absURL);
569 if (NS_FAILED(rv)) {
570 #ifdef DEBUG
571 nsCString message("Failed to open external DTD: publicId \"");
572 AppendUTF16toUTF8(MakeStringSpan(publicId), message);
573 message += "\" systemId \"";
574 AppendUTF16toUTF8(MakeStringSpan(systemId), message);
575 message += "\" base \"";
576 AppendUTF16toUTF8(MakeStringSpan(base), message);
577 message += "\" URL \"";
578 AppendUTF16toUTF8(absURL, message);
579 message += "\"";
580 NS_WARNING(message.get());
581 #endif
582 return 1;
585 nsCOMPtr<nsIUnicharInputStream> uniIn;
586 rv = NS_NewUnicharInputStream(in, getter_AddRefs(uniIn));
587 NS_ENSURE_SUCCESS(rv, 1);
589 int result = 1;
590 if (uniIn) {
591 XML_Parser entParser =
592 XML_ExternalEntityParserCreate(mExpatParser, 0, kUTF16);
593 if (entParser) {
594 XML_SetBase(entParser, absURL.get());
596 mInExternalDTD = true;
598 uint32_t totalRead;
599 do {
600 rv = uniIn->ReadSegments(ExternalDTDStreamReaderFunc, entParser,
601 uint32_t(-1), &totalRead);
602 } while (NS_SUCCEEDED(rv) && totalRead > 0);
604 result = XML_Parse(entParser, nullptr, 0, 1);
606 mInExternalDTD = false;
608 XML_ParserFree(entParser);
612 return result;
615 nsresult nsExpatDriver::OpenInputStreamFromExternalDTD(const char16_t* aFPIStr,
616 const char16_t* aURLStr,
617 const char16_t* aBaseURL,
618 nsIInputStream** aStream,
619 nsAString& aAbsURL) {
620 nsCOMPtr<nsIURI> baseURI;
621 nsresult rv =
622 NS_NewURI(getter_AddRefs(baseURI), NS_ConvertUTF16toUTF8(aBaseURL));
623 NS_ENSURE_SUCCESS(rv, rv);
625 nsCOMPtr<nsIURI> uri;
626 rv = NS_NewURI(getter_AddRefs(uri), NS_ConvertUTF16toUTF8(aURLStr), nullptr,
627 baseURI);
628 // Even if the URI is malformed (most likely because we have a
629 // non-hierarchical base URI and a relative DTD URI, with the latter
630 // being the normal XHTML DTD case), we can try to see whether we
631 // have catalog data for aFPIStr.
632 if (NS_WARN_IF(NS_FAILED(rv) && rv != NS_ERROR_MALFORMED_URI)) {
633 return rv;
636 // make sure the URI, if we have one, is allowed to be loaded in sync
637 bool isUIResource = false;
638 if (uri) {
639 rv = NS_URIChainHasFlags(uri, nsIProtocolHandler::URI_IS_UI_RESOURCE,
640 &isUIResource);
641 NS_ENSURE_SUCCESS(rv, rv);
644 nsCOMPtr<nsIURI> localURI;
645 if (!isUIResource) {
646 // Check to see if we can map the DTD to a known local DTD, or if a DTD
647 // file of the same name exists in the special DTD directory
648 if (aFPIStr) {
649 // see if the Formal Public Identifier (FPI) maps to a catalog entry
650 mCatalogData = LookupCatalogData(aFPIStr);
651 GetLocalDTDURI(mCatalogData, uri, getter_AddRefs(localURI));
653 if (!localURI) {
654 return NS_ERROR_NOT_IMPLEMENTED;
658 nsCOMPtr<nsIChannel> channel;
659 if (localURI) {
660 localURI.swap(uri);
661 rv = NS_NewChannel(getter_AddRefs(channel), uri,
662 nsContentUtils::GetSystemPrincipal(),
663 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
664 nsIContentPolicy::TYPE_DTD);
665 NS_ENSURE_SUCCESS(rv, rv);
666 } else {
667 NS_ASSERTION(
668 mSink == nsCOMPtr<nsIExpatSink>(do_QueryInterface(mOriginalSink)),
669 "In nsExpatDriver::OpenInputStreamFromExternalDTD: "
670 "mOriginalSink not the same object as mSink?");
671 nsContentPolicyType policyType = nsIContentPolicy::TYPE_INTERNAL_DTD;
672 if (mOriginalSink) {
673 nsCOMPtr<Document> doc;
674 doc = do_QueryInterface(mOriginalSink->GetTarget());
675 if (doc) {
676 if (doc->SkipDTDSecurityChecks()) {
677 policyType = nsIContentPolicy::TYPE_INTERNAL_FORCE_ALLOWED_DTD;
679 rv = NS_NewChannel(
680 getter_AddRefs(channel), uri, doc,
681 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT |
682 nsILoadInfo::SEC_ALLOW_CHROME,
683 policyType);
684 NS_ENSURE_SUCCESS(rv, rv);
687 if (!channel) {
688 nsCOMPtr<nsIPrincipal> nullPrincipal =
689 mozilla::NullPrincipal::CreateWithoutOriginAttributes();
690 rv = NS_NewChannel(
691 getter_AddRefs(channel), uri, nullPrincipal,
692 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT |
693 nsILoadInfo::SEC_ALLOW_CHROME,
694 policyType);
695 NS_ENSURE_SUCCESS(rv, rv);
699 nsAutoCString absURL;
700 rv = uri->GetSpec(absURL);
701 NS_ENSURE_SUCCESS(rv, rv);
702 CopyUTF8toUTF16(absURL, aAbsURL);
704 channel->SetContentType("application/xml"_ns);
705 return channel->Open(aStream);
708 static nsresult CreateErrorText(const char16_t* aDescription,
709 const char16_t* aSourceURL,
710 const uint32_t aLineNumber,
711 const uint32_t aColNumber,
712 nsString& aErrorString, bool spoofEnglish) {
713 aErrorString.Truncate();
715 nsAutoString msg;
716 nsresult rv = nsParserMsgUtils::GetLocalizedStringByName(
717 spoofEnglish ? XMLPARSER_PROPERTIES_en_US : XMLPARSER_PROPERTIES,
718 "XMLParsingError", msg);
719 NS_ENSURE_SUCCESS(rv, rv);
721 // XML Parsing Error: %1$S\nLocation: %2$S\nLine Number %3$u, Column %4$u:
722 nsTextFormatter::ssprintf(aErrorString, msg.get(), aDescription, aSourceURL,
723 aLineNumber, aColNumber);
724 return NS_OK;
727 static nsresult AppendErrorPointer(const int32_t aColNumber,
728 const char16_t* aSourceLine,
729 nsString& aSourceString) {
730 aSourceString.Append(char16_t('\n'));
732 // Last character will be '^'.
733 int32_t last = aColNumber - 1;
734 int32_t i;
735 uint32_t minuses = 0;
736 for (i = 0; i < last; ++i) {
737 if (aSourceLine[i] == '\t') {
738 // Since this uses |white-space: pre;| a tab stop equals 8 spaces.
739 uint32_t add = 8 - (minuses % 8);
740 aSourceString.AppendASCII("--------", add);
741 minuses += add;
742 } else {
743 aSourceString.Append(char16_t('-'));
744 ++minuses;
747 aSourceString.Append(char16_t('^'));
749 return NS_OK;
752 nsresult nsExpatDriver::HandleError() {
753 int32_t code = XML_GetErrorCode(mExpatParser);
754 NS_ASSERTION(code > XML_ERROR_NONE, "unexpected XML error code");
756 // Map Expat error code to an error string
757 // XXX Deal with error returns.
758 nsAutoString description;
759 nsCOMPtr<Document> doc;
760 if (mOriginalSink) {
761 doc = do_QueryInterface(mOriginalSink->GetTarget());
764 bool spoofEnglish =
765 nsContentUtils::SpoofLocaleEnglish() && (!doc || !doc->AllowsL10n());
766 nsParserMsgUtils::GetLocalizedStringByID(
767 spoofEnglish ? XMLPARSER_PROPERTIES_en_US : XMLPARSER_PROPERTIES, code,
768 description);
770 if (code == XML_ERROR_TAG_MISMATCH) {
772 * Expat can send the following:
773 * localName
774 * namespaceURI<separator>localName
775 * namespaceURI<separator>localName<separator>prefix
777 * and we use 0xFFFF for the <separator>.
780 const char16_t* mismatch = MOZ_XML_GetMismatchedTag(mExpatParser);
781 const char16_t* uriEnd = nullptr;
782 const char16_t* nameEnd = nullptr;
783 const char16_t* pos;
784 for (pos = mismatch; *pos; ++pos) {
785 if (*pos == kExpatSeparatorChar) {
786 if (uriEnd) {
787 nameEnd = pos;
788 } else {
789 uriEnd = pos;
794 nsAutoString tagName;
795 if (uriEnd && nameEnd) {
796 // We have a prefix.
797 tagName.Append(nameEnd + 1, pos - nameEnd - 1);
798 tagName.Append(char16_t(':'));
800 const char16_t* nameStart = uriEnd ? uriEnd + 1 : mismatch;
801 tagName.Append(nameStart, (nameEnd ? nameEnd : pos) - nameStart);
803 nsAutoString msg;
804 nsParserMsgUtils::GetLocalizedStringByName(
805 spoofEnglish ? XMLPARSER_PROPERTIES_en_US : XMLPARSER_PROPERTIES,
806 "Expected", msg);
808 // . Expected: </%S>.
809 nsAutoString message;
810 nsTextFormatter::ssprintf(message, msg.get(), tagName.get());
811 description.Append(message);
814 // Adjust the column number so that it is one based rather than zero based.
815 uint32_t colNumber = XML_GetCurrentColumnNumber(mExpatParser) + 1;
816 uint32_t lineNumber = XML_GetCurrentLineNumber(mExpatParser);
818 nsAutoString errorText;
819 CreateErrorText(description.get(), XML_GetBase(mExpatParser), lineNumber,
820 colNumber, errorText, spoofEnglish);
822 nsAutoString sourceText(mLastLine);
823 AppendErrorPointer(colNumber, mLastLine.get(), sourceText);
825 if (doc && nsContentUtils::IsChromeDoc(doc)) {
826 nsCString path = doc->GetDocumentURI()->GetSpecOrDefault();
827 nsCOMPtr<nsISupports> container = doc->GetContainer();
828 nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(container);
829 nsCString docShellDestroyed("unknown"_ns);
830 if (docShell) {
831 bool destroyed = false;
832 docShell->IsBeingDestroyed(&destroyed);
833 docShellDestroyed.Assign(destroyed ? "true"_ns : "false"_ns);
836 mozilla::Maybe<nsTArray<mozilla::Telemetry::EventExtraEntry>> extra =
837 mozilla::Some<nsTArray<mozilla::Telemetry::EventExtraEntry>>({
838 mozilla::Telemetry::EventExtraEntry{"error_code"_ns,
839 nsPrintfCString("%u", code)},
840 mozilla::Telemetry::EventExtraEntry{
841 "location"_ns, nsPrintfCString("%u:%u", lineNumber, colNumber)},
842 mozilla::Telemetry::EventExtraEntry{
843 "last_line"_ns, NS_ConvertUTF16toUTF8(mLastLine)},
844 mozilla::Telemetry::EventExtraEntry{
845 "last_line_len"_ns, nsPrintfCString("%u", mLastLine.Length())},
846 mozilla::Telemetry::EventExtraEntry{
847 "hidden"_ns, doc->Hidden() ? "true"_ns : "false"_ns},
848 mozilla::Telemetry::EventExtraEntry{"destroyed"_ns,
849 docShellDestroyed},
852 mozilla::Telemetry::SetEventRecordingEnabled("ysod"_ns, true);
853 mozilla::Telemetry::RecordEvent(
854 mozilla::Telemetry::EventID::Ysod_Shown_Ysod, mozilla::Some(path),
855 extra);
858 // Try to create and initialize the script error.
859 nsCOMPtr<nsIScriptError> serr(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
860 nsresult rv = NS_ERROR_FAILURE;
861 if (serr) {
862 rv = serr->InitWithWindowID(errorText, mURISpec, mLastLine, lineNumber,
863 colNumber, nsIScriptError::errorFlag,
864 "malformed-xml", mInnerWindowID);
867 // If it didn't initialize, we can't do any logging.
868 bool shouldReportError = NS_SUCCEEDED(rv);
870 // mSink might be null here if our parser was terminated.
871 if (mSink && shouldReportError) {
872 rv = mSink->ReportError(errorText.get(), sourceText.get(), serr,
873 &shouldReportError);
874 if (NS_FAILED(rv)) {
875 shouldReportError = true;
879 // mOriginalSink might be null here if our parser was terminated.
880 if (mOriginalSink) {
881 nsCOMPtr<Document> doc = do_QueryInterface(mOriginalSink->GetTarget());
882 if (doc && doc->SuppressParserErrorConsoleMessages()) {
883 shouldReportError = false;
887 if (shouldReportError) {
888 nsCOMPtr<nsIConsoleService> cs(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
889 if (cs) {
890 cs->LogMessage(serr);
894 return NS_ERROR_HTMLPARSER_STOPPARSING;
897 void nsExpatDriver::ParseBuffer(const char16_t* aBuffer, uint32_t aLength,
898 bool aIsFinal, uint32_t* aConsumed) {
899 NS_ASSERTION((aBuffer && aLength != 0) || (!aBuffer && aLength == 0), "?");
900 NS_ASSERTION(mInternalState != NS_OK || aIsFinal || aBuffer,
901 "Useless call, we won't call Expat");
902 MOZ_ASSERT(!BlockedOrInterrupted() || !aBuffer,
903 "Non-null buffer when resuming");
904 MOZ_ASSERT(XML_GetCurrentByteIndex(mExpatParser) % sizeof(char16_t) == 0,
905 "Consumed part of a char16_t?");
907 if (mExpatParser && (mInternalState == NS_OK || BlockedOrInterrupted())) {
908 int32_t parserBytesBefore = XML_GetCurrentByteIndex(mExpatParser);
909 NS_ASSERTION(parserBytesBefore >= 0, "Unexpected value");
911 XML_Status status;
912 if (BlockedOrInterrupted()) {
913 mInternalState = NS_OK; // Resume in case we're blocked.
914 status = XML_ResumeParser(mExpatParser);
915 } else {
916 status = XML_Parse(mExpatParser, reinterpret_cast<const char*>(aBuffer),
917 aLength * sizeof(char16_t), aIsFinal);
920 int32_t parserBytesConsumed = XML_GetCurrentByteIndex(mExpatParser);
922 NS_ASSERTION(parserBytesConsumed >= 0, "Unexpected value");
923 NS_ASSERTION(parserBytesConsumed >= parserBytesBefore,
924 "How'd this happen?");
925 NS_ASSERTION(parserBytesConsumed % sizeof(char16_t) == 0,
926 "Consumed part of a char16_t?");
928 // Consumed something.
929 *aConsumed = (parserBytesConsumed - parserBytesBefore) / sizeof(char16_t);
930 NS_ASSERTION(*aConsumed <= aLength + mExpatBuffered,
931 "Too many bytes consumed?");
933 NS_ASSERTION(status != XML_STATUS_SUSPENDED || BlockedOrInterrupted(),
934 "Inconsistent expat suspension state.");
936 if (status == XML_STATUS_ERROR) {
937 mInternalState = NS_ERROR_HTMLPARSER_STOPPARSING;
939 } else {
940 *aConsumed = 0;
944 NS_IMETHODIMP
945 nsExpatDriver::ConsumeToken(nsScanner& aScanner, bool& aFlushTokens) {
946 // We keep the scanner pointing to the position where Expat will start
947 // parsing.
948 nsScannerIterator currentExpatPosition;
949 aScanner.CurrentPosition(currentExpatPosition);
951 // This is the start of the first buffer that we need to pass to Expat.
952 nsScannerIterator start = currentExpatPosition;
953 start.advance(mExpatBuffered);
955 // This is the end of the last buffer (at this point, more data could come in
956 // later).
957 nsScannerIterator end;
958 aScanner.EndReading(end);
960 MOZ_LOG(gExpatDriverLog, LogLevel::Debug,
961 ("Remaining in expat's buffer: %i, remaining in scanner: %zu.",
962 mExpatBuffered, Distance(start, end)));
964 // We want to call Expat if we have more buffers, or if we know there won't
965 // be more buffers (and so we want to flush the remaining data), or if we're
966 // currently blocked and there's data in Expat's buffer.
967 while (start != end || (mIsFinalChunk && !mMadeFinalCallToExpat) ||
968 (BlockedOrInterrupted() && mExpatBuffered > 0)) {
969 bool noMoreBuffers = start == end && mIsFinalChunk;
970 bool blocked = BlockedOrInterrupted();
972 const char16_t* buffer;
973 uint32_t length;
974 if (blocked || noMoreBuffers) {
975 // If we're blocked we just resume Expat so we don't need a buffer, if
976 // there aren't any more buffers we pass a null buffer to Expat.
977 buffer = nullptr;
978 length = 0;
980 if (blocked) {
981 MOZ_LOG(
982 gExpatDriverLog, LogLevel::Debug,
983 ("Resuming Expat, will parse data remaining in Expat's "
984 "buffer.\nContent of Expat's buffer:\n-----\n%s\n-----\n",
985 NS_ConvertUTF16toUTF8(currentExpatPosition.get(), mExpatBuffered)
986 .get()));
987 } else {
988 NS_ASSERTION(mExpatBuffered == Distance(currentExpatPosition, end),
989 "Didn't pass all the data to Expat?");
990 MOZ_LOG(
991 gExpatDriverLog, LogLevel::Debug,
992 ("Last call to Expat, will parse data remaining in Expat's "
993 "buffer.\nContent of Expat's buffer:\n-----\n%s\n-----\n",
994 NS_ConvertUTF16toUTF8(currentExpatPosition.get(), mExpatBuffered)
995 .get()));
997 } else {
998 buffer = start.get();
999 length = uint32_t(start.size_forward());
1001 MOZ_LOG(gExpatDriverLog, LogLevel::Debug,
1002 ("Calling Expat, will parse data remaining in Expat's buffer and "
1003 "new data.\nContent of Expat's buffer:\n-----\n%s\n-----\nNew "
1004 "data:\n-----\n%s\n-----\n",
1005 NS_ConvertUTF16toUTF8(currentExpatPosition.get(), mExpatBuffered)
1006 .get(),
1007 NS_ConvertUTF16toUTF8(start.get(), length).get()));
1010 uint32_t consumed;
1011 ParseBuffer(buffer, length, noMoreBuffers, &consumed);
1012 if (consumed > 0) {
1013 nsScannerIterator oldExpatPosition = currentExpatPosition;
1014 currentExpatPosition.advance(consumed);
1016 // We consumed some data, we want to store the last line of data that
1017 // was consumed in case we run into an error (to show the line in which
1018 // the error occurred).
1020 // The length of the last line that Expat has parsed.
1021 XML_Size lastLineLength = XML_GetCurrentColumnNumber(mExpatParser);
1023 if (lastLineLength <= consumed) {
1024 // The length of the last line was less than what expat consumed, so
1025 // there was at least one line break in the consumed data. Store the
1026 // last line until the point where we stopped parsing.
1027 nsScannerIterator startLastLine = currentExpatPosition;
1028 startLastLine.advance(-((ptrdiff_t)lastLineLength));
1029 if (!CopyUnicodeTo(startLastLine, currentExpatPosition, mLastLine)) {
1030 return (mInternalState = NS_ERROR_OUT_OF_MEMORY);
1032 } else {
1033 // There was no line break in the consumed data, append the consumed
1034 // data.
1035 if (!AppendUnicodeTo(oldExpatPosition, currentExpatPosition,
1036 mLastLine)) {
1037 return (mInternalState = NS_ERROR_OUT_OF_MEMORY);
1042 mExpatBuffered += length - consumed;
1044 if (BlockedOrInterrupted()) {
1045 MOZ_LOG(gExpatDriverLog, LogLevel::Debug,
1046 ("Blocked or interrupted parser (probably for loading linked "
1047 "stylesheets or scripts)."));
1049 aScanner.SetPosition(currentExpatPosition, true);
1050 aScanner.Mark();
1052 return mInternalState;
1055 if (noMoreBuffers && mExpatBuffered == 0) {
1056 mMadeFinalCallToExpat = true;
1059 if (NS_FAILED(mInternalState)) {
1060 if (XML_GetErrorCode(mExpatParser) != XML_ERROR_NONE) {
1061 NS_ASSERTION(mInternalState == NS_ERROR_HTMLPARSER_STOPPARSING,
1062 "Unexpected error");
1064 // Look for the next newline after the last one we consumed
1065 nsScannerIterator lastLine = currentExpatPosition;
1066 while (lastLine != end) {
1067 length = uint32_t(lastLine.size_forward());
1068 uint32_t endOffset = 0;
1069 const char16_t* buffer = lastLine.get();
1070 while (endOffset < length && buffer[endOffset] != '\n' &&
1071 buffer[endOffset] != '\r') {
1072 ++endOffset;
1074 mLastLine.Append(Substring(buffer, buffer + endOffset));
1075 if (endOffset < length) {
1076 // We found a newline.
1077 break;
1080 lastLine.advance(length);
1083 HandleError();
1086 return mInternalState;
1089 // Either we have more buffers, or we were blocked (and we'll flush in the
1090 // next iteration), or we should have emptied Expat's buffer.
1091 NS_ASSERTION(!noMoreBuffers || blocked ||
1092 (mExpatBuffered == 0 && currentExpatPosition == end),
1093 "Unreachable data left in Expat's buffer");
1095 start.advance(length);
1097 // It's possible for start to have passed end if we received more data
1098 // (e.g. if we spun the event loop in an inline script). Reload end now
1099 // to compensate.
1100 aScanner.EndReading(end);
1103 aScanner.SetPosition(currentExpatPosition, true);
1104 aScanner.Mark();
1106 MOZ_LOG(gExpatDriverLog, LogLevel::Debug,
1107 ("Remaining in expat's buffer: %i, remaining in scanner: %zu.",
1108 mExpatBuffered, Distance(currentExpatPosition, end)));
1110 return NS_SUCCEEDED(mInternalState) ? NS_ERROR_HTMLPARSER_EOF : NS_OK;
1113 NS_IMETHODIMP
1114 nsExpatDriver::WillBuildModel(const CParserContext& aParserContext,
1115 nsITokenizer* aTokenizer, nsIContentSink* aSink) {
1116 mSink = do_QueryInterface(aSink);
1117 if (!mSink) {
1118 NS_ERROR("nsExpatDriver didn't get an nsIExpatSink");
1119 // Make sure future calls to us bail out as needed
1120 mInternalState = NS_ERROR_UNEXPECTED;
1121 return mInternalState;
1124 mOriginalSink = aSink;
1126 static const XML_Memory_Handling_Suite memsuite = {malloc, realloc, free};
1128 static const char16_t kExpatSeparator[] = {kExpatSeparatorChar, '\0'};
1130 mExpatParser = XML_ParserCreate_MM(kUTF16, &memsuite, kExpatSeparator);
1131 NS_ENSURE_TRUE(mExpatParser, NS_ERROR_FAILURE);
1133 XML_SetReturnNSTriplet(mExpatParser, XML_TRUE);
1135 #ifdef XML_DTD
1136 XML_SetParamEntityParsing(mExpatParser, XML_PARAM_ENTITY_PARSING_ALWAYS);
1137 #endif
1139 mURISpec = aParserContext.mScanner->GetFilename();
1141 XML_SetBase(mExpatParser, mURISpec.get());
1143 nsCOMPtr<Document> doc = do_QueryInterface(mOriginalSink->GetTarget());
1144 if (doc) {
1145 nsCOMPtr<nsPIDOMWindowOuter> win = doc->GetWindow();
1146 nsCOMPtr<nsPIDOMWindowInner> inner;
1147 if (win) {
1148 inner = win->GetCurrentInnerWindow();
1149 } else {
1150 bool aHasHadScriptHandlingObject;
1151 nsIScriptGlobalObject* global =
1152 doc->GetScriptHandlingObject(aHasHadScriptHandlingObject);
1153 if (global) {
1154 inner = do_QueryInterface(global);
1157 if (inner) {
1158 mInnerWindowID = inner->WindowID();
1162 // Set up the callbacks
1163 XML_SetXmlDeclHandler(mExpatParser, Driver_HandleXMLDeclaration);
1164 if (doc && doc->NodePrincipal()->IsSystemPrincipal()) {
1165 XML_SetElementHandler(mExpatParser, HandleStartElementForSystemPrincipal,
1166 HandleEndElementForSystemPrincipal);
1167 } else {
1168 XML_SetElementHandler(mExpatParser, HandleStartElement, HandleEndElement);
1170 XML_SetCharacterDataHandler(mExpatParser, Driver_HandleCharacterData);
1171 XML_SetProcessingInstructionHandler(mExpatParser,
1172 Driver_HandleProcessingInstruction);
1173 XML_SetDefaultHandlerExpand(mExpatParser, Driver_HandleDefault);
1174 XML_SetExternalEntityRefHandler(
1175 mExpatParser,
1176 (XML_ExternalEntityRefHandler)Driver_HandleExternalEntityRef);
1177 XML_SetExternalEntityRefHandlerArg(mExpatParser, this);
1178 XML_SetCommentHandler(mExpatParser, Driver_HandleComment);
1179 XML_SetCdataSectionHandler(mExpatParser, Driver_HandleStartCdataSection,
1180 Driver_HandleEndCdataSection);
1182 XML_SetParamEntityParsing(mExpatParser,
1183 XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE);
1184 XML_SetDoctypeDeclHandler(mExpatParser, Driver_HandleStartDoctypeDecl,
1185 Driver_HandleEndDoctypeDecl);
1187 // Set up the user data.
1188 XML_SetUserData(mExpatParser, this);
1190 return mInternalState;
1193 NS_IMETHODIMP
1194 nsExpatDriver::BuildModel(nsITokenizer* aTokenizer, nsIContentSink* aSink) {
1195 return mInternalState;
1198 NS_IMETHODIMP
1199 nsExpatDriver::DidBuildModel(nsresult anErrorCode) {
1200 mOriginalSink = nullptr;
1201 mSink = nullptr;
1202 return NS_OK;
1205 NS_IMETHODIMP
1206 nsExpatDriver::WillTokenize(bool aIsFinalChunk) {
1207 mIsFinalChunk = aIsFinalChunk;
1208 return NS_OK;
1211 NS_IMETHODIMP_(void)
1212 nsExpatDriver::Terminate() {
1213 // XXX - not sure what happens to the unparsed data.
1214 if (mExpatParser) {
1215 XML_StopParser(mExpatParser, XML_FALSE);
1217 mInternalState = NS_ERROR_HTMLPARSER_STOPPARSING;
1220 NS_IMETHODIMP_(int32_t)
1221 nsExpatDriver::GetType() { return NS_IPARSER_FLAG_XML; }
1223 NS_IMETHODIMP_(nsDTDMode)
1224 nsExpatDriver::GetMode() const { return eDTDMode_full_standards; }
1226 /*************************** Unused methods **********************************/
1228 NS_IMETHODIMP_(bool)
1229 nsExpatDriver::IsContainer(int32_t aTag) const { return true; }
1231 NS_IMETHODIMP_(bool)
1232 nsExpatDriver::CanContain(int32_t aParent, int32_t aChild) const {
1233 return true;
1236 void nsExpatDriver::MaybeStopParser(nsresult aState) {
1237 if (NS_FAILED(aState)) {
1238 // If we had a failure we want to override NS_ERROR_HTMLPARSER_INTERRUPTED
1239 // and we want to override NS_ERROR_HTMLPARSER_BLOCK but not with
1240 // NS_ERROR_HTMLPARSER_INTERRUPTED.
1241 if (NS_SUCCEEDED(mInternalState) ||
1242 mInternalState == NS_ERROR_HTMLPARSER_INTERRUPTED ||
1243 (mInternalState == NS_ERROR_HTMLPARSER_BLOCK &&
1244 aState != NS_ERROR_HTMLPARSER_INTERRUPTED)) {
1245 mInternalState = (aState == NS_ERROR_HTMLPARSER_INTERRUPTED ||
1246 aState == NS_ERROR_HTMLPARSER_BLOCK)
1247 ? aState
1248 : NS_ERROR_HTMLPARSER_STOPPARSING;
1251 // If we get an error then we need to stop Expat (by calling XML_StopParser
1252 // with false as the last argument). If the parser should be blocked or
1253 // interrupted we need to pause Expat (by calling XML_StopParser with
1254 // true as the last argument).
1255 XML_StopParser(mExpatParser, BlockedOrInterrupted());
1256 } else if (NS_SUCCEEDED(mInternalState)) {
1257 // Only clobber mInternalState with the success code if we didn't block or
1258 // interrupt before.
1259 mInternalState = aState;