Bumping gaia.json for 8 gaia revision(s) a=gaia-bump
[gecko.git] / dom / base / DOMParser.cpp
blobb9bec3e69b6eb041f917b25fc5df85340b38903e
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"
9 #include "nsNetUtil.h"
10 #include "nsStringStream.h"
11 #include "nsIScriptSecurityManager.h"
12 #include "nsCRT.h"
13 #include "nsStreamUtils.h"
14 #include "nsContentUtils.h"
15 #include "nsDOMJSUtils.h"
16 #include "nsError.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)
40 NS_INTERFACE_MAP_END
42 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DOMParser, mOwner)
44 NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMParser)
45 NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMParser)
47 static const char*
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,
55 ErrorResult& rv)
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();
65 NS_IMETHODIMP
66 DOMParser::ParseFromString(const char16_t *str,
67 const char *contentType,
68 nsIDOMDocument **aResult)
70 NS_ENSURE_ARG(str);
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);
76 nsresult
77 DOMParser::ParseFromString(const nsAString& str,
78 const char *contentType,
79 nsIDOMDocument **aResult)
81 NS_ENSURE_ARG_POINTER(aResult);
83 nsresult rv;
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
92 // XML case
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);
107 return rv;
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);
121 if (NS_FAILED(rv))
122 return rv;
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);
133 return nullptr;
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);
151 return nullptr;
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();
161 NS_IMETHODIMP
162 DOMParser::ParseFromBuffer(const uint8_t *buf,
163 uint32_t bufLen,
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);
175 if (NS_FAILED(rv))
176 return rv;
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,
186 SupportedType aType,
187 ErrorResult& rv)
189 nsCOMPtr<nsIDOMDocument> domDocument;
190 rv = DOMParser::ParseFromStream(aStream,
191 NS_ConvertUTF16toUTF8(aCharset).get(),
192 aContentLength,
193 StringFromSupportedType(aType),
194 getter_AddRefs(domDocument));
195 nsCOMPtr<nsIDocument> document(do_QueryInterface(domDocument));
196 return document.forget();
199 NS_IMETHODIMP
200 DOMParser::ParseFromStream(nsIInputStream *stream,
201 const char *charset,
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);
209 *aResult = nullptr;
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) &&
219 !svg)
220 return NS_ERROR_NOT_IMPLEMENTED;
222 nsresult rv;
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,
228 4096);
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),
242 mDocumentURI,
243 nullptr, // aStream
244 mOriginalPrincipal,
245 nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL,
246 nsIContentPolicy::TYPE_OTHER,
247 nsDependentCString(contentType));
248 NS_ENSURE_STATE(parserChannel);
250 if (charset) {
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
265 // HTML case
267 if (nsContentUtils::IsSystemPrincipal(mOriginalPrincipal)) {
268 document->ForceEnableXULXBL();
271 rv = document->StartDocumentLoad(kLoadAsData, parserChannel,
272 nullptr, nullptr,
273 getter_AddRefs(listener),
274 false);
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
287 nsresult status;
289 rv = listener->OnStartRequest(parserChannel, nullptr);
290 if (NS_FAILED(rv))
291 parserChannel->Cancel(rv);
292 parserChannel->GetStatus(&status);
294 if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(status)) {
295 rv = listener->OnDataAvailable(parserChannel, nullptr, stream, 0,
296 contentLength);
297 if (NS_FAILED(rv))
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.
306 if (NS_FAILED(rv)) {
307 return NS_ERROR_FAILURE;
310 domDocument.swap(*aResult);
312 return NS_OK;
315 NS_IMETHODIMP
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;
326 if (!mDocumentURI) {
327 principal->GetURI(getter_AddRefs(mDocumentURI));
328 // If we have the system principal, then we'll just use the null principals
329 // uri.
330 if (!mDocumentURI && !nsContentUtils::IsSystemPrincipal(principal)) {
331 return NS_ERROR_INVALID_ARG;
335 mScriptHandlingObject = do_GetWeakReference(aScriptObject);
336 mPrincipal = principal;
337 nsresult rv;
338 if (!mPrincipal) {
339 nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
340 NS_ENSURE_TRUE(secMan, NS_ERROR_NOT_AVAILABLE);
341 rv =
342 secMan->GetSimpleCodebasePrincipal(mDocumentURI,
343 getter_AddRefs(mPrincipal));
344 NS_ENSURE_SUCCESS(rv, rv);
345 mOriginalPrincipal = mPrincipal;
346 } else {
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);
354 if (!mDocumentURI) {
355 rv = mPrincipal->GetURI(getter_AddRefs(mDocumentURI));
356 NS_ENSURE_SUCCESS(rv, rv);
361 mBaseURI = baseURI;
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");
369 return NS_OK;
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);
379 return nullptr;
381 nsRefPtr<DOMParser> domParser = new DOMParser(aOwner.GetAsSupports());
382 rv = domParser->InitInternal(aOwner.GetAsSupports(), aPrincipal, aDocumentURI,
383 aBaseURI);
384 if (rv.Failed()) {
385 return nullptr;
387 return domParser.forget();
390 /*static */already_AddRefed<DOMParser>
391 DOMParser::Constructor(const GlobalObject& aOwner,
392 ErrorResult& rv)
394 nsRefPtr<DOMParser> domParser = new DOMParser(aOwner.GetAsSupports());
395 rv = domParser->InitInternal(aOwner.GetAsSupports(),
396 nsContentUtils::SubjectPrincipal(),
397 nullptr, nullptr);
398 if (rv.Failed()) {
399 return nullptr;
401 return domParser.forget();
404 nsresult
405 DOMParser::InitInternal(nsISupports* aOwner, nsIPrincipal* prin,
406 nsIURI* documentURI, nsIURI* baseURI)
408 AttemptedInitMarker marker(&mAttemptedInit);
409 if (!documentURI) {
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.
416 // Does that matter?
418 // Also note that |cx| matches what GetDocumentFromContext() would return,
419 // while GetDocumentFromCaller() gives us the window that the DOMParser()
420 // call was made on.
422 nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aOwner);
423 if (!window) {
424 return NS_ERROR_UNEXPECTED;
427 baseURI = window->GetDocBaseURI();
428 documentURI = window->GetDocumentURI();
429 if (!documentURI) {
430 return NS_ERROR_UNEXPECTED;
434 nsCOMPtr<nsIGlobalObject> scriptglobal = do_QueryInterface(aOwner);
435 return Init(prin, documentURI, baseURI, scriptglobal);
438 void
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());
452 nsresult
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);
462 nsresult rv;
463 if (!mPrincipal) {
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
481 // principal.
482 return NS_NewDOMDocument(aResult, EmptyString(), EmptyString(), nullptr,
483 mDocumentURI, mBaseURI,
484 mOriginalPrincipal,
485 true,
486 scriptHandlingObject,
487 aFlavor);