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"
9 #include "CParserContext.h"
10 #include "nsIExpatSink.h"
11 #include "nsIContentSink.h"
12 #include "nsIDocShell.h"
13 #include "nsParserMsgUtils.h"
15 #include "nsIUnicharInputStream.h"
16 #include "nsIProtocolHandler.h"
17 #include "nsNetUtil.h"
19 #include "nsTextFormatter.h"
20 #include "nsDirectoryServiceDefs.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"
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
;
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.
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
);
63 static const uint32_t sMaxChunkLength
= (128 * 1024) / sizeof(char16_t
);
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, ...) \
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"), \
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
) {
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");
112 /* error_verifier is a type validator for XML_Error */
113 inline enum XML_Error
error_verifier(enum XML_Error code
) {
115 code
>= XML_ERROR_NONE
&& code
<= XML_ERROR_INVALID_ARGUMENT
,
116 "unexpected XML error 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
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
);
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");
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
);
173 // aData is not null terminated; even with bad length we will not span beyond
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
);
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
);
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
);
208 // aData is not null terminated; even with bad length we will not span
209 // beyond sandbox boundary
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
);
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
);
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
);
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
);
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
);
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
,
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
283 // XXX The cleanest solution here would be to fix Bug 98413: Implement XML
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
311 const nsCatalogData
* data
= kCatalogTable
;
312 while (data
->mPublicID
) {
313 if (publicID
.EqualsASCII(data
->mPublicID
)) {
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
,
330 nsAutoCString fileName
;
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
);
343 // Not a URL with a filename, or maybe it was null. Either way, nothing
344 // else we can do here.
348 dtdURL
->GetFileName(fileName
);
349 if (fileName
.IsEmpty()) {
354 nsAutoCString
respath("resource://gre/res/dtd/");
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
)
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),
374 mInInternalSubset(false),
375 mInExternalDTD(false),
376 mMadeFinalCallToExpat(false),
378 mInternalState(NS_OK
),
381 mCatalogData(nullptr),
384 nsExpatDriver::~nsExpatDriver() { Destroy(); }
386 void nsExpatDriver::Destroy() {
387 if (mSandboxPoolData
) {
388 SandboxData()->DetachDriver();
390 RLBOX_EXPAT_MCALL(MOZ_XML_ParserFree
);
393 mSandboxPoolData
.reset();
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
404 const char16_t
** Init(size_t size
) {
405 if (size
<= NUM_STACK_SLOTS
) {
408 mHeapPtr
= mozilla::MakeUnique
<const char16_t
*[]>(size
);
409 return mHeapPtr
.get();
413 const char16_t
* mInlineArr
[NUM_STACK_SLOTS
];
414 mozilla::UniquePtr
<const char16_t
*[]> mHeapPtr
;
415 #undef NUM_STACK_SLOTS
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
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");
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
);
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;
466 // We store the tagdepth in a PRUint16, so make sure the limit fits in a
470 std::numeric_limits
<decltype(nsExpatDriver::mTagDepth
)>::max());
472 if (++self
->mTagDepth
> sMaxXMLTreeDepth
) {
473 self
->MaybeStopParser(NS_ERROR_HTMLPARSER_HIERARCHYTOODEEP
);
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
);
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
);
494 if (!RLBOX_EXPAT_SAFE_CALL(MOZ_XML_ProcessingEntityValue
,
495 safe_unverified
<XML_Bool
>)) {
496 HandleStartElement(aSandbox
, aUserData
, aName
, aAttrs
);
498 nsCOMPtr
<Document
> doc
=
499 do_QueryInterface(self
->mOriginalSink
->GetTarget());
501 // Adjust the column number so that it is one based rather than zero
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
);
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
);
515 error
.AppendLiteral("Ignoring element <");
517 error
.Append(prefix
->GetUTF16String());
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
));
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
);
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
);
545 self
->MaybeStopParser(rv
);
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
);
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!");
566 if (!mCDataText
.Append(aValue
, aLength
, fallible
)) {
567 MaybeStopParser(NS_ERROR_OUT_OF_MEMORY
);
570 nsresult rv
= mSink
->HandleCharacterData(aValue
, aLength
);
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
585 if (mInInternalSubset
) {
586 mInternalSubset
.AppendLiteral("<!--");
587 mInternalSubset
.Append(aValue
);
588 mInternalSubset
.AppendLiteral("-->");
590 nsresult rv
= mSink
->HandleComment(aValue
);
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
607 if (mInInternalSubset
) {
608 mInternalSubset
.AppendLiteral("<?");
609 mInternalSubset
.Append(aTarget
);
610 mInternalSubset
.Append(' ');
611 mInternalSubset
.Append(aData
);
612 mInternalSubset
.AppendLiteral("?>");
614 nsresult rv
= mSink
->HandleProcessingInstruction(aTarget
, aData
);
621 nsresult
nsExpatDriver::HandleXMLDeclaration(const char16_t
* aVersion
,
622 const char16_t
* aEncoding
,
623 int32_t aStandalone
) {
625 nsresult rv
= mSink
->HandleXMLDeclaration(aVersion
, aEncoding
, aStandalone
);
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
641 if (mInInternalSubset
) {
642 mInternalSubset
.Append(aValue
, aLength
);
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);
657 nsresult
nsExpatDriver::HandleStartCdataSection() {
663 nsresult
nsExpatDriver::HandleEndCdataSection() {
664 NS_ASSERTION(mSink
, "content sink not found!");
669 mSink
->HandleCDataSection(mCDataText
.get(), mCDataText
.Length());
672 mCDataText
.Truncate();
677 nsresult
nsExpatDriver::HandleStartDoctypeDecl(const char16_t
* aDoctypeName
,
678 const char16_t
* aSysid
,
679 const char16_t
* aPubid
,
680 bool aHasInternalSubset
) {
681 mDoctypeName
= aDoctypeName
;
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);
692 // Distinguish missing internal subset from an empty one
693 mInternalSubset
.SetIsVoid(true);
699 nsresult
nsExpatDriver::HandleEndDoctypeDecl() {
700 NS_ASSERTION(mSink
, "content sink not found!");
702 mInInternalSubset
= false;
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
;
710 if (mCatalogData
&& mCatalogData
->mAgentSheet
) {
711 NS_NewURI(getter_AddRefs(data
), mCatalogData
->mAgentSheet
);
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
);
726 mInternalSubset
.Truncate();
731 // Wrapper class for passing the sandbox data and parser as a closure to
732 // ExternalDTDStreamReaderFunc.
733 class RLBoxExpatClosure
{
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
; };
742 RLBoxExpatSandboxData
* mSbxData
;
743 tainted_expat
<XML_Parser
> mExpatParser
;
746 static nsresult
ExternalDTDStreamReaderFunc(nsIUnicharInputStream
* aIn
,
748 const char16_t
* aFromSegment
,
749 uint32_t aToOffset
, uint32_t aCount
,
750 uint32_t* aWriteCount
) {
751 MOZ_ASSERT(aClosure
&& aFromSegment
&& aWriteCount
);
755 // Get sandbox and parser
756 auto* closure
= reinterpret_cast<RLBoxExpatClosure
*>(aClosure
);
759 // Transfer segment into the sandbox
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
;
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
));
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 \"";
806 message
.Append(absURI
->GetSpecOrDefault());
809 NS_WARNING(message
.get());
814 nsCOMPtr
<nsIUnicharInputStream
> uniIn
;
815 rv
= NS_NewUnicharInputStream(in
, getter_AddRefs(uniIn
));
816 NS_ENSURE_SUCCESS(rv
, 1);
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
;
825 RLBOX_EXPAT_MCALL(MOZ_XML_ExternalEntityParserCreate
, nullptr, *utf16
);
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
838 RLBoxExpatClosure
closure(SandboxData(), entParser
);
841 rv
= uniIn
->ReadSegments(ExternalDTDStreamReaderFunc
, &closure
,
842 uint32_t(-1), &totalRead
);
843 } while (NS_SUCCEEDED(rv
) && totalRead
> 0);
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
);
860 nsresult
nsExpatDriver::OpenInputStreamFromExternalDTD(const char16_t
* aFPIStr
,
861 const char16_t
* aURLStr
,
863 nsIInputStream
** aStream
,
865 nsCOMPtr
<nsIURI
> uri
;
866 nsresult rv
= NS_NewURI(getter_AddRefs(uri
), NS_ConvertUTF16toUTF8(aURLStr
),
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
)) {
876 // make sure the URI, if we have one, is allowed to be loaded in sync
877 bool isUIResource
= false;
879 rv
= NS_URIChainHasFlags(uri
, nsIProtocolHandler::URI_IS_UI_RESOURCE
,
881 NS_ENSURE_SUCCESS(rv
, rv
);
884 nsCOMPtr
<nsIURI
> localURI
;
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
889 // see if the Formal Public Identifier (FPI) maps to a catalog entry
890 mCatalogData
= LookupCatalogData(aFPIStr
);
891 GetLocalDTDURI(mCatalogData
, uri
, getter_AddRefs(localURI
));
894 return NS_ERROR_NOT_IMPLEMENTED
;
898 nsCOMPtr
<nsIChannel
> channel
;
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
);
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
;
913 nsCOMPtr
<Document
> doc
;
914 doc
= do_QueryInterface(mOriginalSink
->GetTarget());
916 if (doc
->SkipDTDSecurityChecks()) {
917 policyType
= nsIContentPolicy::TYPE_INTERNAL_FORCE_ALLOWED_DTD
;
920 getter_AddRefs(channel
), uri
, doc
,
921 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT
|
922 nsILoadInfo::SEC_ALLOW_CHROME
,
924 NS_ENSURE_SUCCESS(rv
, rv
);
928 nsCOMPtr
<nsIPrincipal
> nullPrincipal
=
929 mozilla::NullPrincipal::CreateWithoutOriginAttributes();
931 getter_AddRefs(channel
), uri
, nullPrincipal
,
932 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT
|
933 nsILoadInfo::SEC_ALLOW_CHROME
,
935 NS_ENSURE_SUCCESS(rv
, rv
);
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();
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
));
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 '^'.
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
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
);
994 aSourceString
.Append(char16_t('-'));
998 aSourceString
.Append(char16_t('^'));
1003 nsresult
nsExpatDriver::HandleError() {
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());
1016 nsContentUtils::SpoofLocaleEnglish() && (!doc
|| !doc
->AllowsL10n());
1017 nsParserMsgUtils::GetLocalizedStringByID(
1018 spoofEnglish
? XMLPARSER_PROPERTIES_en_US
: XMLPARSER_PROPERTIES
, code
,
1021 if (code
== XML_ERROR_TAG_MISMATCH
) {
1023 * Expat can send the following:
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
) {
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
);
1058 nsParserMsgUtils::GetLocalizedStringByName(
1059 spoofEnglish
? XMLPARSER_PROPERTIES_en_US
: XMLPARSER_PROPERTIES
,
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
1083 ExpatBaseURI::Length
);
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(),
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
);
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
{
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
,
1129 mozilla::Telemetry::SetEventRecordingEnabled("ysod"_ns
, true);
1130 mozilla::Telemetry::RecordEvent(
1131 mozilla::Telemetry::EventID::Ysod_Shown_Ysod
, mozilla::Some(path
),
1135 // Try to create and initialize the script error.
1136 nsCOMPtr
<nsIScriptError
> serr(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID
));
1137 nsresult rv
= NS_ERROR_FAILURE
;
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
));
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
) {
1188 *aLastLineLength
= 0;
1190 uint32_t remainder
= aLength
;
1191 while (remainder
> sMaxChunkLength
) {
1192 ParseChunk(aBuffer
, sMaxChunkLength
, ChunkOrBufferIsFinal::None
, aConsumed
,
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
1199 *aPassedToExpat
= aLength
- remainder
;
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
) ||
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()) {
1237 bool inParser
= mInParser
; // Save in-parser status
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
);
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
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
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
;
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.
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
)
1327 NS_ASSERTION(mExpatBuffered
== Distance(currentExpatPosition
, end
),
1328 "Didn't pass all the data to Expat?");
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
)
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
)
1346 NS_ConvertUTF16toUTF8(start
.get(), length
).get()));
1349 uint32_t passedToExpat
;
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
);
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
);
1374 // There was no line break in the consumed data, append the consumed
1376 if (!AppendUnicodeTo(oldExpatPosition
, currentExpatPosition
,
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);
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
) !=
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') {
1416 mLastLine
.Append(Substring(buffer
, buffer
+ endOffset
));
1417 if (endOffset
< length
) {
1418 // We found a newline.
1422 lastLine
.advance(length
);
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
1442 aScanner
.EndReading(end
);
1445 aScanner
.SetPosition(currentExpatPosition
, true);
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
);
1465 bool create_ok
= sandbox
->create_sandbox();
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
);
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
,
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
);
1518 mHandleStartElement
=
1519 mSandbox
->register_callback(nsExpatDriver::HandleStartElement
);
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();
1554 mSandbox
->destroy_sandbox();
1555 MOZ_COUNT_DTOR(RLBoxExpatSandboxData
);
1558 nsresult
nsExpatDriver::Initialize(nsIURI
* aURI
, nsIContentSink
* aSink
) {
1559 mSink
= do_QueryInterface(aSink
);
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());
1574 nsCOMPtr
<nsPIDOMWindowOuter
> win
= doc
->GetWindow();
1575 nsCOMPtr
<nsPIDOMWindowInner
> inner
;
1577 inner
= win
->GetCurrentInnerWindow();
1579 bool aHasHadScriptHandlingObject
;
1580 nsIScriptGlobalObject
* global
=
1581 doc
->GetScriptHandlingObject(aHasHadScriptHandlingObject
);
1583 inner
= do_QueryInterface(global
);
1587 mInnerWindowID
= inner
->WindowID();
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
);
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
);
1625 RLBOX_EXPAT_MCALL(MOZ_XML_SetParamEntityParsing
,
1626 XML_PARAM_ENTITY_PARSING_ALWAYS
);
1629 auto baseURI
= GetExpatBaseURI(aURI
);
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
;
1663 nsExpatDriver::BuildModel(nsIContentSink
* aSink
) { return mInternalState
; }
1665 void nsExpatDriver::DidBuildModel() {
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).
1673 mOriginalSink
= nullptr;
1677 NS_IMETHODIMP_(void)
1678 nsExpatDriver::Terminate() {
1679 // XXX - not sure what happens to the unparsed data.
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
)
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') {
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();