Thu, 15 Jan 2015 21:03:48 +0100
Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)
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/dom/BindingUtils.h"
20 using namespace mozilla;
21 using namespace mozilla::dom;
23 DOMParser::DOMParser()
24 : mAttemptedInit(false)
25 {
26 SetIsDOMBinding();
27 }
29 DOMParser::~DOMParser()
30 {
31 }
33 // QueryInterface implementation for DOMParser
34 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMParser)
35 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
36 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMParser)
37 NS_INTERFACE_MAP_ENTRY(nsIDOMParser)
38 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
39 NS_INTERFACE_MAP_END
41 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(DOMParser, mOwner)
43 NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMParser)
44 NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMParser)
46 static const char*
47 StringFromSupportedType(SupportedType aType)
48 {
49 return SupportedTypeValues::strings[static_cast<int>(aType)].value;
50 }
52 already_AddRefed<nsIDocument>
53 DOMParser::ParseFromString(const nsAString& aStr, SupportedType aType,
54 ErrorResult& rv)
55 {
56 nsCOMPtr<nsIDOMDocument> domDocument;
57 rv = ParseFromString(aStr,
58 StringFromSupportedType(aType),
59 getter_AddRefs(domDocument));
60 nsCOMPtr<nsIDocument> document(do_QueryInterface(domDocument));
61 return document.forget();
62 }
64 NS_IMETHODIMP
65 DOMParser::ParseFromString(const char16_t *str,
66 const char *contentType,
67 nsIDOMDocument **aResult)
68 {
69 NS_ENSURE_ARG(str);
70 // Converting a string to an enum value manually is a bit of a pain,
71 // so let's just use a helper that takes a content-type string.
72 return ParseFromString(nsDependentString(str), contentType, aResult);
73 }
75 nsresult
76 DOMParser::ParseFromString(const nsAString& str,
77 const char *contentType,
78 nsIDOMDocument **aResult)
79 {
80 NS_ENSURE_ARG_POINTER(aResult);
82 nsresult rv;
84 if (!nsCRT::strcmp(contentType, "text/html")) {
85 nsCOMPtr<nsIDOMDocument> domDocument;
86 rv = SetUpDocument(DocumentFlavorHTML, getter_AddRefs(domDocument));
87 NS_ENSURE_SUCCESS(rv, rv);
88 nsCOMPtr<nsIDocument> document = do_QueryInterface(domDocument);
90 // Keep the XULXBL state, base URL and principal setting in sync with the
91 // XML case
93 if (nsContentUtils::IsSystemPrincipal(mOriginalPrincipal)) {
94 document->ForceEnableXULXBL();
95 }
97 // Make sure to give this document the right base URI
98 document->SetBaseURI(mBaseURI);
99 // And the right principal
100 document->SetPrincipal(mPrincipal);
102 rv = nsContentUtils::ParseDocumentHTML(str, document, false);
103 NS_ENSURE_SUCCESS(rv, rv);
105 domDocument.forget(aResult);
106 return rv;
107 }
109 nsAutoCString utf8str;
110 // Convert from UTF16 to UTF8 using fallible allocations
111 if (!AppendUTF16toUTF8(str, utf8str, mozilla::fallible_t())) {
112 return NS_ERROR_OUT_OF_MEMORY;
113 }
115 // The new stream holds a reference to the buffer
116 nsCOMPtr<nsIInputStream> stream;
117 rv = NS_NewByteInputStream(getter_AddRefs(stream),
118 utf8str.get(), utf8str.Length(),
119 NS_ASSIGNMENT_DEPEND);
120 if (NS_FAILED(rv))
121 return rv;
123 return ParseFromStream(stream, "UTF-8", utf8str.Length(), contentType, aResult);
124 }
126 already_AddRefed<nsIDocument>
127 DOMParser::ParseFromBuffer(const Sequence<uint8_t>& aBuf, uint32_t aBufLen,
128 SupportedType aType, ErrorResult& rv)
129 {
130 if (aBufLen > aBuf.Length()) {
131 rv.Throw(NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY);
132 return nullptr;
133 }
134 nsCOMPtr<nsIDOMDocument> domDocument;
135 rv = DOMParser::ParseFromBuffer(aBuf.Elements(), aBufLen,
136 StringFromSupportedType(aType),
137 getter_AddRefs(domDocument));
138 nsCOMPtr<nsIDocument> document(do_QueryInterface(domDocument));
139 return document.forget();
140 }
142 already_AddRefed<nsIDocument>
143 DOMParser::ParseFromBuffer(const Uint8Array& aBuf, uint32_t aBufLen,
144 SupportedType aType, ErrorResult& rv)
145 {
146 aBuf.ComputeLengthAndData();
148 if (aBufLen > aBuf.Length()) {
149 rv.Throw(NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY);
150 return nullptr;
151 }
152 nsCOMPtr<nsIDOMDocument> domDocument;
153 rv = DOMParser::ParseFromBuffer(aBuf.Data(), aBufLen,
154 StringFromSupportedType(aType),
155 getter_AddRefs(domDocument));
156 nsCOMPtr<nsIDocument> document(do_QueryInterface(domDocument));
157 return document.forget();
158 }
160 NS_IMETHODIMP
161 DOMParser::ParseFromBuffer(const uint8_t *buf,
162 uint32_t bufLen,
163 const char *contentType,
164 nsIDOMDocument **aResult)
165 {
166 NS_ENSURE_ARG_POINTER(buf);
167 NS_ENSURE_ARG_POINTER(aResult);
169 // The new stream holds a reference to the buffer
170 nsCOMPtr<nsIInputStream> stream;
171 nsresult rv = NS_NewByteInputStream(getter_AddRefs(stream),
172 reinterpret_cast<const char *>(buf),
173 bufLen, NS_ASSIGNMENT_DEPEND);
174 if (NS_FAILED(rv))
175 return rv;
177 return ParseFromStream(stream, nullptr, bufLen, contentType, aResult);
178 }
181 already_AddRefed<nsIDocument>
182 DOMParser::ParseFromStream(nsIInputStream* aStream,
183 const nsAString& aCharset,
184 int32_t aContentLength,
185 SupportedType aType,
186 ErrorResult& rv)
187 {
188 nsCOMPtr<nsIDOMDocument> domDocument;
189 rv = DOMParser::ParseFromStream(aStream,
190 NS_ConvertUTF16toUTF8(aCharset).get(),
191 aContentLength,
192 StringFromSupportedType(aType),
193 getter_AddRefs(domDocument));
194 nsCOMPtr<nsIDocument> document(do_QueryInterface(domDocument));
195 return document.forget();
196 }
198 NS_IMETHODIMP
199 DOMParser::ParseFromStream(nsIInputStream *stream,
200 const char *charset,
201 int32_t contentLength,
202 const char *contentType,
203 nsIDOMDocument **aResult)
204 {
205 NS_ENSURE_ARG(stream);
206 NS_ENSURE_ARG(contentType);
207 NS_ENSURE_ARG_POINTER(aResult);
208 *aResult = nullptr;
210 bool svg = nsCRT::strcmp(contentType, "image/svg+xml") == 0;
212 // For now, we can only create XML documents.
213 //XXXsmaug Should we create an HTMLDocument (in XHTML mode)
214 // for "application/xhtml+xml"?
215 if ((nsCRT::strcmp(contentType, "text/xml") != 0) &&
216 (nsCRT::strcmp(contentType, "application/xml") != 0) &&
217 (nsCRT::strcmp(contentType, "application/xhtml+xml") != 0) &&
218 !svg)
219 return NS_ERROR_NOT_IMPLEMENTED;
221 nsresult rv;
223 // Put the nsCOMPtr out here so we hold a ref to the stream as needed
224 nsCOMPtr<nsIInputStream> bufferedStream;
225 if (!NS_InputStreamIsBuffered(stream)) {
226 rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream), stream,
227 4096);
228 NS_ENSURE_SUCCESS(rv, rv);
230 stream = bufferedStream;
231 }
233 nsCOMPtr<nsIDOMDocument> domDocument;
234 rv = SetUpDocument(svg ? DocumentFlavorSVG : DocumentFlavorLegacyGuess,
235 getter_AddRefs(domDocument));
236 NS_ENSURE_SUCCESS(rv, rv);
238 // Create a fake channel
239 nsCOMPtr<nsIChannel> parserChannel;
240 NS_NewInputStreamChannel(getter_AddRefs(parserChannel), mDocumentURI, nullptr,
241 nsDependentCString(contentType), nullptr);
242 NS_ENSURE_STATE(parserChannel);
244 // More principal-faking here
245 parserChannel->SetOwner(mOriginalPrincipal);
247 if (charset) {
248 parserChannel->SetContentCharset(nsDependentCString(charset));
249 }
251 // Tell the document to start loading
252 nsCOMPtr<nsIStreamListener> listener;
254 // Have to pass false for reset here, else the reset will remove
255 // our event listener. Should that listener addition move to later
256 // than this call? Then we wouldn't need to mess around with
257 // SetPrincipal, etc, probably!
258 nsCOMPtr<nsIDocument> document(do_QueryInterface(domDocument));
259 if (!document) return NS_ERROR_FAILURE;
261 // Keep the XULXBL state, base URL and principal setting in sync with the
262 // HTML case
264 if (nsContentUtils::IsSystemPrincipal(mOriginalPrincipal)) {
265 document->ForceEnableXULXBL();
266 }
268 rv = document->StartDocumentLoad(kLoadAsData, parserChannel,
269 nullptr, nullptr,
270 getter_AddRefs(listener),
271 false);
273 // Make sure to give this document the right base URI
274 document->SetBaseURI(mBaseURI);
276 // And the right principal
277 document->SetPrincipal(mPrincipal);
279 if (NS_FAILED(rv) || !listener) {
280 return NS_ERROR_FAILURE;
281 }
283 // Now start pumping data to the listener
284 nsresult status;
286 rv = listener->OnStartRequest(parserChannel, nullptr);
287 if (NS_FAILED(rv))
288 parserChannel->Cancel(rv);
289 parserChannel->GetStatus(&status);
291 if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(status)) {
292 rv = listener->OnDataAvailable(parserChannel, nullptr, stream, 0,
293 contentLength);
294 if (NS_FAILED(rv))
295 parserChannel->Cancel(rv);
296 parserChannel->GetStatus(&status);
297 }
299 rv = listener->OnStopRequest(parserChannel, nullptr, status);
300 // Failure returned from OnStopRequest does not affect the final status of
301 // the channel, so we do not need to call Cancel(rv) as we do above.
303 if (NS_FAILED(rv)) {
304 return NS_ERROR_FAILURE;
305 }
307 domDocument.swap(*aResult);
309 return NS_OK;
310 }
312 NS_IMETHODIMP
313 DOMParser::Init(nsIPrincipal* principal, nsIURI* documentURI,
314 nsIURI* baseURI, nsIScriptGlobalObject* aScriptObject)
315 {
316 NS_ENSURE_STATE(!mAttemptedInit);
317 mAttemptedInit = true;
319 NS_ENSURE_ARG(principal || documentURI);
321 mDocumentURI = documentURI;
323 if (!mDocumentURI) {
324 principal->GetURI(getter_AddRefs(mDocumentURI));
325 // If we have the system principal, then we'll just use the null principals
326 // uri.
327 if (!mDocumentURI && !nsContentUtils::IsSystemPrincipal(principal)) {
328 return NS_ERROR_INVALID_ARG;
329 }
330 }
332 mScriptHandlingObject = do_GetWeakReference(aScriptObject);
333 mPrincipal = principal;
334 nsresult rv;
335 if (!mPrincipal) {
336 nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
337 NS_ENSURE_TRUE(secMan, NS_ERROR_NOT_AVAILABLE);
338 rv =
339 secMan->GetSimpleCodebasePrincipal(mDocumentURI,
340 getter_AddRefs(mPrincipal));
341 NS_ENSURE_SUCCESS(rv, rv);
342 mOriginalPrincipal = mPrincipal;
343 } else {
344 mOriginalPrincipal = mPrincipal;
345 if (nsContentUtils::IsSystemPrincipal(mPrincipal)) {
346 // Don't give DOMParsers the system principal. Use a null
347 // principal instead.
348 mPrincipal = do_CreateInstance("@mozilla.org/nullprincipal;1", &rv);
349 NS_ENSURE_SUCCESS(rv, rv);
351 if (!mDocumentURI) {
352 rv = mPrincipal->GetURI(getter_AddRefs(mDocumentURI));
353 NS_ENSURE_SUCCESS(rv, rv);
354 }
355 }
356 }
358 mBaseURI = baseURI;
359 // Note: if mBaseURI is null, fine. Leave it like that; that will use the
360 // documentURI as the base. Otherwise for null principals we'll get
361 // nsDocument::SetBaseURI giving errors.
363 NS_POSTCONDITION(mPrincipal, "Must have principal");
364 NS_POSTCONDITION(mOriginalPrincipal, "Must have original principal");
365 NS_POSTCONDITION(mDocumentURI, "Must have document URI");
366 return NS_OK;
367 }
369 /*static */already_AddRefed<DOMParser>
370 DOMParser::Constructor(const GlobalObject& aOwner,
371 nsIPrincipal* aPrincipal, nsIURI* aDocumentURI,
372 nsIURI* aBaseURI, ErrorResult& rv)
373 {
374 if (!nsContentUtils::IsCallerChrome()) {
375 rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
376 return nullptr;
377 }
378 nsRefPtr<DOMParser> domParser = new DOMParser(aOwner.GetAsSupports());
379 rv = domParser->InitInternal(aOwner.GetAsSupports(), aPrincipal, aDocumentURI,
380 aBaseURI);
381 if (rv.Failed()) {
382 return nullptr;
383 }
384 return domParser.forget();
385 }
387 /*static */already_AddRefed<DOMParser>
388 DOMParser::Constructor(const GlobalObject& aOwner,
389 ErrorResult& rv)
390 {
391 nsCOMPtr<nsIPrincipal> prin;
392 nsCOMPtr<nsIURI> documentURI;
393 nsCOMPtr<nsIURI> baseURI;
394 // No arguments; use the subject principal
395 nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
396 if (!secMan) {
397 rv.Throw(NS_ERROR_UNEXPECTED);
398 return nullptr;
399 }
401 rv = secMan->GetSubjectPrincipal(getter_AddRefs(prin));
402 if (rv.Failed()) {
403 return nullptr;
404 }
406 // We're called from JS; there better be a subject principal, really.
407 if (!prin) {
408 rv.Throw(NS_ERROR_UNEXPECTED);
409 return nullptr;
410 }
412 nsRefPtr<DOMParser> domParser = new DOMParser(aOwner.GetAsSupports());
413 rv = domParser->InitInternal(aOwner.GetAsSupports(), prin, documentURI, baseURI);
414 if (rv.Failed()) {
415 return nullptr;
416 }
417 return domParser.forget();
418 }
420 nsresult
421 DOMParser::InitInternal(nsISupports* aOwner, nsIPrincipal* prin,
422 nsIURI* documentURI, nsIURI* baseURI)
423 {
424 AttemptedInitMarker marker(&mAttemptedInit);
425 if (!documentURI) {
426 // No explicit documentURI; grab document and base URIs off the window our
427 // constructor was called on. Error out if anything untoward happens.
429 // Note that this is a behavior change as far as I can tell -- we're now
430 // using the base URI and document URI of the window off of which the
431 // DOMParser is created, not the window in which parse*() is called.
432 // Does that matter?
434 // Also note that |cx| matches what GetDocumentFromContext() would return,
435 // while GetDocumentFromCaller() gives us the window that the DOMParser()
436 // call was made on.
438 nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aOwner);
439 if (!window) {
440 return NS_ERROR_UNEXPECTED;
441 }
443 baseURI = window->GetDocBaseURI();
444 documentURI = window->GetDocumentURI();
445 if (!documentURI) {
446 return NS_ERROR_UNEXPECTED;
447 }
448 }
450 nsCOMPtr<nsIScriptGlobalObject> scriptglobal = do_QueryInterface(aOwner);
451 return Init(prin, documentURI, baseURI, scriptglobal);
452 }
454 void
455 DOMParser::Init(nsIPrincipal* aPrincipal, nsIURI* aDocumentURI,
456 nsIURI* aBaseURI, mozilla::ErrorResult& rv)
457 {
458 AttemptedInitMarker marker(&mAttemptedInit);
460 JSContext *cx = nsContentUtils::GetCurrentJSContext();
461 if (!cx) {
462 rv.Throw(NS_ERROR_UNEXPECTED);
463 return;
464 }
466 nsIScriptContext* scriptContext = GetScriptContextFromJSContext(cx);
468 nsCOMPtr<nsIPrincipal> principal = aPrincipal;
470 if (!principal && !aDocumentURI) {
471 nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
472 if (!secMan) {
473 rv.Throw(NS_ERROR_UNEXPECTED);
474 return;
475 }
477 rv = secMan->GetSubjectPrincipal(getter_AddRefs(principal));
478 if (rv.Failed()) {
479 return;
480 }
482 // We're called from JS; there better be a subject principal, really.
483 if (!principal) {
484 rv.Throw(NS_ERROR_UNEXPECTED);
485 return;
486 }
487 }
489 rv = Init(principal, aDocumentURI, aBaseURI,
490 scriptContext ? scriptContext->GetGlobalObject() : nullptr);
491 }
493 nsresult
494 DOMParser::SetUpDocument(DocumentFlavor aFlavor, nsIDOMDocument** aResult)
495 {
496 nsCOMPtr<nsIScriptGlobalObject> scriptHandlingObject =
497 do_QueryReferent(mScriptHandlingObject);
498 nsresult rv;
499 if (!mPrincipal) {
500 NS_ENSURE_TRUE(!mAttemptedInit, NS_ERROR_NOT_INITIALIZED);
501 AttemptedInitMarker marker(&mAttemptedInit);
503 nsCOMPtr<nsIPrincipal> prin =
504 do_CreateInstance("@mozilla.org/nullprincipal;1", &rv);
505 NS_ENSURE_SUCCESS(rv, rv);
507 rv = Init(prin, nullptr, nullptr, scriptHandlingObject);
508 NS_ENSURE_SUCCESS(rv, rv);
509 }
511 NS_ASSERTION(mPrincipal, "Must have principal by now");
512 NS_ASSERTION(mDocumentURI, "Must have document URI by now");
514 // Here we have to cheat a little bit... Setting the base URI won't
515 // work if the document has a null principal, so use
516 // mOriginalPrincipal when creating the document, then reset the
517 // principal.
518 return NS_NewDOMDocument(aResult, EmptyString(), EmptyString(), nullptr,
519 mDocumentURI, mBaseURI,
520 mOriginalPrincipal,
521 true,
522 scriptHandlingObject,
523 aFlavor);
524 }