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