Line data Source code
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/. */
6 :
7 : #include "mozilla/dom/DOMParser.h"
8 :
9 : #include "nsIDOMDocument.h"
10 : #include "nsNetUtil.h"
11 : #include "nsIStreamListener.h"
12 : #include "nsStringStream.h"
13 : #include "nsIScriptError.h"
14 : #include "nsIScriptSecurityManager.h"
15 : #include "nsCRT.h"
16 : #include "nsStreamUtils.h"
17 : #include "nsContentUtils.h"
18 : #include "nsDOMJSUtils.h"
19 : #include "nsError.h"
20 : #include "nsPIDOMWindow.h"
21 : #include "NullPrincipal.h"
22 : #include "mozilla/LoadInfo.h"
23 : #include "mozilla/dom/BindingUtils.h"
24 : #include "mozilla/dom/ScriptSettings.h"
25 :
26 : using namespace mozilla;
27 : using namespace mozilla::dom;
28 :
29 0 : DOMParser::DOMParser()
30 : : mAttemptedInit(false)
31 0 : , mOriginalPrincipalWasSystem(false)
32 : {
33 0 : }
34 :
35 0 : DOMParser::~DOMParser()
36 : {
37 0 : }
38 :
39 : // QueryInterface implementation for DOMParser
40 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMParser)
41 0 : NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
42 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMParser)
43 0 : NS_INTERFACE_MAP_ENTRY(nsIDOMParser)
44 0 : NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
45 0 : NS_INTERFACE_MAP_END
46 :
47 0 : NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DOMParser, mOwner)
48 :
49 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMParser)
50 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMParser)
51 :
52 : static const char*
53 0 : StringFromSupportedType(SupportedType aType)
54 : {
55 0 : return SupportedTypeValues::strings[static_cast<int>(aType)].value;
56 : }
57 :
58 : already_AddRefed<nsIDocument>
59 0 : DOMParser::ParseFromString(const nsAString& aStr, SupportedType aType,
60 : ErrorResult& rv)
61 : {
62 0 : nsCOMPtr<nsIDOMDocument> domDocument;
63 0 : rv = ParseFromString(aStr,
64 : StringFromSupportedType(aType),
65 0 : getter_AddRefs(domDocument));
66 0 : nsCOMPtr<nsIDocument> document(do_QueryInterface(domDocument));
67 0 : return document.forget();
68 : }
69 :
70 : NS_IMETHODIMP
71 0 : DOMParser::ParseFromString(const char16_t *str,
72 : const char *contentType,
73 : nsIDOMDocument **aResult)
74 : {
75 0 : NS_ENSURE_ARG(str);
76 : // Converting a string to an enum value manually is a bit of a pain,
77 : // so let's just use a helper that takes a content-type string.
78 0 : return ParseFromString(nsDependentString(str), contentType, aResult);
79 : }
80 :
81 : nsresult
82 0 : DOMParser::ParseFromString(const nsAString& str,
83 : const char *contentType,
84 : nsIDOMDocument **aResult)
85 : {
86 0 : NS_ENSURE_ARG_POINTER(aResult);
87 :
88 : nsresult rv;
89 :
90 0 : if (!nsCRT::strcmp(contentType, "text/html")) {
91 0 : nsCOMPtr<nsIDOMDocument> domDocument;
92 0 : rv = SetUpDocument(DocumentFlavorHTML, getter_AddRefs(domDocument));
93 0 : NS_ENSURE_SUCCESS(rv, rv);
94 0 : nsCOMPtr<nsIDocument> document = do_QueryInterface(domDocument);
95 :
96 : // Keep the XULXBL state in sync with the XML case.
97 :
98 0 : if (mOriginalPrincipalWasSystem) {
99 0 : document->ForceEnableXULXBL();
100 : }
101 :
102 0 : rv = nsContentUtils::ParseDocumentHTML(str, document, false);
103 0 : NS_ENSURE_SUCCESS(rv, rv);
104 :
105 0 : domDocument.forget(aResult);
106 0 : return rv;
107 : }
108 :
109 0 : nsAutoCString utf8str;
110 : // Convert from UTF16 to UTF8 using fallible allocations
111 0 : if (!AppendUTF16toUTF8(str, utf8str, mozilla::fallible)) {
112 0 : return NS_ERROR_OUT_OF_MEMORY;
113 : }
114 :
115 : // The new stream holds a reference to the buffer
116 0 : nsCOMPtr<nsIInputStream> stream;
117 0 : rv = NS_NewByteInputStream(getter_AddRefs(stream),
118 0 : utf8str.get(), utf8str.Length(),
119 0 : NS_ASSIGNMENT_DEPEND);
120 0 : if (NS_FAILED(rv))
121 0 : return rv;
122 :
123 0 : return ParseFromStream(stream, "UTF-8", utf8str.Length(), contentType, aResult);
124 : }
125 :
126 : already_AddRefed<nsIDocument>
127 0 : DOMParser::ParseFromBuffer(const Sequence<uint8_t>& aBuf, uint32_t aBufLen,
128 : SupportedType aType, ErrorResult& rv)
129 : {
130 0 : if (aBufLen > aBuf.Length()) {
131 0 : rv.Throw(NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY);
132 0 : return nullptr;
133 : }
134 0 : nsCOMPtr<nsIDOMDocument> domDocument;
135 0 : rv = DOMParser::ParseFromBuffer(aBuf.Elements(), aBufLen,
136 : StringFromSupportedType(aType),
137 0 : getter_AddRefs(domDocument));
138 0 : nsCOMPtr<nsIDocument> document(do_QueryInterface(domDocument));
139 0 : return document.forget();
140 : }
141 :
142 : already_AddRefed<nsIDocument>
143 0 : DOMParser::ParseFromBuffer(const Uint8Array& aBuf, uint32_t aBufLen,
144 : SupportedType aType, ErrorResult& rv)
145 : {
146 0 : aBuf.ComputeLengthAndData();
147 :
148 0 : if (aBufLen > aBuf.Length()) {
149 0 : rv.Throw(NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY);
150 0 : return nullptr;
151 : }
152 0 : nsCOMPtr<nsIDOMDocument> domDocument;
153 0 : rv = DOMParser::ParseFromBuffer(aBuf.Data(), aBufLen,
154 : StringFromSupportedType(aType),
155 0 : getter_AddRefs(domDocument));
156 0 : nsCOMPtr<nsIDocument> document(do_QueryInterface(domDocument));
157 0 : return document.forget();
158 : }
159 :
160 : NS_IMETHODIMP
161 0 : DOMParser::ParseFromBuffer(const uint8_t *buf,
162 : uint32_t bufLen,
163 : const char *contentType,
164 : nsIDOMDocument **aResult)
165 : {
166 0 : NS_ENSURE_ARG_POINTER(buf);
167 0 : NS_ENSURE_ARG_POINTER(aResult);
168 :
169 : // The new stream holds a reference to the buffer
170 0 : nsCOMPtr<nsIInputStream> stream;
171 0 : nsresult rv = NS_NewByteInputStream(getter_AddRefs(stream),
172 : reinterpret_cast<const char *>(buf),
173 0 : bufLen, NS_ASSIGNMENT_DEPEND);
174 0 : if (NS_FAILED(rv))
175 0 : return rv;
176 :
177 0 : return ParseFromStream(stream, nullptr, bufLen, contentType, aResult);
178 : }
179 :
180 :
181 : already_AddRefed<nsIDocument>
182 0 : DOMParser::ParseFromStream(nsIInputStream* aStream,
183 : const nsAString& aCharset,
184 : int32_t aContentLength,
185 : SupportedType aType,
186 : ErrorResult& rv)
187 : {
188 0 : nsCOMPtr<nsIDOMDocument> domDocument;
189 0 : rv = DOMParser::ParseFromStream(aStream,
190 0 : NS_ConvertUTF16toUTF8(aCharset).get(),
191 : aContentLength,
192 : StringFromSupportedType(aType),
193 0 : getter_AddRefs(domDocument));
194 0 : nsCOMPtr<nsIDocument> document(do_QueryInterface(domDocument));
195 0 : return document.forget();
196 : }
197 :
198 : NS_IMETHODIMP
199 0 : DOMParser::ParseFromStream(nsIInputStream *stream,
200 : const char *charset,
201 : int32_t contentLength,
202 : const char *contentType,
203 : nsIDOMDocument **aResult)
204 : {
205 0 : NS_ENSURE_ARG(stream);
206 0 : NS_ENSURE_ARG(contentType);
207 0 : NS_ENSURE_ARG_POINTER(aResult);
208 0 : *aResult = nullptr;
209 :
210 0 : bool svg = nsCRT::strcmp(contentType, "image/svg+xml") == 0;
211 :
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 0 : if ((nsCRT::strcmp(contentType, "text/xml") != 0) &&
216 0 : (nsCRT::strcmp(contentType, "application/xml") != 0) &&
217 0 : (nsCRT::strcmp(contentType, "application/xhtml+xml") != 0) &&
218 0 : !svg)
219 0 : return NS_ERROR_NOT_IMPLEMENTED;
220 :
221 : nsresult rv;
222 :
223 : // Put the nsCOMPtr out here so we hold a ref to the stream as needed
224 0 : nsCOMPtr<nsIInputStream> bufferedStream;
225 0 : if (!NS_InputStreamIsBuffered(stream)) {
226 0 : rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream), stream,
227 0 : 4096);
228 0 : NS_ENSURE_SUCCESS(rv, rv);
229 :
230 0 : stream = bufferedStream;
231 : }
232 :
233 0 : nsCOMPtr<nsIDOMDocument> domDocument;
234 0 : rv = SetUpDocument(svg ? DocumentFlavorSVG : DocumentFlavorLegacyGuess,
235 0 : getter_AddRefs(domDocument));
236 0 : NS_ENSURE_SUCCESS(rv, rv);
237 :
238 : // Create a fake channel
239 0 : nsCOMPtr<nsIChannel> parserChannel;
240 0 : NS_NewInputStreamChannel(getter_AddRefs(parserChannel),
241 : mDocumentURI,
242 : nullptr, // aStream
243 : mPrincipal,
244 : nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL,
245 : nsIContentPolicy::TYPE_OTHER,
246 0 : nsDependentCString(contentType));
247 0 : NS_ENSURE_STATE(parserChannel);
248 :
249 0 : if (charset) {
250 0 : parserChannel->SetContentCharset(nsDependentCString(charset));
251 : }
252 :
253 : // Tell the document to start loading
254 0 : nsCOMPtr<nsIStreamListener> listener;
255 :
256 : // Have to pass false for reset here, else the reset will remove
257 : // our event listener. Should that listener addition move to later
258 : // than this call?
259 0 : nsCOMPtr<nsIDocument> document(do_QueryInterface(domDocument));
260 0 : if (!document) return NS_ERROR_FAILURE;
261 :
262 : // Keep the XULXBL state in sync with the HTML case
263 :
264 0 : if (mOriginalPrincipalWasSystem) {
265 0 : document->ForceEnableXULXBL();
266 : }
267 :
268 0 : rv = document->StartDocumentLoad(kLoadAsData, parserChannel,
269 : nullptr, nullptr,
270 0 : getter_AddRefs(listener),
271 0 : false);
272 :
273 0 : if (NS_FAILED(rv) || !listener) {
274 0 : return NS_ERROR_FAILURE;
275 : }
276 :
277 : // Now start pumping data to the listener
278 : nsresult status;
279 :
280 0 : rv = listener->OnStartRequest(parserChannel, nullptr);
281 0 : if (NS_FAILED(rv))
282 0 : parserChannel->Cancel(rv);
283 0 : parserChannel->GetStatus(&status);
284 :
285 0 : if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(status)) {
286 0 : rv = listener->OnDataAvailable(parserChannel, nullptr, stream, 0,
287 0 : contentLength);
288 0 : if (NS_FAILED(rv))
289 0 : parserChannel->Cancel(rv);
290 0 : parserChannel->GetStatus(&status);
291 : }
292 :
293 0 : rv = listener->OnStopRequest(parserChannel, nullptr, status);
294 : // Failure returned from OnStopRequest does not affect the final status of
295 : // the channel, so we do not need to call Cancel(rv) as we do above.
296 :
297 0 : if (NS_FAILED(rv)) {
298 0 : return NS_ERROR_FAILURE;
299 : }
300 :
301 0 : domDocument.swap(*aResult);
302 :
303 0 : return NS_OK;
304 : }
305 :
306 : NS_IMETHODIMP
307 0 : DOMParser::Init(nsIPrincipal* principal, nsIURI* documentURI,
308 : nsIURI* baseURI, nsIGlobalObject* aScriptObject)
309 : {
310 0 : NS_ENSURE_STATE(!mAttemptedInit);
311 0 : mAttemptedInit = true;
312 0 : NS_ENSURE_ARG(principal || documentURI);
313 0 : mDocumentURI = documentURI;
314 :
315 0 : if (!mDocumentURI) {
316 0 : principal->GetURI(getter_AddRefs(mDocumentURI));
317 : // If we have the system principal, then we'll just use the null principals
318 : // uri.
319 0 : if (!mDocumentURI && !nsContentUtils::IsSystemPrincipal(principal)) {
320 0 : return NS_ERROR_INVALID_ARG;
321 : }
322 : }
323 :
324 0 : mScriptHandlingObject = do_GetWeakReference(aScriptObject);
325 0 : mPrincipal = principal;
326 : nsresult rv;
327 0 : if (!mPrincipal) {
328 : // BUG 1237080 -- in this case we're getting a chrome privilege scripted
329 : // DOMParser object creation without an explicit principal set. This is
330 : // now deprecated.
331 0 : nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
332 0 : NS_LITERAL_CSTRING("DOM"),
333 : nullptr,
334 : nsContentUtils::eDOM_PROPERTIES,
335 : "ChromeScriptedDOMParserWithoutPrincipal",
336 : nullptr,
337 : 0,
338 0 : documentURI);
339 :
340 0 : OriginAttributes attrs;
341 0 : mPrincipal = BasePrincipal::CreateCodebasePrincipal(mDocumentURI, attrs);
342 0 : NS_ENSURE_TRUE(mPrincipal, NS_ERROR_FAILURE);
343 : } else {
344 0 : if (nsContentUtils::IsSystemPrincipal(mPrincipal)) {
345 : // Don't give DOMParsers the system principal. Use a null
346 : // principal instead.
347 0 : mOriginalPrincipalWasSystem = true;
348 0 : mPrincipal = NullPrincipal::Create();
349 :
350 0 : if (!mDocumentURI) {
351 0 : rv = mPrincipal->GetURI(getter_AddRefs(mDocumentURI));
352 0 : NS_ENSURE_SUCCESS(rv, rv);
353 : }
354 : }
355 : }
356 :
357 0 : mBaseURI = baseURI;
358 :
359 0 : NS_POSTCONDITION(mPrincipal, "Must have principal");
360 0 : NS_POSTCONDITION(mDocumentURI, "Must have document URI");
361 0 : return NS_OK;
362 : }
363 :
364 : /*static */already_AddRefed<DOMParser>
365 0 : DOMParser::Constructor(const GlobalObject& aOwner,
366 : nsIPrincipal* aPrincipal, nsIURI* aDocumentURI,
367 : nsIURI* aBaseURI, ErrorResult& rv)
368 : {
369 0 : if (aOwner.CallerType() != CallerType::System) {
370 0 : rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
371 0 : return nullptr;
372 : }
373 0 : RefPtr<DOMParser> domParser = new DOMParser(aOwner.GetAsSupports());
374 0 : rv = domParser->InitInternal(aOwner.GetAsSupports(), aPrincipal, aDocumentURI,
375 0 : aBaseURI);
376 0 : if (rv.Failed()) {
377 0 : return nullptr;
378 : }
379 0 : return domParser.forget();
380 : }
381 :
382 : /*static */already_AddRefed<DOMParser>
383 0 : DOMParser::Constructor(const GlobalObject& aOwner,
384 : ErrorResult& rv)
385 : {
386 0 : RefPtr<DOMParser> domParser = new DOMParser(aOwner.GetAsSupports());
387 0 : rv = domParser->InitInternal(aOwner.GetAsSupports(),
388 : nsContentUtils::SubjectPrincipal(),
389 0 : nullptr, nullptr);
390 0 : if (rv.Failed()) {
391 0 : return nullptr;
392 : }
393 0 : return domParser.forget();
394 : }
395 :
396 : nsresult
397 0 : DOMParser::InitInternal(nsISupports* aOwner, nsIPrincipal* prin,
398 : nsIURI* documentURI, nsIURI* baseURI)
399 : {
400 0 : AttemptedInitMarker marker(&mAttemptedInit);
401 0 : if (!documentURI) {
402 : // No explicit documentURI; grab document and base URIs off the window our
403 : // constructor was called on. Error out if anything untoward happens.
404 :
405 : // Note that this is a behavior change as far as I can tell -- we're now
406 : // using the base URI and document URI of the window off of which the
407 : // DOMParser is created, not the window in which parse*() is called.
408 : // Does that matter?
409 :
410 : // Also note that |cx| matches what GetDocumentFromContext() would return,
411 : // while GetDocumentFromCaller() gives us the window that the DOMParser()
412 : // call was made on.
413 :
414 0 : nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aOwner);
415 0 : if (!window) {
416 0 : return NS_ERROR_UNEXPECTED;
417 : }
418 :
419 0 : baseURI = window->GetDocBaseURI();
420 0 : documentURI = window->GetDocumentURI();
421 0 : if (!documentURI) {
422 0 : return NS_ERROR_UNEXPECTED;
423 : }
424 : }
425 :
426 0 : nsCOMPtr<nsIGlobalObject> scriptglobal = do_QueryInterface(aOwner);
427 0 : return Init(prin, documentURI, baseURI, scriptglobal);
428 : }
429 :
430 : void
431 0 : DOMParser::Init(nsIPrincipal* aPrincipal, nsIURI* aDocumentURI,
432 : nsIURI* aBaseURI, mozilla::ErrorResult& rv)
433 : {
434 0 : AttemptedInitMarker marker(&mAttemptedInit);
435 :
436 0 : nsCOMPtr<nsIPrincipal> principal = aPrincipal;
437 0 : if (!principal && !aDocumentURI) {
438 0 : principal = nsContentUtils::SubjectPrincipal();
439 : }
440 :
441 0 : rv = Init(principal, aDocumentURI, aBaseURI, GetEntryGlobal());
442 0 : }
443 :
444 : nsresult
445 0 : DOMParser::SetUpDocument(DocumentFlavor aFlavor, nsIDOMDocument** aResult)
446 : {
447 : // We should really QI to nsIGlobalObject here, but nsDocument gets confused
448 : // if we pass it a scriptHandlingObject that doesn't QI to
449 : // nsIScriptGlobalObject, and test_isequalnode.js (an xpcshell test without
450 : // a window global) breaks. The correct solution is just to wean nsDocument
451 : // off of nsIScriptGlobalObject, but that's a yak to shave another day.
452 : nsCOMPtr<nsIScriptGlobalObject> scriptHandlingObject =
453 0 : do_QueryReferent(mScriptHandlingObject);
454 : nsresult rv;
455 0 : if (!mPrincipal) {
456 0 : NS_ENSURE_TRUE(!mAttemptedInit, NS_ERROR_NOT_INITIALIZED);
457 0 : AttemptedInitMarker marker(&mAttemptedInit);
458 :
459 0 : nsCOMPtr<nsIPrincipal> prin = NullPrincipal::Create();
460 0 : rv = Init(prin, nullptr, nullptr, scriptHandlingObject);
461 0 : NS_ENSURE_SUCCESS(rv, rv);
462 : }
463 :
464 0 : NS_ASSERTION(mPrincipal, "Must have principal by now");
465 0 : NS_ASSERTION(mDocumentURI, "Must have document URI by now");
466 :
467 0 : return NS_NewDOMDocument(aResult, EmptyString(), EmptyString(), nullptr,
468 : mDocumentURI, mBaseURI,
469 : mPrincipal,
470 : true,
471 : scriptHandlingObject,
472 0 : aFlavor);
473 : }
|