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 "mozilla/dom/DOMParser.h"
8 #include "nsIDOMDocument.h"
10 #include "nsStringStream.h"
11 #include "nsIScriptSecurityManager.h"
13 #include "nsStreamUtils.h"
14 #include "nsContentUtils.h"
15 #include "nsDOMJSUtils.h"
17 #include "nsPIDOMWindow.h"
18 #include "mozilla/LoadInfo.h"
19 #include "mozilla/dom/BindingUtils.h"
20 #include "mozilla/dom/ScriptSettings.h"
22 using namespace mozilla
;
23 using namespace mozilla::dom
;
25 DOMParser::DOMParser()
26 : mAttemptedInit(false)
30 DOMParser::~DOMParser()
34 // QueryInterface implementation for DOMParser
35 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMParser
)
36 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
37 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIDOMParser
)
38 NS_INTERFACE_MAP_ENTRY(nsIDOMParser
)
39 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference
)
42 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DOMParser
, mOwner
)
44 NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMParser
)
45 NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMParser
)
48 StringFromSupportedType(SupportedType aType
)
50 return SupportedTypeValues::strings
[static_cast<int>(aType
)].value
;
53 already_AddRefed
<nsIDocument
>
54 DOMParser::ParseFromString(const nsAString
& aStr
, SupportedType aType
,
57 nsCOMPtr
<nsIDOMDocument
> domDocument
;
58 rv
= ParseFromString(aStr
,
59 StringFromSupportedType(aType
),
60 getter_AddRefs(domDocument
));
61 nsCOMPtr
<nsIDocument
> document(do_QueryInterface(domDocument
));
62 return document
.forget();
66 DOMParser::ParseFromString(const char16_t
*str
,
67 const char *contentType
,
68 nsIDOMDocument
**aResult
)
71 // Converting a string to an enum value manually is a bit of a pain,
72 // so let's just use a helper that takes a content-type string.
73 return ParseFromString(nsDependentString(str
), contentType
, aResult
);
77 DOMParser::ParseFromString(const nsAString
& str
,
78 const char *contentType
,
79 nsIDOMDocument
**aResult
)
81 NS_ENSURE_ARG_POINTER(aResult
);
85 if (!nsCRT::strcmp(contentType
, "text/html")) {
86 nsCOMPtr
<nsIDOMDocument
> domDocument
;
87 rv
= SetUpDocument(DocumentFlavorHTML
, getter_AddRefs(domDocument
));
88 NS_ENSURE_SUCCESS(rv
, rv
);
89 nsCOMPtr
<nsIDocument
> document
= do_QueryInterface(domDocument
);
91 // Keep the XULXBL state, base URL and principal setting in sync with the
94 if (nsContentUtils::IsSystemPrincipal(mOriginalPrincipal
)) {
95 document
->ForceEnableXULXBL();
98 // Make sure to give this document the right base URI
99 document
->SetBaseURI(mBaseURI
);
100 // And the right principal
101 document
->SetPrincipal(mPrincipal
);
103 rv
= nsContentUtils::ParseDocumentHTML(str
, document
, false);
104 NS_ENSURE_SUCCESS(rv
, rv
);
106 domDocument
.forget(aResult
);
110 nsAutoCString utf8str
;
111 // Convert from UTF16 to UTF8 using fallible allocations
112 if (!AppendUTF16toUTF8(str
, utf8str
, mozilla::fallible_t())) {
113 return NS_ERROR_OUT_OF_MEMORY
;
116 // The new stream holds a reference to the buffer
117 nsCOMPtr
<nsIInputStream
> stream
;
118 rv
= NS_NewByteInputStream(getter_AddRefs(stream
),
119 utf8str
.get(), utf8str
.Length(),
120 NS_ASSIGNMENT_DEPEND
);
124 return ParseFromStream(stream
, "UTF-8", utf8str
.Length(), contentType
, aResult
);
127 already_AddRefed
<nsIDocument
>
128 DOMParser::ParseFromBuffer(const Sequence
<uint8_t>& aBuf
, uint32_t aBufLen
,
129 SupportedType aType
, ErrorResult
& rv
)
131 if (aBufLen
> aBuf
.Length()) {
132 rv
.Throw(NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY
);
135 nsCOMPtr
<nsIDOMDocument
> domDocument
;
136 rv
= DOMParser::ParseFromBuffer(aBuf
.Elements(), aBufLen
,
137 StringFromSupportedType(aType
),
138 getter_AddRefs(domDocument
));
139 nsCOMPtr
<nsIDocument
> document(do_QueryInterface(domDocument
));
140 return document
.forget();
143 already_AddRefed
<nsIDocument
>
144 DOMParser::ParseFromBuffer(const Uint8Array
& aBuf
, uint32_t aBufLen
,
145 SupportedType aType
, ErrorResult
& rv
)
147 aBuf
.ComputeLengthAndData();
149 if (aBufLen
> aBuf
.Length()) {
150 rv
.Throw(NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY
);
153 nsCOMPtr
<nsIDOMDocument
> domDocument
;
154 rv
= DOMParser::ParseFromBuffer(aBuf
.Data(), aBufLen
,
155 StringFromSupportedType(aType
),
156 getter_AddRefs(domDocument
));
157 nsCOMPtr
<nsIDocument
> document(do_QueryInterface(domDocument
));
158 return document
.forget();
162 DOMParser::ParseFromBuffer(const uint8_t *buf
,
164 const char *contentType
,
165 nsIDOMDocument
**aResult
)
167 NS_ENSURE_ARG_POINTER(buf
);
168 NS_ENSURE_ARG_POINTER(aResult
);
170 // The new stream holds a reference to the buffer
171 nsCOMPtr
<nsIInputStream
> stream
;
172 nsresult rv
= NS_NewByteInputStream(getter_AddRefs(stream
),
173 reinterpret_cast<const char *>(buf
),
174 bufLen
, NS_ASSIGNMENT_DEPEND
);
178 return ParseFromStream(stream
, nullptr, bufLen
, contentType
, aResult
);
182 already_AddRefed
<nsIDocument
>
183 DOMParser::ParseFromStream(nsIInputStream
* aStream
,
184 const nsAString
& aCharset
,
185 int32_t aContentLength
,
189 nsCOMPtr
<nsIDOMDocument
> domDocument
;
190 rv
= DOMParser::ParseFromStream(aStream
,
191 NS_ConvertUTF16toUTF8(aCharset
).get(),
193 StringFromSupportedType(aType
),
194 getter_AddRefs(domDocument
));
195 nsCOMPtr
<nsIDocument
> document(do_QueryInterface(domDocument
));
196 return document
.forget();
200 DOMParser::ParseFromStream(nsIInputStream
*stream
,
202 int32_t contentLength
,
203 const char *contentType
,
204 nsIDOMDocument
**aResult
)
206 NS_ENSURE_ARG(stream
);
207 NS_ENSURE_ARG(contentType
);
208 NS_ENSURE_ARG_POINTER(aResult
);
211 bool svg
= nsCRT::strcmp(contentType
, "image/svg+xml") == 0;
213 // For now, we can only create XML documents.
214 //XXXsmaug Should we create an HTMLDocument (in XHTML mode)
215 // for "application/xhtml+xml"?
216 if ((nsCRT::strcmp(contentType
, "text/xml") != 0) &&
217 (nsCRT::strcmp(contentType
, "application/xml") != 0) &&
218 (nsCRT::strcmp(contentType
, "application/xhtml+xml") != 0) &&
220 return NS_ERROR_NOT_IMPLEMENTED
;
224 // Put the nsCOMPtr out here so we hold a ref to the stream as needed
225 nsCOMPtr
<nsIInputStream
> bufferedStream
;
226 if (!NS_InputStreamIsBuffered(stream
)) {
227 rv
= NS_NewBufferedInputStream(getter_AddRefs(bufferedStream
), stream
,
229 NS_ENSURE_SUCCESS(rv
, rv
);
231 stream
= bufferedStream
;
234 nsCOMPtr
<nsIDOMDocument
> domDocument
;
235 rv
= SetUpDocument(svg
? DocumentFlavorSVG
: DocumentFlavorLegacyGuess
,
236 getter_AddRefs(domDocument
));
237 NS_ENSURE_SUCCESS(rv
, rv
);
239 // Create a fake channel
240 nsCOMPtr
<nsIChannel
> parserChannel
;
241 NS_NewInputStreamChannel(getter_AddRefs(parserChannel
),
245 nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL
,
246 nsIContentPolicy::TYPE_OTHER
,
247 nsDependentCString(contentType
));
248 NS_ENSURE_STATE(parserChannel
);
251 parserChannel
->SetContentCharset(nsDependentCString(charset
));
254 // Tell the document to start loading
255 nsCOMPtr
<nsIStreamListener
> listener
;
257 // Have to pass false for reset here, else the reset will remove
258 // our event listener. Should that listener addition move to later
259 // than this call? Then we wouldn't need to mess around with
260 // SetPrincipal, etc, probably!
261 nsCOMPtr
<nsIDocument
> document(do_QueryInterface(domDocument
));
262 if (!document
) return NS_ERROR_FAILURE
;
264 // Keep the XULXBL state, base URL and principal setting in sync with the
267 if (nsContentUtils::IsSystemPrincipal(mOriginalPrincipal
)) {
268 document
->ForceEnableXULXBL();
271 rv
= document
->StartDocumentLoad(kLoadAsData
, parserChannel
,
273 getter_AddRefs(listener
),
276 // Make sure to give this document the right base URI
277 document
->SetBaseURI(mBaseURI
);
279 // And the right principal
280 document
->SetPrincipal(mPrincipal
);
282 if (NS_FAILED(rv
) || !listener
) {
283 return NS_ERROR_FAILURE
;
286 // Now start pumping data to the listener
289 rv
= listener
->OnStartRequest(parserChannel
, nullptr);
291 parserChannel
->Cancel(rv
);
292 parserChannel
->GetStatus(&status
);
294 if (NS_SUCCEEDED(rv
) && NS_SUCCEEDED(status
)) {
295 rv
= listener
->OnDataAvailable(parserChannel
, nullptr, stream
, 0,
298 parserChannel
->Cancel(rv
);
299 parserChannel
->GetStatus(&status
);
302 rv
= listener
->OnStopRequest(parserChannel
, nullptr, status
);
303 // Failure returned from OnStopRequest does not affect the final status of
304 // the channel, so we do not need to call Cancel(rv) as we do above.
307 return NS_ERROR_FAILURE
;
310 domDocument
.swap(*aResult
);
316 DOMParser::Init(nsIPrincipal
* principal
, nsIURI
* documentURI
,
317 nsIURI
* baseURI
, nsIGlobalObject
* aScriptObject
)
319 NS_ENSURE_STATE(!mAttemptedInit
);
320 mAttemptedInit
= true;
322 NS_ENSURE_ARG(principal
|| documentURI
);
324 mDocumentURI
= documentURI
;
327 principal
->GetURI(getter_AddRefs(mDocumentURI
));
328 // If we have the system principal, then we'll just use the null principals
330 if (!mDocumentURI
&& !nsContentUtils::IsSystemPrincipal(principal
)) {
331 return NS_ERROR_INVALID_ARG
;
335 mScriptHandlingObject
= do_GetWeakReference(aScriptObject
);
336 mPrincipal
= principal
;
339 nsIScriptSecurityManager
* secMan
= nsContentUtils::GetSecurityManager();
340 NS_ENSURE_TRUE(secMan
, NS_ERROR_NOT_AVAILABLE
);
342 secMan
->GetSimpleCodebasePrincipal(mDocumentURI
,
343 getter_AddRefs(mPrincipal
));
344 NS_ENSURE_SUCCESS(rv
, rv
);
345 mOriginalPrincipal
= mPrincipal
;
347 mOriginalPrincipal
= mPrincipal
;
348 if (nsContentUtils::IsSystemPrincipal(mPrincipal
)) {
349 // Don't give DOMParsers the system principal. Use a null
350 // principal instead.
351 mPrincipal
= do_CreateInstance("@mozilla.org/nullprincipal;1", &rv
);
352 NS_ENSURE_SUCCESS(rv
, rv
);
355 rv
= mPrincipal
->GetURI(getter_AddRefs(mDocumentURI
));
356 NS_ENSURE_SUCCESS(rv
, rv
);
362 // Note: if mBaseURI is null, fine. Leave it like that; that will use the
363 // documentURI as the base. Otherwise for null principals we'll get
364 // nsDocument::SetBaseURI giving errors.
366 NS_POSTCONDITION(mPrincipal
, "Must have principal");
367 NS_POSTCONDITION(mOriginalPrincipal
, "Must have original principal");
368 NS_POSTCONDITION(mDocumentURI
, "Must have document URI");
372 /*static */already_AddRefed
<DOMParser
>
373 DOMParser::Constructor(const GlobalObject
& aOwner
,
374 nsIPrincipal
* aPrincipal
, nsIURI
* aDocumentURI
,
375 nsIURI
* aBaseURI
, ErrorResult
& rv
)
377 if (!nsContentUtils::IsCallerChrome()) {
378 rv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
381 nsRefPtr
<DOMParser
> domParser
= new DOMParser(aOwner
.GetAsSupports());
382 rv
= domParser
->InitInternal(aOwner
.GetAsSupports(), aPrincipal
, aDocumentURI
,
387 return domParser
.forget();
390 /*static */already_AddRefed
<DOMParser
>
391 DOMParser::Constructor(const GlobalObject
& aOwner
,
394 nsRefPtr
<DOMParser
> domParser
= new DOMParser(aOwner
.GetAsSupports());
395 rv
= domParser
->InitInternal(aOwner
.GetAsSupports(),
396 nsContentUtils::SubjectPrincipal(),
401 return domParser
.forget();
405 DOMParser::InitInternal(nsISupports
* aOwner
, nsIPrincipal
* prin
,
406 nsIURI
* documentURI
, nsIURI
* baseURI
)
408 AttemptedInitMarker
marker(&mAttemptedInit
);
410 // No explicit documentURI; grab document and base URIs off the window our
411 // constructor was called on. Error out if anything untoward happens.
413 // Note that this is a behavior change as far as I can tell -- we're now
414 // using the base URI and document URI of the window off of which the
415 // DOMParser is created, not the window in which parse*() is called.
418 // Also note that |cx| matches what GetDocumentFromContext() would return,
419 // while GetDocumentFromCaller() gives us the window that the DOMParser()
422 nsCOMPtr
<nsPIDOMWindow
> window
= do_QueryInterface(aOwner
);
424 return NS_ERROR_UNEXPECTED
;
427 baseURI
= window
->GetDocBaseURI();
428 documentURI
= window
->GetDocumentURI();
430 return NS_ERROR_UNEXPECTED
;
434 nsCOMPtr
<nsIGlobalObject
> scriptglobal
= do_QueryInterface(aOwner
);
435 return Init(prin
, documentURI
, baseURI
, scriptglobal
);
439 DOMParser::Init(nsIPrincipal
* aPrincipal
, nsIURI
* aDocumentURI
,
440 nsIURI
* aBaseURI
, mozilla::ErrorResult
& rv
)
442 AttemptedInitMarker
marker(&mAttemptedInit
);
444 nsCOMPtr
<nsIPrincipal
> principal
= aPrincipal
;
445 if (!principal
&& !aDocumentURI
) {
446 principal
= nsContentUtils::SubjectPrincipal();
449 rv
= Init(principal
, aDocumentURI
, aBaseURI
, GetEntryGlobal());
453 DOMParser::SetUpDocument(DocumentFlavor aFlavor
, nsIDOMDocument
** aResult
)
455 // We should really QI to nsIGlobalObject here, but nsDocument gets confused
456 // if we pass it a scriptHandlingObject that doesn't QI to
457 // nsIScriptGlobalObject, and test_isequalnode.js (an xpcshell test without
458 // a window global) breaks. The correct solution is just to wean nsDocument
459 // off of nsIScriptGlobalObject, but that's a yak to shave another day.
460 nsCOMPtr
<nsIScriptGlobalObject
> scriptHandlingObject
=
461 do_QueryReferent(mScriptHandlingObject
);
464 NS_ENSURE_TRUE(!mAttemptedInit
, NS_ERROR_NOT_INITIALIZED
);
465 AttemptedInitMarker
marker(&mAttemptedInit
);
467 nsCOMPtr
<nsIPrincipal
> prin
=
468 do_CreateInstance("@mozilla.org/nullprincipal;1", &rv
);
469 NS_ENSURE_SUCCESS(rv
, rv
);
471 rv
= Init(prin
, nullptr, nullptr, scriptHandlingObject
);
472 NS_ENSURE_SUCCESS(rv
, rv
);
475 NS_ASSERTION(mPrincipal
, "Must have principal by now");
476 NS_ASSERTION(mDocumentURI
, "Must have document URI by now");
478 // Here we have to cheat a little bit... Setting the base URI won't
479 // work if the document has a null principal, so use
480 // mOriginalPrincipal when creating the document, then reset the
482 return NS_NewDOMDocument(aResult
, EmptyString(), EmptyString(), nullptr,
483 mDocumentURI
, mBaseURI
,
486 scriptHandlingObject
,