1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/dom/DOMParser.h"
10 #include "nsDOMString.h"
11 #include "MainThreadUtils.h"
12 #include "SystemPrincipal.h"
13 #include "nsIScriptGlobalObject.h"
14 #include "nsIStreamListener.h"
15 #include "nsStringStream.h"
17 #include "nsStreamUtils.h"
18 #include "nsContentUtils.h"
19 #include "nsDOMJSUtils.h"
21 #include "nsPIDOMWindow.h"
22 #include "mozilla/BasePrincipal.h"
23 #include "mozilla/LoadInfo.h"
24 #include "mozilla/NullPrincipal.h"
25 #include "mozilla/dom/BindingUtils.h"
26 #include "mozilla/dom/Document.h"
27 #include "mozilla/dom/ScriptSettings.h"
29 using namespace mozilla
;
30 using namespace mozilla::dom
;
32 DOMParser::DOMParser(nsIGlobalObject
* aOwner
, nsIPrincipal
* aDocPrincipal
,
33 nsIURI
* aDocumentURI
, nsIURI
* aBaseURI
)
35 mPrincipal(aDocPrincipal
),
36 mDocumentURI(aDocumentURI
),
38 mForceEnableXULXBL(false),
39 mForceEnableDTD(false) {
40 MOZ_ASSERT(aDocPrincipal
);
41 MOZ_ASSERT(aDocumentURI
);
44 DOMParser::~DOMParser() = default;
46 // QueryInterface implementation for DOMParser
47 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMParser
)
48 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
49 NS_INTERFACE_MAP_ENTRY(nsISupports
)
52 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DOMParser
, mOwner
)
54 NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMParser
)
55 NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMParser
)
57 already_AddRefed
<Document
> DOMParser::ParseFromString(const nsAString
& aStr
,
60 if (aType
== SupportedType::Text_html
) {
61 nsCOMPtr
<Document
> document
= SetUpDocument(DocumentFlavorHTML
, aRv
);
62 if (NS_WARN_IF(aRv
.Failed())) {
66 // Keep the XULXBL state in sync with the XML case.
67 if (mForceEnableXULXBL
) {
68 document
->ForceEnableXULXBL();
71 if (mForceEnableDTD
) {
72 document
->ForceSkipDTDSecurityChecks();
75 nsresult rv
= nsContentUtils::ParseDocumentHTML(aStr
, document
, false);
76 if (NS_WARN_IF(NS_FAILED(rv
))) {
81 return document
.forget();
84 nsAutoCString utf8str
;
85 // Convert from UTF16 to UTF8 using fallible allocations
86 if (!AppendUTF16toUTF8(aStr
, utf8str
, mozilla::fallible
)) {
87 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
91 // The new stream holds a reference to the buffer
92 nsCOMPtr
<nsIInputStream
> stream
;
93 nsresult rv
= NS_NewByteInputStream(getter_AddRefs(stream
), utf8str
,
94 NS_ASSIGNMENT_DEPEND
);
95 if (NS_WARN_IF(NS_FAILED(rv
))) {
100 return ParseFromStream(stream
, u
"UTF-8"_ns
, utf8str
.Length(), aType
, aRv
);
103 already_AddRefed
<Document
> DOMParser::ParseFromSafeString(const nsAString
& aStr
,
106 // Create the new document with the same principal as `mOwner`, even if it is
107 // the system principal. This will ensure that nodes from the returned
108 // document are in the same DocGroup as the owner global's document, allowing
109 // nodes to be adopted.
110 nsCOMPtr
<nsIPrincipal
> docPrincipal
= mPrincipal
;
111 if (mOwner
&& mOwner
->PrincipalOrNull()) {
112 mPrincipal
= mOwner
->PrincipalOrNull();
115 RefPtr
<Document
> ret
= ParseFromString(aStr
, aType
, aRv
);
116 mPrincipal
= docPrincipal
;
120 already_AddRefed
<Document
> DOMParser::ParseFromBuffer(const Uint8Array
& aBuf
,
123 return aBuf
.ProcessFixedData([&](const Span
<uint8_t>& aData
) {
124 return ParseFromBuffer(aData
, aType
, aRv
);
128 already_AddRefed
<Document
> DOMParser::ParseFromBuffer(Span
<const uint8_t> aBuf
,
131 // The new stream holds a reference to the buffer
132 nsCOMPtr
<nsIInputStream
> stream
;
133 nsresult rv
= NS_NewByteInputStream(
134 getter_AddRefs(stream
),
135 Span(reinterpret_cast<const char*>(aBuf
.Elements()), aBuf
.Length()),
136 NS_ASSIGNMENT_DEPEND
);
142 return ParseFromStream(stream
, VoidString(), aBuf
.Length(), aType
, aRv
);
145 already_AddRefed
<Document
> DOMParser::ParseFromStream(nsIInputStream
* aStream
,
146 const nsAString
& aCharset
,
147 int32_t aContentLength
,
150 bool svg
= (aType
== SupportedType::Image_svg_xml
);
152 // For now, we can only create XML documents.
153 // XXXsmaug Should we create an HTMLDocument (in XHTML mode)
154 // for "application/xhtml+xml"?
155 if (aType
!= SupportedType::Text_xml
&&
156 aType
!= SupportedType::Application_xml
&&
157 aType
!= SupportedType::Application_xhtml_xml
&& !svg
) {
158 aRv
.Throw(NS_ERROR_NOT_IMPLEMENTED
);
162 // Put the nsCOMPtr out here so we hold a ref to the stream as needed
163 nsCOMPtr
<nsIInputStream
> stream
= aStream
;
164 if (!NS_InputStreamIsBuffered(stream
)) {
165 nsCOMPtr
<nsIInputStream
> bufferedStream
;
166 nsresult rv
= NS_NewBufferedInputStream(getter_AddRefs(bufferedStream
),
167 stream
.forget(), 4096);
168 if (NS_WARN_IF(NS_FAILED(rv
))) {
173 stream
= bufferedStream
;
176 nsCOMPtr
<Document
> document
=
177 SetUpDocument(svg
? DocumentFlavorSVG
: DocumentFlavorLegacyGuess
, aRv
);
178 if (NS_WARN_IF(aRv
.Failed())) {
182 // Create a fake channel
183 nsCOMPtr
<nsIChannel
> parserChannel
;
184 NS_NewInputStreamChannel(
185 getter_AddRefs(parserChannel
), mDocumentURI
,
187 mPrincipal
, nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL
,
188 nsIContentPolicy::TYPE_OTHER
,
189 nsDependentCSubstring(SupportedTypeValues::GetString(aType
)));
190 if (NS_WARN_IF(!parserChannel
)) {
191 aRv
.Throw(NS_ERROR_UNEXPECTED
);
195 if (!DOMStringIsNull(aCharset
)) {
196 parserChannel
->SetContentCharset(NS_ConvertUTF16toUTF8(aCharset
));
199 // Tell the document to start loading
200 nsCOMPtr
<nsIStreamListener
> listener
;
202 // Keep the XULXBL state in sync with the HTML case
203 if (mForceEnableXULXBL
) {
204 document
->ForceEnableXULXBL();
207 if (mForceEnableDTD
) {
208 document
->ForceSkipDTDSecurityChecks();
211 // Have to pass false for reset here, else the reset will remove
212 // our event listener. Should that listener addition move to later
215 document
->StartDocumentLoad(kLoadAsData
, parserChannel
, nullptr, nullptr,
216 getter_AddRefs(listener
), false);
218 if (NS_FAILED(rv
) || !listener
) {
219 aRv
.Throw(NS_ERROR_FAILURE
);
223 // Now start pumping data to the listener
226 rv
= listener
->OnStartRequest(parserChannel
);
227 if (NS_FAILED(rv
)) parserChannel
->Cancel(rv
);
228 parserChannel
->GetStatus(&status
);
230 if (NS_SUCCEEDED(rv
) && NS_SUCCEEDED(status
)) {
231 rv
= listener
->OnDataAvailable(parserChannel
, stream
, 0, aContentLength
);
232 if (NS_FAILED(rv
)) parserChannel
->Cancel(rv
);
233 parserChannel
->GetStatus(&status
);
236 rv
= listener
->OnStopRequest(parserChannel
, status
);
237 // Failure returned from OnStopRequest does not affect the final status of
238 // the channel, so we do not need to call Cancel(rv) as we do above.
241 aRv
.Throw(NS_ERROR_FAILURE
);
245 return document
.forget();
249 already_AddRefed
<DOMParser
> DOMParser::Constructor(const GlobalObject
& aOwner
,
251 MOZ_ASSERT(NS_IsMainThread());
252 nsCOMPtr
<nsIPrincipal
> docPrincipal
= aOwner
.GetSubjectPrincipal();
253 nsCOMPtr
<nsIURI
> documentURI
;
254 nsIURI
* baseURI
= nullptr;
255 if (docPrincipal
->IsSystemPrincipal()) {
256 docPrincipal
= NullPrincipal::Create(OriginAttributes());
257 documentURI
= docPrincipal
->GetURI();
259 // Grab document and base URIs off the window our constructor was
260 // called on. Error out if anything untoward happens.
261 nsCOMPtr
<nsPIDOMWindowInner
> window
=
262 do_QueryInterface(aOwner
.GetAsSupports());
264 rv
.Throw(NS_ERROR_UNEXPECTED
);
268 baseURI
= window
->GetDocBaseURI();
269 documentURI
= window
->GetDocumentURI();
273 rv
.Throw(NS_ERROR_UNEXPECTED
);
277 nsCOMPtr
<nsIGlobalObject
> global
= do_QueryInterface(aOwner
.GetAsSupports());
279 RefPtr
<DOMParser
> domParser
=
280 new DOMParser(global
, docPrincipal
, documentURI
, baseURI
);
281 return domParser
.forget();
285 already_AddRefed
<DOMParser
> DOMParser::CreateWithoutGlobal(ErrorResult
& aRv
) {
286 nsCOMPtr
<nsIPrincipal
> docPrincipal
=
287 NullPrincipal::Create(OriginAttributes());
289 nsCOMPtr
<nsIURI
> documentURI
= docPrincipal
->GetURI();
291 aRv
.Throw(NS_ERROR_UNEXPECTED
);
295 RefPtr
<DOMParser
> domParser
=
296 new DOMParser(nullptr, docPrincipal
, documentURI
, nullptr);
297 return domParser
.forget();
300 already_AddRefed
<Document
> DOMParser::SetUpDocument(DocumentFlavor aFlavor
,
302 // We should really just use mOwner here, but Document gets confused
303 // if we pass it a scriptHandlingObject that doesn't QI to
304 // nsIScriptGlobalObject, and test_isequalnode.js (an xpcshell test without
305 // a window global) breaks. The correct solution is just to wean Document off
306 // of nsIScriptGlobalObject, but that's a yak to shave another day.
307 nsCOMPtr
<nsIScriptGlobalObject
> scriptHandlingObject
=
308 do_QueryInterface(mOwner
);
310 // Try to inherit a style backend.
311 NS_ASSERTION(mPrincipal
, "Must have principal by now");
312 NS_ASSERTION(mDocumentURI
, "Must have document URI by now");
314 nsCOMPtr
<Document
> doc
;
315 nsresult rv
= NS_NewDOMDocument(getter_AddRefs(doc
), u
""_ns
, u
""_ns
, nullptr,
316 mDocumentURI
, mBaseURI
, mPrincipal
, true,
317 scriptHandlingObject
, aFlavor
);
318 if (NS_WARN_IF(NS_FAILED(rv
))) {