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