Bug 1839316: part 5) Guard the "fetchpriority" attribute behind a pref. r=kershaw...
[gecko.git] / parser / htmlparser / nsExpatDriver.cpp
blob4da2444f84cc6ffb9754496b5b6ca89777749c0b
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsExpatDriver.h"
7 #include "mozilla/fallible.h"
8 #include "nsCOMPtr.h"
9 #include "CParserContext.h"
10 #include "nsIExpatSink.h"
11 #include "nsIContentSink.h"
12 #include "nsIDocShell.h"
13 #include "nsParserMsgUtils.h"
14 #include "nsIURL.h"
15 #include "nsIUnicharInputStream.h"
16 #include "nsIProtocolHandler.h"
17 #include "nsNetUtil.h"
18 #include "nsString.h"
19 #include "nsTextFormatter.h"
20 #include "nsDirectoryServiceDefs.h"
21 #include "nsCRT.h"
22 #include "nsIConsoleService.h"
23 #include "nsIScriptError.h"
24 #include "nsIScriptGlobalObject.h"
25 #include "nsIContentPolicy.h"
26 #include "nsComponentManagerUtils.h"
27 #include "nsContentPolicyUtils.h"
28 #include "nsError.h"
29 #include "nsXPCOMCIDInternal.h"
30 #include "nsUnicharInputStream.h"
31 #include "nsContentUtils.h"
32 #include "mozilla/Array.h"
33 #include "mozilla/ArrayUtils.h"
34 #include "mozilla/BasePrincipal.h"
35 #include "mozilla/IntegerTypeTraits.h"
36 #include "mozilla/NullPrincipal.h"
37 #include "mozilla/Telemetry.h"
38 #include "mozilla/TelemetryComms.h"
40 #include "nsThreadUtils.h"
41 #include "mozilla/ClearOnShutdown.h"
42 #include "mozilla/RLBoxUtils.h"
43 #include "mozilla/UniquePtr.h"
45 #include "mozilla/Logging.h"
47 using mozilla::fallible;
48 using mozilla::LogLevel;
49 using mozilla::MakeStringSpan;
50 using mozilla::Maybe;
51 using mozilla::Unused;
52 using mozilla::dom::Document;
54 // We only pass chunks of length sMaxChunkLength to Expat in the RLBOX sandbox.
55 // The RLBOX sandbox has a limited amount of memory, and we have to account for
56 // other memory use by Expat (including the buffering it does).
57 // Note that sMaxChunkLength is in number of characters.
58 #ifdef DEBUG
59 // On debug builds we set a much lower limit (1kB) to try to hit boundary
60 // conditions more frequently.
61 static const uint32_t sMaxChunkLength = 1024 / sizeof(char16_t);
62 #else
63 static const uint32_t sMaxChunkLength = (128 * 1024) / sizeof(char16_t);
64 #endif
66 #define kExpatSeparatorChar 0xFFFF
68 static const char16_t kUTF16[] = {'U', 'T', 'F', '-', '1', '6', '\0'};
70 static mozilla::LazyLogModule gExpatDriverLog("expatdriver");
72 // Use the same maximum tree depth as Chromium (see
73 // https://chromium.googlesource.com/chromium/src/+/f464165c1dedff1c955d3c051c5a9a1c6a0e8f6b/third_party/WebKit/Source/core/xml/parser/XMLDocumentParser.cpp#85).
74 static const uint16_t sMaxXMLTreeDepth = 5000;
76 /***************************** RLBOX HELPERS ********************************/
77 // Helpers for calling sandboxed expat functions in handlers
79 #define RLBOX_EXPAT_SAFE_CALL(foo, verifier, ...) \
80 aSandbox.invoke_sandbox_function(foo, self->mExpatParser, ##__VA_ARGS__) \
81 .copy_and_verify(verifier)
83 #define RLBOX_EXPAT_SAFE_MCALL(foo, verifier, ...) \
84 Sandbox() \
85 ->invoke_sandbox_function(foo, mExpatParser, ##__VA_ARGS__) \
86 .copy_and_verify(verifier)
88 #define RLBOX_EXPAT_CALL(foo, ...) \
89 aSandbox.invoke_sandbox_function(foo, self->mExpatParser, ##__VA_ARGS__)
91 #define RLBOX_EXPAT_MCALL(foo, ...) \
92 Sandbox()->invoke_sandbox_function(foo, mExpatParser, ##__VA_ARGS__)
94 #define RLBOX_SAFE_PRINT "Value used only for printing"
95 #define MOZ_RELEASE_ASSERT_TAINTED(cond, ...) \
96 MOZ_RELEASE_ASSERT((cond).unverified_safe_because("Sanity check"), \
97 ##__VA_ARGS__)
99 /* safe_unverified is used whenever it's safe to not use a validator */
100 template <typename T>
101 static T safe_unverified(T val) {
102 return val;
105 /* status_verifier is a type validator for XML_Status */
106 inline enum XML_Status status_verifier(enum XML_Status s) {
107 MOZ_RELEASE_ASSERT(s >= XML_STATUS_ERROR && s <= XML_STATUS_SUSPENDED,
108 "unexpected status code");
109 return s;
112 /* error_verifier is a type validator for XML_Error */
113 inline enum XML_Error error_verifier(enum XML_Error code) {
114 MOZ_RELEASE_ASSERT(
115 code >= XML_ERROR_NONE && code <= XML_ERROR_INVALID_ARGUMENT,
116 "unexpected XML error code");
117 return code;
120 /* We use unverified_xml_string to just expose sandbox expat strings to Firefox
121 * without any validation. On 64-bit we have guard pages at the sandbox
122 * boundary; on 32-bit we don't and a string could be used to read beyond the
123 * sandbox boundary. In our attacker model this is okay (the attacker can just
124 * Spectre).
126 * Nevertheless, we should try to add strings validators to the consumer code
127 * of expat whenever we have some semantics. At the very lest we should make
128 * sure that the strings are never written to. Bug 1693991 tracks this.
130 static const XML_Char* unverified_xml_string(uintptr_t ptr) {
131 return reinterpret_cast<const XML_Char*>(ptr);
134 /* The TransferBuffer class is used to copy (or directly expose in the
135 * noop-sandbox case) buffers into the expat sandbox (and automatically
136 * when out of scope).
138 template <typename T>
139 using TransferBuffer =
140 mozilla::RLBoxTransferBufferToSandbox<T, rlbox_expat_sandbox_type>;
142 /*************************** END RLBOX HELPERS ******************************/
144 /***************************** EXPAT CALL BACKS ******************************/
145 // The callback handlers that get called from the expat parser.
147 static void Driver_HandleXMLDeclaration(
148 rlbox_sandbox_expat& aSandbox, tainted_expat<void*> /* aUserData */,
149 tainted_expat<const XML_Char*> aVersion,
150 tainted_expat<const XML_Char*> aEncoding, tainted_expat<int> aStandalone) {
151 nsExpatDriver* driver = static_cast<nsExpatDriver*>(aSandbox.sandbox_storage);
152 MOZ_ASSERT(driver);
154 int standalone = aStandalone.copy_and_verify([&](auto a) {
155 // Standalone argument can be -1, 0, or 1 (see
156 // /parser/expat/lib/expat.h#185)
157 MOZ_RELEASE_ASSERT(a >= -1 && a <= 1, "Unexpected standalone parameter");
158 return a;
161 const auto* version = aVersion.copy_and_verify_address(unverified_xml_string);
162 const auto* encoding =
163 aEncoding.copy_and_verify_address(unverified_xml_string);
164 driver->HandleXMLDeclaration(version, encoding, standalone);
167 static void Driver_HandleCharacterData(rlbox_sandbox_expat& aSandbox,
168 tainted_expat<void*> /* aUserData */,
169 tainted_expat<const XML_Char*> aData,
170 tainted_expat<int> aLength) {
171 nsExpatDriver* driver = static_cast<nsExpatDriver*>(aSandbox.sandbox_storage);
172 MOZ_ASSERT(driver);
173 // aData is not null terminated; even with bad length we will not span beyond
174 // sandbox boundary
175 uint32_t length =
176 static_cast<uint32_t>(aLength.copy_and_verify(safe_unverified<int>));
177 const auto* data = aData.unverified_safe_pointer_because(
178 length, "Only care that the data is within sandbox boundary.");
179 driver->HandleCharacterData(data, length);
182 static void Driver_HandleComment(rlbox_sandbox_expat& aSandbox,
183 tainted_expat<void*> /* aUserData */,
184 tainted_expat<const XML_Char*> aName) {
185 nsExpatDriver* driver = static_cast<nsExpatDriver*>(aSandbox.sandbox_storage);
186 MOZ_ASSERT(driver);
187 const auto* name = aName.copy_and_verify_address(unverified_xml_string);
188 driver->HandleComment(name);
191 static void Driver_HandleProcessingInstruction(
192 rlbox_sandbox_expat& aSandbox, tainted_expat<void*> /* aUserData */,
193 tainted_expat<const XML_Char*> aTarget,
194 tainted_expat<const XML_Char*> aData) {
195 nsExpatDriver* driver = static_cast<nsExpatDriver*>(aSandbox.sandbox_storage);
196 MOZ_ASSERT(driver);
197 const auto* target = aTarget.copy_and_verify_address(unverified_xml_string);
198 const auto* data = aData.copy_and_verify_address(unverified_xml_string);
199 driver->HandleProcessingInstruction(target, data);
202 static void Driver_HandleDefault(rlbox_sandbox_expat& aSandbox,
203 tainted_expat<void*> /* aUserData */,
204 tainted_expat<const XML_Char*> aData,
205 tainted_expat<int> aLength) {
206 nsExpatDriver* driver = static_cast<nsExpatDriver*>(aSandbox.sandbox_storage);
207 MOZ_ASSERT(driver);
208 // aData is not null terminated; even with bad length we will not span
209 // beyond sandbox boundary
210 uint32_t length =
211 static_cast<uint32_t>(aLength.copy_and_verify(safe_unverified<int>));
212 const auto* data = aData.unverified_safe_pointer_because(
213 length, "Only care that the data is within sandbox boundary.");
214 driver->HandleDefault(data, length);
217 static void Driver_HandleStartCdataSection(
218 rlbox_sandbox_expat& aSandbox, tainted_expat<void*> /* aUserData */) {
219 nsExpatDriver* driver = static_cast<nsExpatDriver*>(aSandbox.sandbox_storage);
220 MOZ_ASSERT(driver);
221 driver->HandleStartCdataSection();
224 static void Driver_HandleEndCdataSection(rlbox_sandbox_expat& aSandbox,
225 tainted_expat<void*> /* aUserData */) {
226 nsExpatDriver* driver = static_cast<nsExpatDriver*>(aSandbox.sandbox_storage);
227 MOZ_ASSERT(driver);
228 driver->HandleEndCdataSection();
231 static void Driver_HandleStartDoctypeDecl(
232 rlbox_sandbox_expat& aSandbox, tainted_expat<void*> /* aUserData */,
233 tainted_expat<const XML_Char*> aDoctypeName,
234 tainted_expat<const XML_Char*> aSysid,
235 tainted_expat<const XML_Char*> aPubid,
236 tainted_expat<int> aHasInternalSubset) {
237 nsExpatDriver* driver = static_cast<nsExpatDriver*>(aSandbox.sandbox_storage);
238 MOZ_ASSERT(driver);
239 const auto* doctypeName =
240 aDoctypeName.copy_and_verify_address(unverified_xml_string);
241 const auto* sysid = aSysid.copy_and_verify_address(unverified_xml_string);
242 const auto* pubid = aPubid.copy_and_verify_address(unverified_xml_string);
243 bool hasInternalSubset =
244 !!(aHasInternalSubset.copy_and_verify(safe_unverified<int>));
245 driver->HandleStartDoctypeDecl(doctypeName, sysid, pubid, hasInternalSubset);
248 static void Driver_HandleEndDoctypeDecl(rlbox_sandbox_expat& aSandbox,
249 tainted_expat<void*> /* aUserData */) {
250 nsExpatDriver* driver = static_cast<nsExpatDriver*>(aSandbox.sandbox_storage);
251 MOZ_ASSERT(driver);
252 driver->HandleEndDoctypeDecl();
255 static tainted_expat<int> Driver_HandleExternalEntityRef(
256 rlbox_sandbox_expat& aSandbox, tainted_expat<XML_Parser> /* aParser */,
257 tainted_expat<const XML_Char*> aOpenEntityNames,
258 tainted_expat<const XML_Char*> aBase,
259 tainted_expat<const XML_Char*> aSystemId,
260 tainted_expat<const XML_Char*> aPublicId) {
261 nsExpatDriver* driver = static_cast<nsExpatDriver*>(aSandbox.sandbox_storage);
262 MOZ_ASSERT(driver);
264 const auto* openEntityNames =
265 aOpenEntityNames.copy_and_verify_address(unverified_xml_string);
266 const auto* base = aBase.copy_and_verify_address(unverified_xml_string);
267 const auto* systemId =
268 aSystemId.copy_and_verify_address(unverified_xml_string);
269 const auto* publicId =
270 aPublicId.copy_and_verify_address(unverified_xml_string);
271 return driver->HandleExternalEntityRef(openEntityNames, base, systemId,
272 publicId);
275 /***************************** END CALL BACKS ********************************/
277 /***************************** CATALOG UTILS *********************************/
279 // Initially added for bug 113400 to switch from the remote "XHTML 1.0 plus
280 // MathML 2.0" DTD to the the lightweight customized version that Mozilla uses.
281 // Since Mozilla is not validating, no need to fetch a *huge* file at each
282 // click.
283 // XXX The cleanest solution here would be to fix Bug 98413: Implement XML
284 // Catalogs.
285 struct nsCatalogData {
286 const char* mPublicID;
287 const char* mLocalDTD;
288 const char* mAgentSheet;
291 // The order of this table is guestimated to be in the optimum order
292 static const nsCatalogData kCatalogTable[] = {
293 {"-//W3C//DTD XHTML 1.0 Transitional//EN", "htmlmathml-f.ent", nullptr},
294 {"-//W3C//DTD XHTML 1.1//EN", "htmlmathml-f.ent", nullptr},
295 {"-//W3C//DTD XHTML 1.0 Strict//EN", "htmlmathml-f.ent", nullptr},
296 {"-//W3C//DTD XHTML 1.0 Frameset//EN", "htmlmathml-f.ent", nullptr},
297 {"-//W3C//DTD XHTML Basic 1.0//EN", "htmlmathml-f.ent", nullptr},
298 {"-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN", "htmlmathml-f.ent", nullptr},
299 {"-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN",
300 "htmlmathml-f.ent", nullptr},
301 {"-//W3C//DTD MathML 2.0//EN", "htmlmathml-f.ent", nullptr},
302 {"-//WAPFORUM//DTD XHTML Mobile 1.0//EN", "htmlmathml-f.ent", nullptr},
303 {nullptr, nullptr, nullptr}};
305 static const nsCatalogData* LookupCatalogData(const char16_t* aPublicID) {
306 nsDependentString publicID(aPublicID);
308 // linear search for now since the number of entries is going to
309 // be negligible, and the fix for bug 98413 would get rid of this
310 // code anyway
311 const nsCatalogData* data = kCatalogTable;
312 while (data->mPublicID) {
313 if (publicID.EqualsASCII(data->mPublicID)) {
314 return data;
316 ++data;
319 return nullptr;
322 // This function provides a resource URI to a local DTD
323 // in resource://gre/res/dtd/ which may or may not exist.
324 // If aCatalogData is provided, it is used to remap the
325 // DTD instead of taking the filename from the URI. aDTD
326 // may be null in some cases that are relying on
327 // aCatalogData working for them.
328 static void GetLocalDTDURI(const nsCatalogData* aCatalogData, nsIURI* aDTD,
329 nsIURI** aResult) {
330 nsAutoCString fileName;
331 if (aCatalogData) {
332 // remap the DTD to a known local DTD
333 fileName.Assign(aCatalogData->mLocalDTD);
336 if (fileName.IsEmpty()) {
337 // Try to see if the user has installed the DTD file -- we extract the
338 // filename.ext of the DTD here. Hence, for any DTD for which we have
339 // no predefined mapping, users just have to copy the DTD file to our
340 // special DTD directory and it will be picked.
341 nsCOMPtr<nsIURL> dtdURL = do_QueryInterface(aDTD);
342 if (!dtdURL) {
343 // Not a URL with a filename, or maybe it was null. Either way, nothing
344 // else we can do here.
345 return;
348 dtdURL->GetFileName(fileName);
349 if (fileName.IsEmpty()) {
350 return;
354 nsAutoCString respath("resource://gre/res/dtd/");
355 respath += fileName;
356 NS_NewURI(aResult, respath);
359 /***************************** END CATALOG UTILS *****************************/
361 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsExpatDriver)
362 NS_INTERFACE_MAP_ENTRY(nsIDTD)
363 NS_INTERFACE_MAP_ENTRY(nsISupports)
364 NS_INTERFACE_MAP_END
366 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsExpatDriver)
367 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsExpatDriver)
369 NS_IMPL_CYCLE_COLLECTION(nsExpatDriver, mSink)
371 nsExpatDriver::nsExpatDriver()
372 : mExpatParser(nullptr),
373 mInCData(false),
374 mInInternalSubset(false),
375 mInExternalDTD(false),
376 mMadeFinalCallToExpat(false),
377 mInParser(false),
378 mInternalState(NS_OK),
379 mExpatBuffered(0),
380 mTagDepth(0),
381 mCatalogData(nullptr),
382 mInnerWindowID(0) {}
384 nsExpatDriver::~nsExpatDriver() { Destroy(); }
386 void nsExpatDriver::Destroy() {
387 if (mSandboxPoolData) {
388 SandboxData()->DetachDriver();
389 if (mExpatParser) {
390 RLBOX_EXPAT_MCALL(MOZ_XML_ParserFree);
393 mSandboxPoolData.reset();
394 mURIs.Clear();
395 mExpatParser = nullptr;
398 // The AllocAttrs class is used to speed up copying attributes from the
399 // sandboxed expat by fast allocating attributes on the stack and only falling
400 // back to malloc when we need to allocate lots of attributes.
401 class MOZ_STACK_CLASS AllocAttrs {
402 #define NUM_STACK_SLOTS 16
403 public:
404 const char16_t** Init(size_t size) {
405 if (size <= NUM_STACK_SLOTS) {
406 return mInlineArr;
408 mHeapPtr = mozilla::MakeUnique<const char16_t*[]>(size);
409 return mHeapPtr.get();
412 private:
413 const char16_t* mInlineArr[NUM_STACK_SLOTS];
414 mozilla::UniquePtr<const char16_t*[]> mHeapPtr;
415 #undef NUM_STACK_SLOTS
418 /* static */
419 void nsExpatDriver::HandleStartElement(rlbox_sandbox_expat& aSandbox,
420 tainted_expat<void*> /* aUserData */,
421 tainted_expat<const char16_t*> aName,
422 tainted_expat<const char16_t**> aAttrs) {
423 nsExpatDriver* self = static_cast<nsExpatDriver*>(aSandbox.sandbox_storage);
424 MOZ_ASSERT(self && self->mSink);
426 const auto* name = aName.copy_and_verify_address(unverified_xml_string);
428 // Calculate the total number of elements in aAttrs.
429 // XML_GetSpecifiedAttributeCount will only give us the number of specified
430 // attrs (twice that number, actually), so we have to check for default
431 // attrs ourselves.
432 tainted_expat<int> count =
433 RLBOX_EXPAT_CALL(MOZ_XML_GetSpecifiedAttributeCount);
434 MOZ_RELEASE_ASSERT_TAINTED(count >= 0, "Unexpected attribute count");
436 tainted_expat<uint64_t> attrArrayLengthTainted;
437 for (attrArrayLengthTainted = rlbox::sandbox_static_cast<uint64_t>(count);
438 (aAttrs[attrArrayLengthTainted] != nullptr)
439 .unverified_safe_because("Bad length is checked later");
440 attrArrayLengthTainted += 2) {
441 // Just looping till we find out what the length is
444 uint32_t attrArrayLength =
445 attrArrayLengthTainted.copy_and_verify([&](uint64_t value) {
446 // A malicious length could result in an overflow when we allocate
447 // aAttrs and then access elements of the array.
448 MOZ_RELEASE_ASSERT(value < UINT32_MAX, "Overflow attempt");
449 return value;
452 // Copy tainted aAttrs from sandbox
453 AllocAttrs allocAttrs;
454 const char16_t** attrs = allocAttrs.Init(attrArrayLength + 1);
455 if (NS_WARN_IF(!aAttrs || !attrs)) {
456 self->MaybeStopParser(NS_ERROR_OUT_OF_MEMORY);
457 return;
460 for (uint32_t i = 0; i < attrArrayLength; i++) {
461 attrs[i] = aAttrs[i].copy_and_verify_address(unverified_xml_string);
463 attrs[attrArrayLength] = nullptr;
465 if (self->mSink) {
466 // We store the tagdepth in a PRUint16, so make sure the limit fits in a
467 // PRUint16.
468 static_assert(
469 sMaxXMLTreeDepth <=
470 std::numeric_limits<decltype(nsExpatDriver::mTagDepth)>::max());
472 if (++self->mTagDepth > sMaxXMLTreeDepth) {
473 self->MaybeStopParser(NS_ERROR_HTMLPARSER_HIERARCHYTOODEEP);
474 return;
477 nsresult rv = self->mSink->HandleStartElement(
478 name, attrs, attrArrayLength,
479 RLBOX_EXPAT_SAFE_CALL(MOZ_XML_GetCurrentLineNumber,
480 safe_unverified<XML_Size>),
481 RLBOX_EXPAT_SAFE_CALL(MOZ_XML_GetCurrentColumnNumber,
482 safe_unverified<XML_Size>));
483 self->MaybeStopParser(rv);
487 /* static */
488 void nsExpatDriver::HandleStartElementForSystemPrincipal(
489 rlbox_sandbox_expat& aSandbox, tainted_expat<void*> aUserData,
490 tainted_expat<const char16_t*> aName,
491 tainted_expat<const char16_t**> aAttrs) {
492 nsExpatDriver* self = static_cast<nsExpatDriver*>(aSandbox.sandbox_storage);
493 MOZ_ASSERT(self);
494 if (!RLBOX_EXPAT_SAFE_CALL(MOZ_XML_ProcessingEntityValue,
495 safe_unverified<XML_Bool>)) {
496 HandleStartElement(aSandbox, aUserData, aName, aAttrs);
497 } else {
498 nsCOMPtr<Document> doc =
499 do_QueryInterface(self->mOriginalSink->GetTarget());
501 // Adjust the column number so that it is one based rather than zero
502 // based.
503 tainted_expat<XML_Size> colNumber =
504 RLBOX_EXPAT_CALL(MOZ_XML_GetCurrentColumnNumber) + 1;
505 tainted_expat<XML_Size> lineNumber =
506 RLBOX_EXPAT_CALL(MOZ_XML_GetCurrentLineNumber);
508 int32_t nameSpaceID;
509 RefPtr<nsAtom> prefix, localName;
510 const auto* name = aName.copy_and_verify_address(unverified_xml_string);
511 nsContentUtils::SplitExpatName(name, getter_AddRefs(prefix),
512 getter_AddRefs(localName), &nameSpaceID);
514 nsAutoString error;
515 error.AppendLiteral("Ignoring element <");
516 if (prefix) {
517 error.Append(prefix->GetUTF16String());
518 error.Append(':');
520 error.Append(localName->GetUTF16String());
521 error.AppendLiteral("> created from entity value.");
523 nsContentUtils::ReportToConsoleNonLocalized(
524 error, nsIScriptError::warningFlag, "XML Document"_ns, doc, nullptr,
525 u""_ns, lineNumber.unverified_safe_because(RLBOX_SAFE_PRINT),
526 colNumber.unverified_safe_because(RLBOX_SAFE_PRINT));
530 /* static */
531 void nsExpatDriver::HandleEndElement(rlbox_sandbox_expat& aSandbox,
532 tainted_expat<void*> aUserData,
533 tainted_expat<const char16_t*> aName) {
534 nsExpatDriver* self = static_cast<nsExpatDriver*>(aSandbox.sandbox_storage);
535 MOZ_ASSERT(self);
536 const auto* name = aName.copy_and_verify_address(unverified_xml_string);
538 NS_ASSERTION(self->mSink, "content sink not found!");
539 NS_ASSERTION(self->mInternalState != NS_ERROR_HTMLPARSER_BLOCK,
540 "Shouldn't block from HandleStartElement.");
542 if (self->mSink && self->mInternalState != NS_ERROR_HTMLPARSER_STOPPARSING) {
543 nsresult rv = self->mSink->HandleEndElement(name);
544 --self->mTagDepth;
545 self->MaybeStopParser(rv);
549 /* static */
550 void nsExpatDriver::HandleEndElementForSystemPrincipal(
551 rlbox_sandbox_expat& aSandbox, tainted_expat<void*> aUserData,
552 tainted_expat<const char16_t*> aName) {
553 nsExpatDriver* self = static_cast<nsExpatDriver*>(aSandbox.sandbox_storage);
554 MOZ_ASSERT(self);
555 if (!RLBOX_EXPAT_SAFE_CALL(MOZ_XML_ProcessingEntityValue,
556 safe_unverified<XML_Bool>)) {
557 HandleEndElement(aSandbox, aUserData, aName);
561 nsresult nsExpatDriver::HandleCharacterData(const char16_t* aValue,
562 const uint32_t aLength) {
563 NS_ASSERTION(mSink, "content sink not found!");
565 if (mInCData) {
566 if (!mCDataText.Append(aValue, aLength, fallible)) {
567 MaybeStopParser(NS_ERROR_OUT_OF_MEMORY);
569 } else if (mSink) {
570 nsresult rv = mSink->HandleCharacterData(aValue, aLength);
571 MaybeStopParser(rv);
574 return NS_OK;
577 nsresult nsExpatDriver::HandleComment(const char16_t* aValue) {
578 NS_ASSERTION(mSink, "content sink not found!");
580 if (mInExternalDTD) {
581 // Ignore comments from external DTDs
582 return NS_OK;
585 if (mInInternalSubset) {
586 mInternalSubset.AppendLiteral("<!--");
587 mInternalSubset.Append(aValue);
588 mInternalSubset.AppendLiteral("-->");
589 } else if (mSink) {
590 nsresult rv = mSink->HandleComment(aValue);
591 MaybeStopParser(rv);
594 return NS_OK;
597 nsresult nsExpatDriver::HandleProcessingInstruction(const char16_t* aTarget,
598 const char16_t* aData) {
599 NS_ASSERTION(mSink, "content sink not found!");
601 if (mInExternalDTD) {
602 // Ignore PIs in external DTDs for now. Eventually we want to
603 // pass them to the sink in a way that doesn't put them in the DOM
604 return NS_OK;
607 if (mInInternalSubset) {
608 mInternalSubset.AppendLiteral("<?");
609 mInternalSubset.Append(aTarget);
610 mInternalSubset.Append(' ');
611 mInternalSubset.Append(aData);
612 mInternalSubset.AppendLiteral("?>");
613 } else if (mSink) {
614 nsresult rv = mSink->HandleProcessingInstruction(aTarget, aData);
615 MaybeStopParser(rv);
618 return NS_OK;
621 nsresult nsExpatDriver::HandleXMLDeclaration(const char16_t* aVersion,
622 const char16_t* aEncoding,
623 int32_t aStandalone) {
624 if (mSink) {
625 nsresult rv = mSink->HandleXMLDeclaration(aVersion, aEncoding, aStandalone);
626 MaybeStopParser(rv);
629 return NS_OK;
632 nsresult nsExpatDriver::HandleDefault(const char16_t* aValue,
633 const uint32_t aLength) {
634 NS_ASSERTION(mSink, "content sink not found!");
636 if (mInExternalDTD) {
637 // Ignore newlines in external DTDs
638 return NS_OK;
641 if (mInInternalSubset) {
642 mInternalSubset.Append(aValue, aLength);
643 } else if (mSink) {
644 uint32_t i;
645 nsresult rv = mInternalState;
646 for (i = 0; i < aLength && NS_SUCCEEDED(rv); ++i) {
647 if (aValue[i] == '\n' || aValue[i] == '\r') {
648 rv = mSink->HandleCharacterData(&aValue[i], 1);
651 MaybeStopParser(rv);
654 return NS_OK;
657 nsresult nsExpatDriver::HandleStartCdataSection() {
658 mInCData = true;
660 return NS_OK;
663 nsresult nsExpatDriver::HandleEndCdataSection() {
664 NS_ASSERTION(mSink, "content sink not found!");
666 mInCData = false;
667 if (mSink) {
668 nsresult rv =
669 mSink->HandleCDataSection(mCDataText.get(), mCDataText.Length());
670 MaybeStopParser(rv);
672 mCDataText.Truncate();
674 return NS_OK;
677 nsresult nsExpatDriver::HandleStartDoctypeDecl(const char16_t* aDoctypeName,
678 const char16_t* aSysid,
679 const char16_t* aPubid,
680 bool aHasInternalSubset) {
681 mDoctypeName = aDoctypeName;
682 mSystemID = aSysid;
683 mPublicID = aPubid;
685 if (aHasInternalSubset) {
686 // Consuming a huge internal subset translates to numerous
687 // allocations. In an effort to avoid too many allocations
688 // setting mInternalSubset's capacity to be 1K ( just a guesstimate! ).
689 mInInternalSubset = true;
690 mInternalSubset.SetCapacity(1024);
691 } else {
692 // Distinguish missing internal subset from an empty one
693 mInternalSubset.SetIsVoid(true);
696 return NS_OK;
699 nsresult nsExpatDriver::HandleEndDoctypeDecl() {
700 NS_ASSERTION(mSink, "content sink not found!");
702 mInInternalSubset = false;
704 if (mSink) {
705 // let the sink know any additional knowledge that we have about the
706 // document (currently, from bug 124570, we only expect to pass additional
707 // agent sheets needed to layout the XML vocabulary of the document)
708 nsCOMPtr<nsIURI> data;
709 #if 0
710 if (mCatalogData && mCatalogData->mAgentSheet) {
711 NS_NewURI(getter_AddRefs(data), mCatalogData->mAgentSheet);
713 #endif
715 // The unused support for "catalog style sheets" was removed. It doesn't
716 // look like we'll ever fix bug 98413 either.
717 MOZ_ASSERT(!mCatalogData || !mCatalogData->mAgentSheet,
718 "Need to add back support for catalog style sheets");
720 // Note: mInternalSubset already doesn't include the [] around it.
721 nsresult rv = mSink->HandleDoctypeDecl(mInternalSubset, mDoctypeName,
722 mSystemID, mPublicID, data);
723 MaybeStopParser(rv);
726 mInternalSubset.Truncate();
728 return NS_OK;
731 // Wrapper class for passing the sandbox data and parser as a closure to
732 // ExternalDTDStreamReaderFunc.
733 class RLBoxExpatClosure {
734 public:
735 RLBoxExpatClosure(RLBoxExpatSandboxData* aSbxData,
736 tainted_expat<XML_Parser> aExpatParser)
737 : mSbxData(aSbxData), mExpatParser(aExpatParser){};
738 inline rlbox_sandbox_expat* Sandbox() const { return mSbxData->Sandbox(); };
739 inline tainted_expat<XML_Parser> Parser() const { return mExpatParser; };
741 private:
742 RLBoxExpatSandboxData* mSbxData;
743 tainted_expat<XML_Parser> mExpatParser;
746 static nsresult ExternalDTDStreamReaderFunc(nsIUnicharInputStream* aIn,
747 void* aClosure,
748 const char16_t* aFromSegment,
749 uint32_t aToOffset, uint32_t aCount,
750 uint32_t* aWriteCount) {
751 MOZ_ASSERT(aClosure && aFromSegment && aWriteCount);
753 *aWriteCount = 0;
755 // Get sandbox and parser
756 auto* closure = reinterpret_cast<RLBoxExpatClosure*>(aClosure);
757 MOZ_ASSERT(closure);
759 // Transfer segment into the sandbox
760 auto fromSegment =
761 TransferBuffer<char16_t>(closure->Sandbox(), aFromSegment, aCount);
762 NS_ENSURE_TRUE(*fromSegment, NS_ERROR_OUT_OF_MEMORY);
764 // Pass the buffer to expat for parsing.
765 if (closure->Sandbox()
766 ->invoke_sandbox_function(
767 MOZ_XML_Parse, closure->Parser(),
768 rlbox::sandbox_reinterpret_cast<const char*>(*fromSegment),
769 aCount * sizeof(char16_t), 0)
770 .copy_and_verify(status_verifier) == XML_STATUS_OK) {
771 *aWriteCount = aCount;
772 return NS_OK;
775 return NS_ERROR_FAILURE;
778 int nsExpatDriver::HandleExternalEntityRef(const char16_t* openEntityNames,
779 const char16_t* base,
780 const char16_t* systemId,
781 const char16_t* publicId) {
782 if (mInInternalSubset && !mInExternalDTD && openEntityNames) {
783 mInternalSubset.Append(char16_t('%'));
784 mInternalSubset.Append(nsDependentString(openEntityNames));
785 mInternalSubset.Append(char16_t(';'));
788 nsCOMPtr<nsIURI> baseURI = GetBaseURI(base);
789 NS_ENSURE_TRUE(baseURI, 1);
791 // Load the external entity into a buffer.
792 nsCOMPtr<nsIInputStream> in;
793 nsCOMPtr<nsIURI> absURI;
794 nsresult rv = OpenInputStreamFromExternalDTD(
795 publicId, systemId, baseURI, getter_AddRefs(in), getter_AddRefs(absURI));
796 if (NS_FAILED(rv)) {
797 #ifdef DEBUG
798 nsCString message("Failed to open external DTD: publicId \"");
799 AppendUTF16toUTF8(MakeStringSpan(publicId), message);
800 message += "\" systemId \"";
801 AppendUTF16toUTF8(MakeStringSpan(systemId), message);
802 message += "\" base \"";
803 message.Append(baseURI->GetSpecOrDefault());
804 message += "\" URL \"";
805 if (absURI) {
806 message.Append(absURI->GetSpecOrDefault());
808 message += "\"";
809 NS_WARNING(message.get());
810 #endif
811 return 1;
814 nsCOMPtr<nsIUnicharInputStream> uniIn;
815 rv = NS_NewUnicharInputStream(in, getter_AddRefs(uniIn));
816 NS_ENSURE_SUCCESS(rv, 1);
818 int result = 1;
819 if (uniIn) {
820 auto utf16 = TransferBuffer<char16_t>(
821 Sandbox(), kUTF16, nsCharTraits<char16_t>::length(kUTF16) + 1);
822 NS_ENSURE_TRUE(*utf16, 1);
823 tainted_expat<XML_Parser> entParser;
824 entParser =
825 RLBOX_EXPAT_MCALL(MOZ_XML_ExternalEntityParserCreate, nullptr, *utf16);
826 if (entParser) {
827 auto baseURI = GetExpatBaseURI(absURI);
828 auto url = TransferBuffer<XML_Char>(Sandbox(), &baseURI[0],
829 ArrayLength(baseURI));
830 NS_ENSURE_TRUE(*url, 1);
831 Sandbox()->invoke_sandbox_function(MOZ_XML_SetBase, entParser, *url);
833 mInExternalDTD = true;
835 bool inParser = mInParser; // Save in-parser status
836 mInParser = true;
838 RLBoxExpatClosure closure(SandboxData(), entParser);
839 uint32_t totalRead;
840 do {
841 rv = uniIn->ReadSegments(ExternalDTDStreamReaderFunc, &closure,
842 uint32_t(-1), &totalRead);
843 } while (NS_SUCCEEDED(rv) && totalRead > 0);
845 result =
846 Sandbox()
847 ->invoke_sandbox_function(MOZ_XML_Parse, entParser, nullptr, 0, 1)
848 .copy_and_verify(status_verifier);
850 mInParser = inParser; // Restore in-parser status
851 mInExternalDTD = false;
853 Sandbox()->invoke_sandbox_function(MOZ_XML_ParserFree, entParser);
857 return result;
860 nsresult nsExpatDriver::OpenInputStreamFromExternalDTD(const char16_t* aFPIStr,
861 const char16_t* aURLStr,
862 nsIURI* aBaseURI,
863 nsIInputStream** aStream,
864 nsIURI** aAbsURI) {
865 nsCOMPtr<nsIURI> uri;
866 nsresult rv = NS_NewURI(getter_AddRefs(uri), NS_ConvertUTF16toUTF8(aURLStr),
867 nullptr, aBaseURI);
868 // Even if the URI is malformed (most likely because we have a
869 // non-hierarchical base URI and a relative DTD URI, with the latter
870 // being the normal XHTML DTD case), we can try to see whether we
871 // have catalog data for aFPIStr.
872 if (NS_WARN_IF(NS_FAILED(rv) && rv != NS_ERROR_MALFORMED_URI)) {
873 return rv;
876 // make sure the URI, if we have one, is allowed to be loaded in sync
877 bool isUIResource = false;
878 if (uri) {
879 rv = NS_URIChainHasFlags(uri, nsIProtocolHandler::URI_IS_UI_RESOURCE,
880 &isUIResource);
881 NS_ENSURE_SUCCESS(rv, rv);
884 nsCOMPtr<nsIURI> localURI;
885 if (!isUIResource) {
886 // Check to see if we can map the DTD to a known local DTD, or if a DTD
887 // file of the same name exists in the special DTD directory
888 if (aFPIStr) {
889 // see if the Formal Public Identifier (FPI) maps to a catalog entry
890 mCatalogData = LookupCatalogData(aFPIStr);
891 GetLocalDTDURI(mCatalogData, uri, getter_AddRefs(localURI));
893 if (!localURI) {
894 return NS_ERROR_NOT_IMPLEMENTED;
898 nsCOMPtr<nsIChannel> channel;
899 if (localURI) {
900 localURI.swap(uri);
901 rv = NS_NewChannel(getter_AddRefs(channel), uri,
902 nsContentUtils::GetSystemPrincipal(),
903 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
904 nsIContentPolicy::TYPE_DTD);
905 NS_ENSURE_SUCCESS(rv, rv);
906 } else {
907 NS_ASSERTION(
908 mSink == nsCOMPtr<nsIExpatSink>(do_QueryInterface(mOriginalSink)),
909 "In nsExpatDriver::OpenInputStreamFromExternalDTD: "
910 "mOriginalSink not the same object as mSink?");
911 nsContentPolicyType policyType = nsIContentPolicy::TYPE_INTERNAL_DTD;
912 if (mOriginalSink) {
913 nsCOMPtr<Document> doc;
914 doc = do_QueryInterface(mOriginalSink->GetTarget());
915 if (doc) {
916 if (doc->SkipDTDSecurityChecks()) {
917 policyType = nsIContentPolicy::TYPE_INTERNAL_FORCE_ALLOWED_DTD;
919 rv = NS_NewChannel(
920 getter_AddRefs(channel), uri, doc,
921 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT |
922 nsILoadInfo::SEC_ALLOW_CHROME,
923 policyType);
924 NS_ENSURE_SUCCESS(rv, rv);
927 if (!channel) {
928 nsCOMPtr<nsIPrincipal> nullPrincipal =
929 mozilla::NullPrincipal::CreateWithoutOriginAttributes();
930 rv = NS_NewChannel(
931 getter_AddRefs(channel), uri, nullPrincipal,
932 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT |
933 nsILoadInfo::SEC_ALLOW_CHROME,
934 policyType);
935 NS_ENSURE_SUCCESS(rv, rv);
939 uri.forget(aAbsURI);
941 channel->SetContentType("application/xml"_ns);
942 return channel->Open(aStream);
945 static nsresult CreateErrorText(const char16_t* aDescription,
946 const char16_t* aSourceURL,
947 tainted_expat<XML_Size> aLineNumber,
948 tainted_expat<XML_Size> aColNumber,
949 nsString& aErrorString, bool spoofEnglish) {
950 aErrorString.Truncate();
952 nsAutoString msg;
953 nsresult rv = nsParserMsgUtils::GetLocalizedStringByName(
954 spoofEnglish ? XMLPARSER_PROPERTIES_en_US : XMLPARSER_PROPERTIES,
955 "XMLParsingError", msg);
956 NS_ENSURE_SUCCESS(rv, rv);
958 // XML Parsing Error: %1$S\nLocation: %2$S\nLine Number %3$u, Column %4$u:
959 nsTextFormatter::ssprintf(
960 aErrorString, msg.get(), aDescription, aSourceURL,
961 aLineNumber.unverified_safe_because(RLBOX_SAFE_PRINT),
962 aColNumber.unverified_safe_because(RLBOX_SAFE_PRINT));
963 return NS_OK;
966 static nsresult AppendErrorPointer(tainted_expat<XML_Size> aColNumber,
967 const char16_t* aSourceLine,
968 size_t aSourceLineLength,
969 nsString& aSourceString) {
970 aSourceString.Append(char16_t('\n'));
972 MOZ_RELEASE_ASSERT_TAINTED(aColNumber != static_cast<XML_Size>(0),
973 "Unexpected value of column");
975 // Last character will be '^'.
976 XML_Size last =
977 (aColNumber - 1).copy_and_verify([&](XML_Size val) -> XML_Size {
978 if (val > aSourceLineLength) {
979 // Unexpected value of last column, just return a safe value
980 return 0;
982 return val;
985 XML_Size i;
986 uint32_t minuses = 0;
987 for (i = 0; i < last; ++i) {
988 if (aSourceLine[i] == '\t') {
989 // Since this uses |white-space: pre;| a tab stop equals 8 spaces.
990 uint32_t add = 8 - (minuses % 8);
991 aSourceString.AppendASCII("--------", add);
992 minuses += add;
993 } else {
994 aSourceString.Append(char16_t('-'));
995 ++minuses;
998 aSourceString.Append(char16_t('^'));
1000 return NS_OK;
1003 nsresult nsExpatDriver::HandleError() {
1004 int32_t code =
1005 RLBOX_EXPAT_MCALL(MOZ_XML_GetErrorCode).copy_and_verify(error_verifier);
1007 // Map Expat error code to an error string
1008 // XXX Deal with error returns.
1009 nsAutoString description;
1010 nsCOMPtr<Document> doc;
1011 if (mOriginalSink) {
1012 doc = do_QueryInterface(mOriginalSink->GetTarget());
1015 bool spoofEnglish =
1016 nsContentUtils::SpoofLocaleEnglish() && (!doc || !doc->AllowsL10n());
1017 nsParserMsgUtils::GetLocalizedStringByID(
1018 spoofEnglish ? XMLPARSER_PROPERTIES_en_US : XMLPARSER_PROPERTIES, code,
1019 description);
1021 if (code == XML_ERROR_TAG_MISMATCH) {
1023 * Expat can send the following:
1024 * localName
1025 * namespaceURI<separator>localName
1026 * namespaceURI<separator>localName<separator>prefix
1028 * and we use 0xFFFF for the <separator>.
1032 const char16_t* mismatch =
1033 RLBOX_EXPAT_MCALL(MOZ_XML_GetMismatchedTag)
1034 .copy_and_verify_address(unverified_xml_string);
1035 const char16_t* uriEnd = nullptr;
1036 const char16_t* nameEnd = nullptr;
1037 const char16_t* pos;
1038 for (pos = mismatch; *pos; ++pos) {
1039 if (*pos == kExpatSeparatorChar) {
1040 if (uriEnd) {
1041 nameEnd = pos;
1042 } else {
1043 uriEnd = pos;
1048 nsAutoString tagName;
1049 if (uriEnd && nameEnd) {
1050 // We have a prefix.
1051 tagName.Append(nameEnd + 1, pos - nameEnd - 1);
1052 tagName.Append(char16_t(':'));
1054 const char16_t* nameStart = uriEnd ? uriEnd + 1 : mismatch;
1055 tagName.Append(nameStart, (nameEnd ? nameEnd : pos) - nameStart);
1057 nsAutoString msg;
1058 nsParserMsgUtils::GetLocalizedStringByName(
1059 spoofEnglish ? XMLPARSER_PROPERTIES_en_US : XMLPARSER_PROPERTIES,
1060 "Expected", msg);
1062 // . Expected: </%S>.
1063 nsAutoString message;
1064 nsTextFormatter::ssprintf(message, msg.get(), tagName.get());
1065 description.Append(message);
1068 // Adjust the column number so that it is one based rather than zero based.
1069 tainted_expat<XML_Size> colNumber =
1070 RLBOX_EXPAT_MCALL(MOZ_XML_GetCurrentColumnNumber) + 1;
1071 tainted_expat<XML_Size> lineNumber =
1072 RLBOX_EXPAT_MCALL(MOZ_XML_GetCurrentLineNumber);
1074 // Copy out the two character bufer that holds the expatBase
1075 const std::unique_ptr<XML_Char[]> expatBase =
1076 RLBOX_EXPAT_MCALL(MOZ_XML_GetBase)
1077 .copy_and_verify_range(
1078 [](std::unique_ptr<XML_Char[]> val) {
1079 // No additional checks needed as this is sent to GetBaseURI
1080 // which checks its inputs
1081 return val;
1083 ExpatBaseURI::Length);
1084 nsAutoString uri;
1085 nsCOMPtr<nsIURI> baseURI;
1086 if (expatBase && (baseURI = GetBaseURI(expatBase.get()))) {
1087 // Let's ignore if this fails, we're already reporting a parse error.
1088 Unused << CopyUTF8toUTF16(baseURI->GetSpecOrDefault(), uri, fallible);
1090 nsAutoString errorText;
1091 CreateErrorText(description.get(), uri.get(), lineNumber, colNumber,
1092 errorText, spoofEnglish);
1094 nsAutoString sourceText(mLastLine);
1095 AppendErrorPointer(colNumber, mLastLine.get(), mLastLine.Length(),
1096 sourceText);
1098 if (doc && nsContentUtils::IsChromeDoc(doc)) {
1099 nsCString path = doc->GetDocumentURI()->GetSpecOrDefault();
1100 nsCOMPtr<nsISupports> container = doc->GetContainer();
1101 nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(container);
1102 nsCString docShellDestroyed("unknown"_ns);
1103 if (docShell) {
1104 bool destroyed = false;
1105 docShell->IsBeingDestroyed(&destroyed);
1106 docShellDestroyed.Assign(destroyed ? "true"_ns : "false"_ns);
1109 mozilla::Maybe<nsTArray<mozilla::Telemetry::EventExtraEntry>> extra =
1110 mozilla::Some<nsTArray<mozilla::Telemetry::EventExtraEntry>>({
1111 mozilla::Telemetry::EventExtraEntry{"error_code"_ns,
1112 nsPrintfCString("%u", code)},
1113 mozilla::Telemetry::EventExtraEntry{
1114 "location"_ns,
1115 nsPrintfCString(
1116 "%lu:%lu",
1117 lineNumber.unverified_safe_because(RLBOX_SAFE_PRINT),
1118 colNumber.unverified_safe_because(RLBOX_SAFE_PRINT))},
1119 mozilla::Telemetry::EventExtraEntry{
1120 "last_line"_ns, NS_ConvertUTF16toUTF8(mLastLine)},
1121 mozilla::Telemetry::EventExtraEntry{
1122 "last_line_len"_ns, nsPrintfCString("%zu", mLastLine.Length())},
1123 mozilla::Telemetry::EventExtraEntry{
1124 "hidden"_ns, doc->Hidden() ? "true"_ns : "false"_ns},
1125 mozilla::Telemetry::EventExtraEntry{"destroyed"_ns,
1126 docShellDestroyed},
1129 mozilla::Telemetry::SetEventRecordingEnabled("ysod"_ns, true);
1130 mozilla::Telemetry::RecordEvent(
1131 mozilla::Telemetry::EventID::Ysod_Shown_Ysod, mozilla::Some(path),
1132 extra);
1135 // Try to create and initialize the script error.
1136 nsCOMPtr<nsIScriptError> serr(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
1137 nsresult rv = NS_ERROR_FAILURE;
1138 if (serr) {
1139 rv = serr->InitWithSourceURI(
1140 errorText, mURIs.SafeElementAt(0), mLastLine,
1141 lineNumber.unverified_safe_because(RLBOX_SAFE_PRINT),
1142 colNumber.unverified_safe_because(RLBOX_SAFE_PRINT),
1143 nsIScriptError::errorFlag, "malformed-xml", mInnerWindowID);
1146 // If it didn't initialize, we can't do any logging.
1147 bool shouldReportError = NS_SUCCEEDED(rv);
1149 // mSink might be null here if our parser was terminated.
1150 if (mSink && shouldReportError) {
1151 rv = mSink->ReportError(errorText.get(), sourceText.get(), serr,
1152 &shouldReportError);
1153 if (NS_FAILED(rv)) {
1154 shouldReportError = true;
1158 // mOriginalSink might be null here if our parser was terminated.
1159 if (mOriginalSink) {
1160 nsCOMPtr<Document> doc = do_QueryInterface(mOriginalSink->GetTarget());
1161 if (doc && doc->SuppressParserErrorConsoleMessages()) {
1162 shouldReportError = false;
1166 if (shouldReportError) {
1167 nsCOMPtr<nsIConsoleService> cs(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
1168 if (cs) {
1169 cs->LogMessage(serr);
1173 return NS_ERROR_HTMLPARSER_STOPPARSING;
1176 // Because we need to allocate a buffer in the RLBOX sandbox, and copy the data
1177 // to it for Expat to parse, we are limited in size by the memory available in
1178 // the RLBOX sandbox. nsExpatDriver::ChunkAndParseBuffer divides the buffer into
1179 // chunks of sMaxChunkLength characters or less, and passes them to
1180 // nsExpatDriver::ParseBuffer. That should ensure that we almost never run out
1181 // of memory in the sandbox.
1182 void nsExpatDriver::ChunkAndParseBuffer(const char16_t* aBuffer,
1183 uint32_t aLength, bool aIsFinal,
1184 uint32_t* aPassedToExpat,
1185 uint32_t* aConsumed,
1186 XML_Size* aLastLineLength) {
1187 *aConsumed = 0;
1188 *aLastLineLength = 0;
1190 uint32_t remainder = aLength;
1191 while (remainder > sMaxChunkLength) {
1192 ParseChunk(aBuffer, sMaxChunkLength, ChunkOrBufferIsFinal::None, aConsumed,
1193 aLastLineLength);
1194 aBuffer += sMaxChunkLength;
1195 remainder -= sMaxChunkLength;
1196 if (NS_FAILED(mInternalState)) {
1197 // Stop parsing if there's an error (including if we're blocked or
1198 // interrupted).
1199 *aPassedToExpat = aLength - remainder;
1200 return;
1204 ParseChunk(aBuffer, remainder,
1205 aIsFinal ? ChunkOrBufferIsFinal::FinalChunkAndBuffer
1206 : ChunkOrBufferIsFinal::FinalChunk,
1207 aConsumed, aLastLineLength);
1208 *aPassedToExpat = aLength;
1211 void nsExpatDriver::ParseChunk(const char16_t* aBuffer, uint32_t aLength,
1212 ChunkOrBufferIsFinal aIsFinal,
1213 uint32_t* aConsumed, XML_Size* aLastLineLength) {
1214 NS_ASSERTION((aBuffer && aLength != 0) || (!aBuffer && aLength == 0), "?");
1215 NS_ASSERTION(mInternalState != NS_OK ||
1216 (aIsFinal == ChunkOrBufferIsFinal::FinalChunkAndBuffer) ||
1217 aBuffer,
1218 "Useless call, we won't call Expat");
1219 MOZ_ASSERT(!BlockedOrInterrupted() || !aBuffer,
1220 "Non-null buffer when resuming");
1221 MOZ_ASSERT(mExpatParser);
1223 auto parserBytesBefore_verifier = [&](auto parserBytesBefore) {
1224 MOZ_RELEASE_ASSERT(parserBytesBefore >= 0, "Unexpected value");
1225 MOZ_RELEASE_ASSERT(parserBytesBefore % sizeof(char16_t) == 0,
1226 "Consumed part of a char16_t?");
1227 return parserBytesBefore;
1229 int32_t parserBytesBefore = RLBOX_EXPAT_SAFE_MCALL(
1230 XML_GetCurrentByteIndex, parserBytesBefore_verifier);
1232 if (mInternalState != NS_OK && !BlockedOrInterrupted()) {
1233 return;
1236 XML_Status status;
1237 bool inParser = mInParser; // Save in-parser status
1238 mInParser = true;
1239 Maybe<TransferBuffer<char16_t>> buffer;
1240 if (BlockedOrInterrupted()) {
1241 mInternalState = NS_OK; // Resume in case we're blocked.
1242 status = RLBOX_EXPAT_SAFE_MCALL(MOZ_XML_ResumeParser, status_verifier);
1243 } else {
1244 buffer.emplace(Sandbox(), aBuffer, aLength);
1245 MOZ_RELEASE_ASSERT(!aBuffer || !!*buffer.ref(),
1246 "Chunking should avoid OOM in ParseBuffer");
1248 status = RLBOX_EXPAT_SAFE_MCALL(
1249 MOZ_XML_Parse, status_verifier,
1250 rlbox::sandbox_reinterpret_cast<const char*>(*buffer.ref()),
1251 aLength * sizeof(char16_t),
1252 aIsFinal == ChunkOrBufferIsFinal::FinalChunkAndBuffer);
1254 mInParser = inParser; // Restore in-parser status
1256 auto parserBytesConsumed_verifier = [&](auto parserBytesConsumed) {
1257 MOZ_RELEASE_ASSERT(parserBytesConsumed >= 0, "Unexpected value");
1258 MOZ_RELEASE_ASSERT(parserBytesConsumed >= parserBytesBefore,
1259 "How'd this happen?");
1260 MOZ_RELEASE_ASSERT(parserBytesConsumed % sizeof(char16_t) == 0,
1261 "Consumed part of a char16_t?");
1262 return parserBytesConsumed;
1264 int32_t parserBytesConsumed = RLBOX_EXPAT_SAFE_MCALL(
1265 XML_GetCurrentByteIndex, parserBytesConsumed_verifier);
1267 // Consumed something.
1268 *aConsumed += (parserBytesConsumed - parserBytesBefore) / sizeof(char16_t);
1270 NS_ASSERTION(status != XML_STATUS_SUSPENDED || BlockedOrInterrupted(),
1271 "Inconsistent expat suspension state.");
1273 if (status == XML_STATUS_ERROR) {
1274 mInternalState = NS_ERROR_HTMLPARSER_STOPPARSING;
1277 if (*aConsumed > 0 &&
1278 (aIsFinal != ChunkOrBufferIsFinal::None || NS_FAILED(mInternalState))) {
1279 *aLastLineLength = RLBOX_EXPAT_SAFE_MCALL(MOZ_XML_GetCurrentColumnNumber,
1280 safe_unverified<XML_Size>);
1284 nsresult nsExpatDriver::ResumeParse(nsScanner& aScanner, bool aIsFinalChunk) {
1285 // We keep the scanner pointing to the position where Expat will start
1286 // parsing.
1287 nsScannerIterator currentExpatPosition;
1288 aScanner.CurrentPosition(currentExpatPosition);
1290 // This is the start of the first buffer that we need to pass to Expat.
1291 nsScannerIterator start = currentExpatPosition;
1292 start.advance(mExpatBuffered);
1294 // This is the end of the last buffer (at this point, more data could come in
1295 // later).
1296 nsScannerIterator end;
1297 aScanner.EndReading(end);
1299 MOZ_LOG(gExpatDriverLog, LogLevel::Debug,
1300 ("Remaining in expat's buffer: %i, remaining in scanner: %zu.",
1301 mExpatBuffered, Distance(start, end)));
1303 // We want to call Expat if we have more buffers, or if we know there won't
1304 // be more buffers (and so we want to flush the remaining data), or if we're
1305 // currently blocked and there's data in Expat's buffer.
1306 while (start != end || (aIsFinalChunk && !mMadeFinalCallToExpat) ||
1307 (BlockedOrInterrupted() && mExpatBuffered > 0)) {
1308 bool noMoreBuffers = start == end && aIsFinalChunk;
1309 bool blocked = BlockedOrInterrupted();
1311 const char16_t* buffer;
1312 uint32_t length;
1313 if (blocked || noMoreBuffers) {
1314 // If we're blocked we just resume Expat so we don't need a buffer, if
1315 // there aren't any more buffers we pass a null buffer to Expat.
1316 buffer = nullptr;
1317 length = 0;
1319 if (blocked) {
1320 MOZ_LOG(
1321 gExpatDriverLog, LogLevel::Debug,
1322 ("Resuming Expat, will parse data remaining in Expat's "
1323 "buffer.\nContent of Expat's buffer:\n-----\n%s\n-----\n",
1324 NS_ConvertUTF16toUTF8(currentExpatPosition.get(), mExpatBuffered)
1325 .get()));
1326 } else {
1327 NS_ASSERTION(mExpatBuffered == Distance(currentExpatPosition, end),
1328 "Didn't pass all the data to Expat?");
1329 MOZ_LOG(
1330 gExpatDriverLog, LogLevel::Debug,
1331 ("Last call to Expat, will parse data remaining in Expat's "
1332 "buffer.\nContent of Expat's buffer:\n-----\n%s\n-----\n",
1333 NS_ConvertUTF16toUTF8(currentExpatPosition.get(), mExpatBuffered)
1334 .get()));
1336 } else {
1337 buffer = start.get();
1338 length = uint32_t(start.size_forward());
1340 MOZ_LOG(gExpatDriverLog, LogLevel::Debug,
1341 ("Calling Expat, will parse data remaining in Expat's buffer and "
1342 "new data.\nContent of Expat's buffer:\n-----\n%s\n-----\nNew "
1343 "data:\n-----\n%s\n-----\n",
1344 NS_ConvertUTF16toUTF8(currentExpatPosition.get(), mExpatBuffered)
1345 .get(),
1346 NS_ConvertUTF16toUTF8(start.get(), length).get()));
1349 uint32_t passedToExpat;
1350 uint32_t consumed;
1351 XML_Size lastLineLength;
1352 ChunkAndParseBuffer(buffer, length, noMoreBuffers, &passedToExpat,
1353 &consumed, &lastLineLength);
1354 MOZ_ASSERT_IF(passedToExpat != length, NS_FAILED(mInternalState));
1355 MOZ_ASSERT(consumed <= passedToExpat + mExpatBuffered);
1356 if (consumed > 0) {
1357 nsScannerIterator oldExpatPosition = currentExpatPosition;
1358 currentExpatPosition.advance(consumed);
1360 // We consumed some data, we want to store the last line of data that
1361 // was consumed in case we run into an error (to show the line in which
1362 // the error occurred).
1364 if (lastLineLength <= consumed) {
1365 // The length of the last line was less than what expat consumed, so
1366 // there was at least one line break in the consumed data. Store the
1367 // last line until the point where we stopped parsing.
1368 nsScannerIterator startLastLine = currentExpatPosition;
1369 startLastLine.advance(-((ptrdiff_t)lastLineLength));
1370 if (!CopyUnicodeTo(startLastLine, currentExpatPosition, mLastLine)) {
1371 return (mInternalState = NS_ERROR_OUT_OF_MEMORY);
1373 } else {
1374 // There was no line break in the consumed data, append the consumed
1375 // data.
1376 if (!AppendUnicodeTo(oldExpatPosition, currentExpatPosition,
1377 mLastLine)) {
1378 return (mInternalState = NS_ERROR_OUT_OF_MEMORY);
1383 mExpatBuffered += passedToExpat - consumed;
1385 if (BlockedOrInterrupted()) {
1386 MOZ_LOG(gExpatDriverLog, LogLevel::Debug,
1387 ("Blocked or interrupted parser (probably for loading linked "
1388 "stylesheets or scripts)."));
1390 aScanner.SetPosition(currentExpatPosition, true);
1391 aScanner.Mark();
1393 return mInternalState;
1396 if (noMoreBuffers && mExpatBuffered == 0) {
1397 mMadeFinalCallToExpat = true;
1400 if (NS_FAILED(mInternalState)) {
1401 if (RLBOX_EXPAT_SAFE_MCALL(MOZ_XML_GetErrorCode, error_verifier) !=
1402 XML_ERROR_NONE) {
1403 NS_ASSERTION(mInternalState == NS_ERROR_HTMLPARSER_STOPPARSING,
1404 "Unexpected error");
1406 // Look for the next newline after the last one we consumed
1407 nsScannerIterator lastLine = currentExpatPosition;
1408 while (lastLine != end) {
1409 length = uint32_t(lastLine.size_forward());
1410 uint32_t endOffset = 0;
1411 const char16_t* buffer = lastLine.get();
1412 while (endOffset < length && buffer[endOffset] != '\n' &&
1413 buffer[endOffset] != '\r') {
1414 ++endOffset;
1416 mLastLine.Append(Substring(buffer, buffer + endOffset));
1417 if (endOffset < length) {
1418 // We found a newline.
1419 break;
1422 lastLine.advance(length);
1425 HandleError();
1428 return mInternalState;
1431 // Either we have more buffers, or we were blocked (and we'll flush in the
1432 // next iteration), or we should have emptied Expat's buffer.
1433 NS_ASSERTION(!noMoreBuffers || blocked ||
1434 (mExpatBuffered == 0 && currentExpatPosition == end),
1435 "Unreachable data left in Expat's buffer");
1437 start.advance(length);
1439 // It's possible for start to have passed end if we received more data
1440 // (e.g. if we spun the event loop in an inline script). Reload end now
1441 // to compensate.
1442 aScanner.EndReading(end);
1445 aScanner.SetPosition(currentExpatPosition, true);
1446 aScanner.Mark();
1448 MOZ_LOG(gExpatDriverLog, LogLevel::Debug,
1449 ("Remaining in expat's buffer: %i, remaining in scanner: %zu.",
1450 mExpatBuffered, Distance(currentExpatPosition, end)));
1452 return NS_SUCCEEDED(mInternalState) ? NS_ERROR_HTMLPARSER_EOF : NS_OK;
1455 mozilla::UniquePtr<mozilla::RLBoxSandboxDataBase>
1456 RLBoxExpatSandboxPool::CreateSandboxData(uint64_t aSize) {
1457 // Create expat sandbox
1458 auto sandbox = mozilla::MakeUnique<rlbox_sandbox_expat>();
1460 #ifdef MOZ_WASM_SANDBOXING_EXPAT
1461 const w2c_mem_capacity capacity =
1462 get_valid_wasm2c_memory_capacity(aSize, true /* 32-bit wasm memory*/);
1463 bool create_ok = sandbox->create_sandbox(/* infallible = */ false, &capacity);
1464 #else
1465 bool create_ok = sandbox->create_sandbox();
1466 #endif
1468 NS_ENSURE_TRUE(create_ok, nullptr);
1470 mozilla::UniquePtr<RLBoxExpatSandboxData> sbxData =
1471 mozilla::MakeUnique<RLBoxExpatSandboxData>(aSize);
1473 // Register callbacks common to both system and non-system principals
1474 sbxData->mHandleXMLDeclaration =
1475 sandbox->register_callback(Driver_HandleXMLDeclaration);
1476 sbxData->mHandleCharacterData =
1477 sandbox->register_callback(Driver_HandleCharacterData);
1478 sbxData->mHandleProcessingInstruction =
1479 sandbox->register_callback(Driver_HandleProcessingInstruction);
1480 sbxData->mHandleDefault = sandbox->register_callback(Driver_HandleDefault);
1481 sbxData->mHandleExternalEntityRef =
1482 sandbox->register_callback(Driver_HandleExternalEntityRef);
1483 sbxData->mHandleComment = sandbox->register_callback(Driver_HandleComment);
1484 sbxData->mHandleStartCdataSection =
1485 sandbox->register_callback(Driver_HandleStartCdataSection);
1486 sbxData->mHandleEndCdataSection =
1487 sandbox->register_callback(Driver_HandleEndCdataSection);
1488 sbxData->mHandleStartDoctypeDecl =
1489 sandbox->register_callback(Driver_HandleStartDoctypeDecl);
1490 sbxData->mHandleEndDoctypeDecl =
1491 sandbox->register_callback(Driver_HandleEndDoctypeDecl);
1493 sbxData->mSandbox = std::move(sandbox);
1495 return sbxData;
1498 mozilla::StaticRefPtr<RLBoxExpatSandboxPool> RLBoxExpatSandboxPool::sSingleton;
1500 void RLBoxExpatSandboxPool::Initialize(size_t aDelaySeconds) {
1501 mozilla::AssertIsOnMainThread();
1502 RLBoxExpatSandboxPool::sSingleton = new RLBoxExpatSandboxPool(aDelaySeconds);
1503 ClearOnShutdown(&RLBoxExpatSandboxPool::sSingleton);
1506 void RLBoxExpatSandboxData::AttachDriver(bool aIsSystemPrincipal,
1507 void* aDriver) {
1508 MOZ_ASSERT(!mSandbox->sandbox_storage);
1509 MOZ_ASSERT(mHandleStartElement.is_unregistered());
1510 MOZ_ASSERT(mHandleEndElement.is_unregistered());
1512 if (aIsSystemPrincipal) {
1513 mHandleStartElement = mSandbox->register_callback(
1514 nsExpatDriver::HandleStartElementForSystemPrincipal);
1515 mHandleEndElement = mSandbox->register_callback(
1516 nsExpatDriver::HandleEndElementForSystemPrincipal);
1517 } else {
1518 mHandleStartElement =
1519 mSandbox->register_callback(nsExpatDriver::HandleStartElement);
1520 mHandleEndElement =
1521 mSandbox->register_callback(nsExpatDriver::HandleEndElement);
1524 mSandbox->sandbox_storage = aDriver;
1527 void RLBoxExpatSandboxData::DetachDriver() {
1528 mSandbox->sandbox_storage = nullptr;
1529 mHandleStartElement.unregister();
1530 mHandleEndElement.unregister();
1533 RLBoxExpatSandboxData::~RLBoxExpatSandboxData() {
1534 MOZ_ASSERT(mSandbox);
1536 // DetachDriver should always be called before a sandbox goes back into the
1537 // pool, and thus before it's freed.
1538 MOZ_ASSERT(!mSandbox->sandbox_storage);
1539 MOZ_ASSERT(mHandleStartElement.is_unregistered());
1540 MOZ_ASSERT(mHandleEndElement.is_unregistered());
1542 // Unregister callbacks
1543 mHandleXMLDeclaration.unregister();
1544 mHandleCharacterData.unregister();
1545 mHandleProcessingInstruction.unregister();
1546 mHandleDefault.unregister();
1547 mHandleExternalEntityRef.unregister();
1548 mHandleComment.unregister();
1549 mHandleStartCdataSection.unregister();
1550 mHandleEndCdataSection.unregister();
1551 mHandleStartDoctypeDecl.unregister();
1552 mHandleEndDoctypeDecl.unregister();
1553 // Destroy sandbox
1554 mSandbox->destroy_sandbox();
1555 MOZ_COUNT_DTOR(RLBoxExpatSandboxData);
1558 nsresult nsExpatDriver::Initialize(nsIURI* aURI, nsIContentSink* aSink) {
1559 mSink = do_QueryInterface(aSink);
1560 if (!mSink) {
1561 NS_ERROR("nsExpatDriver didn't get an nsIExpatSink");
1562 // Make sure future calls to us bail out as needed
1563 mInternalState = NS_ERROR_UNEXPECTED;
1564 return mInternalState;
1567 mOriginalSink = aSink;
1569 static const char16_t kExpatSeparator[] = {kExpatSeparatorChar, '\0'};
1571 // Get the doc if any
1572 nsCOMPtr<Document> doc = do_QueryInterface(mOriginalSink->GetTarget());
1573 if (doc) {
1574 nsCOMPtr<nsPIDOMWindowOuter> win = doc->GetWindow();
1575 nsCOMPtr<nsPIDOMWindowInner> inner;
1576 if (win) {
1577 inner = win->GetCurrentInnerWindow();
1578 } else {
1579 bool aHasHadScriptHandlingObject;
1580 nsIScriptGlobalObject* global =
1581 doc->GetScriptHandlingObject(aHasHadScriptHandlingObject);
1582 if (global) {
1583 inner = do_QueryInterface(global);
1586 if (inner) {
1587 mInnerWindowID = inner->WindowID();
1591 // Create sandbox
1593 // We have to make sure the sandbox is large enough. We unscientifically
1594 // request two MB. Note that the parsing itself is chunked so as not to
1595 // require a large sandbox.
1596 static const uint64_t minSandboxSize = 2 * 1024 * 1024;
1597 MOZ_ASSERT(!mSandboxPoolData);
1598 mSandboxPoolData =
1599 RLBoxExpatSandboxPool::sSingleton->PopOrCreate(minSandboxSize);
1600 NS_ENSURE_TRUE(mSandboxPoolData, NS_ERROR_OUT_OF_MEMORY);
1602 MOZ_ASSERT(SandboxData());
1604 SandboxData()->AttachDriver(doc && doc->NodePrincipal()->IsSystemPrincipal(),
1605 static_cast<void*>(this));
1607 // Create expat parser.
1608 // We need to copy the encoding and namespace separator into the sandbox.
1609 // For the noop sandbox we pass in the memsuite; for the Wasm sandbox, we
1610 // pass in nullptr to let expat use the standard library memory suite.
1611 auto expatSeparator = TransferBuffer<char16_t>(
1612 Sandbox(), kExpatSeparator,
1613 nsCharTraits<char16_t>::length(kExpatSeparator) + 1);
1614 MOZ_RELEASE_ASSERT(*expatSeparator);
1615 auto utf16 = TransferBuffer<char16_t>(
1616 Sandbox(), kUTF16, nsCharTraits<char16_t>::length(kUTF16) + 1);
1617 MOZ_RELEASE_ASSERT(*utf16);
1618 mExpatParser = Sandbox()->invoke_sandbox_function(
1619 MOZ_XML_ParserCreate_MM, *utf16, nullptr, *expatSeparator);
1620 NS_ENSURE_TRUE(mExpatParser, NS_ERROR_FAILURE);
1622 RLBOX_EXPAT_MCALL(MOZ_XML_SetReturnNSTriplet, XML_TRUE);
1624 #ifdef XML_DTD
1625 RLBOX_EXPAT_MCALL(MOZ_XML_SetParamEntityParsing,
1626 XML_PARAM_ENTITY_PARSING_ALWAYS);
1627 #endif
1629 auto baseURI = GetExpatBaseURI(aURI);
1630 auto uri =
1631 TransferBuffer<XML_Char>(Sandbox(), &baseURI[0], ArrayLength(baseURI));
1632 RLBOX_EXPAT_MCALL(MOZ_XML_SetBase, *uri);
1634 // Set up the callbacks
1635 RLBOX_EXPAT_MCALL(MOZ_XML_SetXmlDeclHandler,
1636 SandboxData()->mHandleXMLDeclaration);
1637 RLBOX_EXPAT_MCALL(MOZ_XML_SetElementHandler,
1638 SandboxData()->mHandleStartElement,
1639 SandboxData()->mHandleEndElement);
1640 RLBOX_EXPAT_MCALL(MOZ_XML_SetCharacterDataHandler,
1641 SandboxData()->mHandleCharacterData);
1642 RLBOX_EXPAT_MCALL(MOZ_XML_SetProcessingInstructionHandler,
1643 SandboxData()->mHandleProcessingInstruction);
1644 RLBOX_EXPAT_MCALL(MOZ_XML_SetDefaultHandlerExpand,
1645 SandboxData()->mHandleDefault);
1646 RLBOX_EXPAT_MCALL(MOZ_XML_SetExternalEntityRefHandler,
1647 SandboxData()->mHandleExternalEntityRef);
1648 RLBOX_EXPAT_MCALL(MOZ_XML_SetCommentHandler, SandboxData()->mHandleComment);
1649 RLBOX_EXPAT_MCALL(MOZ_XML_SetCdataSectionHandler,
1650 SandboxData()->mHandleStartCdataSection,
1651 SandboxData()->mHandleEndCdataSection);
1653 RLBOX_EXPAT_MCALL(MOZ_XML_SetParamEntityParsing,
1654 XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE);
1655 RLBOX_EXPAT_MCALL(MOZ_XML_SetDoctypeDeclHandler,
1656 SandboxData()->mHandleStartDoctypeDecl,
1657 SandboxData()->mHandleEndDoctypeDecl);
1659 return mInternalState;
1662 NS_IMETHODIMP
1663 nsExpatDriver::BuildModel(nsIContentSink* aSink) { return mInternalState; }
1665 void nsExpatDriver::DidBuildModel() {
1666 if (!mInParser) {
1667 // Because nsExpatDriver is cycle-collected, it gets destroyed
1668 // asynchronously. We want to eagerly release the sandbox back into the
1669 // pool so that it can be reused immediately, unless this is a reentrant
1670 // call (which we track with mInParser).
1671 Destroy();
1673 mOriginalSink = nullptr;
1674 mSink = nullptr;
1677 NS_IMETHODIMP_(void)
1678 nsExpatDriver::Terminate() {
1679 // XXX - not sure what happens to the unparsed data.
1680 if (mExpatParser) {
1681 RLBOX_EXPAT_MCALL(MOZ_XML_StopParser, XML_FALSE);
1683 mInternalState = NS_ERROR_HTMLPARSER_STOPPARSING;
1686 /*************************** Unused methods **********************************/
1688 void nsExpatDriver::MaybeStopParser(nsresult aState) {
1689 if (NS_FAILED(aState)) {
1690 // If we had a failure we want to override NS_ERROR_HTMLPARSER_INTERRUPTED
1691 // and we want to override NS_ERROR_HTMLPARSER_BLOCK but not with
1692 // NS_ERROR_HTMLPARSER_INTERRUPTED.
1693 if (NS_SUCCEEDED(mInternalState) ||
1694 mInternalState == NS_ERROR_HTMLPARSER_INTERRUPTED ||
1695 (mInternalState == NS_ERROR_HTMLPARSER_BLOCK &&
1696 aState != NS_ERROR_HTMLPARSER_INTERRUPTED)) {
1697 mInternalState = (aState == NS_ERROR_HTMLPARSER_INTERRUPTED ||
1698 aState == NS_ERROR_HTMLPARSER_BLOCK)
1699 ? aState
1700 : NS_ERROR_HTMLPARSER_STOPPARSING;
1703 // If we get an error then we need to stop Expat (by calling XML_StopParser
1704 // with false as the last argument). If the parser should be blocked or
1705 // interrupted we need to pause Expat (by calling XML_StopParser with
1706 // true as the last argument).
1708 // Note that due to Bug 1742913, we need to explicitly cast the parameter to
1709 // an int so that the value is correctly zero extended.
1710 int resumable = BlockedOrInterrupted();
1711 RLBOX_EXPAT_MCALL(MOZ_XML_StopParser, resumable);
1712 } else if (NS_SUCCEEDED(mInternalState)) {
1713 // Only clobber mInternalState with the success code if we didn't block or
1714 // interrupt before.
1715 mInternalState = aState;
1719 nsExpatDriver::ExpatBaseURI nsExpatDriver::GetExpatBaseURI(nsIURI* aURI) {
1720 mURIs.AppendElement(aURI);
1722 MOZ_RELEASE_ASSERT(mURIs.Length() <= std::numeric_limits<XML_Char>::max());
1724 return ExpatBaseURI(static_cast<XML_Char>(mURIs.Length()), XML_T('\0'));
1727 nsIURI* nsExpatDriver::GetBaseURI(const XML_Char* aBase) const {
1728 MOZ_ASSERT(aBase[0] != '\0' && aBase[1] == '\0');
1730 if (aBase[0] == '\0' || aBase[1] != '\0') {
1731 return nullptr;
1734 uint32_t index = aBase[0] - 1;
1735 MOZ_ASSERT(index < mURIs.Length());
1737 return mURIs.SafeElementAt(index);
1740 inline RLBoxExpatSandboxData* nsExpatDriver::SandboxData() const {
1741 return reinterpret_cast<RLBoxExpatSandboxData*>(
1742 mSandboxPoolData->SandboxData());
1745 inline rlbox_sandbox_expat* nsExpatDriver::Sandbox() const {
1746 return SandboxData()->Sandbox();