1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either the GNU General Public License Version 2 or later (the "GPL"), or
26 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : #include "jsapi.h"
39 : #include "nsDOMParser.h"
40 : #include "nsIURI.h"
41 : #include "nsIChannel.h"
42 : #include "nsILoadGroup.h"
43 : #include "nsIInputStream.h"
44 : #include "nsNetUtil.h"
45 : #include "nsStringStream.h"
46 : #include "nsIDOMDocument.h"
47 : #include "nsIScriptSecurityManager.h"
48 : #include "nsIPrincipal.h"
49 : #include "nsDOMClassInfoID.h"
50 : #include "nsReadableUtils.h"
51 : #include "nsCRT.h"
52 : #include "nsStreamUtils.h"
53 : #include "nsThreadUtils.h"
54 : #include "nsNetCID.h"
55 : #include "nsContentUtils.h"
56 : #include "nsDOMJSUtils.h"
57 : #include "nsDOMError.h"
58 : #include "nsIDOMWindow.h"
59 : #include "nsPIDOMWindow.h"
60 : #include "mozilla/AutoRestore.h"
61 :
62 : using namespace mozilla;
63 :
64 569 : nsDOMParser::nsDOMParser()
65 569 : : mAttemptedInit(false)
66 : {
67 569 : }
68 :
69 1138 : nsDOMParser::~nsDOMParser()
70 : {
71 2276 : }
72 :
73 : DOMCI_DATA(DOMParser, nsDOMParser)
74 :
75 : // QueryInterface implementation for nsDOMParser
76 4124 : NS_INTERFACE_MAP_BEGIN(nsDOMParser)
77 4124 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMParser)
78 3555 : NS_INTERFACE_MAP_ENTRY(nsIDOMParser)
79 2417 : NS_INTERFACE_MAP_ENTRY(nsIDOMParserJS)
80 2347 : NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
81 2347 : NS_INTERFACE_MAP_ENTRY(nsIJSNativeInitializer)
82 2347 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(DOMParser)
83 1778 : NS_INTERFACE_MAP_END
84 :
85 :
86 2346 : NS_IMPL_ADDREF(nsDOMParser)
87 2346 : NS_IMPL_RELEASE(nsDOMParser)
88 :
89 : NS_IMETHODIMP
90 246 : nsDOMParser::ParseFromString(const PRUnichar *str,
91 : const char *contentType,
92 : nsIDOMDocument **aResult)
93 : {
94 246 : NS_ENSURE_ARG(str);
95 245 : NS_ENSURE_ARG_POINTER(aResult);
96 :
97 : nsresult rv;
98 :
99 245 : if (!nsCRT::strcmp(contentType, "text/html")) {
100 0 : nsCOMPtr<nsIDOMDocument> domDocument;
101 0 : rv = SetUpDocument(DocumentFlavorHTML, getter_AddRefs(domDocument));
102 0 : NS_ENSURE_SUCCESS(rv, rv);
103 0 : nsCOMPtr<nsIDocument> document = do_QueryInterface(domDocument);
104 0 : nsDependentString sourceBuffer(str);
105 0 : rv = nsContentUtils::ParseDocumentHTML(sourceBuffer, document, false);
106 0 : NS_ENSURE_SUCCESS(rv, rv);
107 :
108 : // Keep the XULXBL state, base URL and principal setting in sync with the
109 : // XML case
110 :
111 0 : if (nsContentUtils::IsSystemPrincipal(mOriginalPrincipal)) {
112 0 : document->ForceEnableXULXBL();
113 : }
114 :
115 : // Make sure to give this document the right base URI
116 0 : document->SetBaseURI(mBaseURI);
117 : // And the right principal
118 0 : document->SetPrincipal(mPrincipal);
119 :
120 0 : domDocument.forget(aResult);
121 0 : return rv;
122 : }
123 :
124 490 : NS_ConvertUTF16toUTF8 data(str);
125 :
126 : // The new stream holds a reference to the buffer
127 490 : nsCOMPtr<nsIInputStream> stream;
128 245 : rv = NS_NewByteInputStream(getter_AddRefs(stream),
129 245 : data.get(), data.Length(),
130 245 : NS_ASSIGNMENT_DEPEND);
131 245 : if (NS_FAILED(rv))
132 0 : return rv;
133 :
134 245 : return ParseFromStream(stream, "UTF-8", data.Length(), contentType, aResult);
135 : }
136 :
137 : NS_IMETHODIMP
138 1 : nsDOMParser::ParseFromBuffer(const PRUint8 *buf,
139 : PRUint32 bufLen,
140 : const char *contentType,
141 : nsIDOMDocument **aResult)
142 : {
143 1 : NS_ENSURE_ARG_POINTER(buf);
144 1 : NS_ENSURE_ARG_POINTER(aResult);
145 :
146 : // The new stream holds a reference to the buffer
147 2 : nsCOMPtr<nsIInputStream> stream;
148 1 : nsresult rv = NS_NewByteInputStream(getter_AddRefs(stream),
149 : reinterpret_cast<const char *>(buf),
150 1 : bufLen, NS_ASSIGNMENT_DEPEND);
151 1 : if (NS_FAILED(rv))
152 0 : return rv;
153 :
154 1 : return ParseFromStream(stream, nsnull, bufLen, contentType, aResult);
155 : }
156 :
157 :
158 : NS_IMETHODIMP
159 569 : nsDOMParser::ParseFromStream(nsIInputStream *stream,
160 : const char *charset,
161 : PRInt32 contentLength,
162 : const char *contentType,
163 : nsIDOMDocument **aResult)
164 : {
165 569 : NS_ENSURE_ARG(stream);
166 569 : NS_ENSURE_ARG(contentType);
167 569 : NS_ENSURE_ARG_POINTER(aResult);
168 569 : *aResult = nsnull;
169 :
170 569 : bool svg = nsCRT::strcmp(contentType, "image/svg+xml") == 0;
171 :
172 : // For now, we can only create XML documents.
173 : //XXXsmaug Should we create an HTMLDocument (in XHTML mode)
174 : // for "application/xhtml+xml"?
175 670 : if ((nsCRT::strcmp(contentType, "text/xml") != 0) &&
176 101 : (nsCRT::strcmp(contentType, "application/xml") != 0) &&
177 0 : (nsCRT::strcmp(contentType, "application/xhtml+xml") != 0) &&
178 0 : !svg)
179 0 : return NS_ERROR_NOT_IMPLEMENTED;
180 :
181 : nsresult rv;
182 :
183 : // Put the nsCOMPtr out here so we hold a ref to the stream as needed
184 1138 : nsCOMPtr<nsIInputStream> bufferedStream;
185 569 : if (!NS_InputStreamIsBuffered(stream)) {
186 323 : rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream), stream,
187 323 : 4096);
188 323 : NS_ENSURE_SUCCESS(rv, rv);
189 :
190 323 : stream = bufferedStream;
191 : }
192 :
193 1138 : nsCOMPtr<nsIDOMDocument> domDocument;
194 : rv = SetUpDocument(svg ? DocumentFlavorSVG : DocumentFlavorLegacyGuess,
195 569 : getter_AddRefs(domDocument));
196 569 : NS_ENSURE_SUCCESS(rv, rv);
197 :
198 : // Create a fake channel
199 1138 : nsCOMPtr<nsIChannel> parserChannel;
200 569 : NS_NewInputStreamChannel(getter_AddRefs(parserChannel), mDocumentURI, nsnull,
201 1138 : nsDependentCString(contentType), nsnull);
202 569 : NS_ENSURE_STATE(parserChannel);
203 :
204 : // More principal-faking here
205 569 : parserChannel->SetOwner(mOriginalPrincipal);
206 :
207 569 : if (charset) {
208 556 : parserChannel->SetContentCharset(nsDependentCString(charset));
209 : }
210 :
211 : // Tell the document to start loading
212 1138 : nsCOMPtr<nsIStreamListener> listener;
213 :
214 : // Have to pass false for reset here, else the reset will remove
215 : // our event listener. Should that listener addition move to later
216 : // than this call? Then we wouldn't need to mess around with
217 : // SetPrincipal, etc, probably!
218 1138 : nsCOMPtr<nsIDocument> document(do_QueryInterface(domDocument));
219 569 : if (!document) return NS_ERROR_FAILURE;
220 :
221 : // Keep the XULXBL state, base URL and principal setting in sync with the
222 : // HTML case
223 :
224 569 : if (nsContentUtils::IsSystemPrincipal(mOriginalPrincipal)) {
225 70 : document->ForceEnableXULXBL();
226 : }
227 :
228 569 : rv = document->StartDocumentLoad(kLoadAsData, parserChannel,
229 : nsnull, nsnull,
230 569 : getter_AddRefs(listener),
231 569 : false);
232 :
233 : // Make sure to give this document the right base URI
234 569 : document->SetBaseURI(mBaseURI);
235 :
236 : // And the right principal
237 569 : document->SetPrincipal(mPrincipal);
238 :
239 569 : if (NS_FAILED(rv) || !listener) {
240 0 : return NS_ERROR_FAILURE;
241 : }
242 :
243 : // Now start pumping data to the listener
244 : nsresult status;
245 :
246 569 : rv = listener->OnStartRequest(parserChannel, nsnull);
247 569 : if (NS_FAILED(rv))
248 0 : parserChannel->Cancel(rv);
249 569 : parserChannel->GetStatus(&status);
250 :
251 569 : if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(status)) {
252 569 : rv = listener->OnDataAvailable(parserChannel, nsnull, stream, 0,
253 569 : contentLength);
254 569 : if (NS_FAILED(rv))
255 0 : parserChannel->Cancel(rv);
256 569 : parserChannel->GetStatus(&status);
257 : }
258 :
259 569 : rv = listener->OnStopRequest(parserChannel, nsnull, status);
260 : // Failure returned from OnStopRequest does not affect the final status of
261 : // the channel, so we do not need to call Cancel(rv) as we do above.
262 :
263 569 : if (NS_FAILED(rv)) {
264 0 : return NS_ERROR_FAILURE;
265 : }
266 :
267 569 : domDocument.swap(*aResult);
268 :
269 569 : return NS_OK;
270 : }
271 :
272 : NS_IMETHODIMP
273 568 : nsDOMParser::Init(nsIPrincipal* principal, nsIURI* documentURI,
274 : nsIURI* baseURI, nsIScriptGlobalObject* aScriptObject)
275 : {
276 568 : NS_ENSURE_STATE(!mAttemptedInit);
277 568 : mAttemptedInit = true;
278 :
279 568 : NS_ENSURE_ARG(principal || documentURI);
280 :
281 568 : mDocumentURI = documentURI;
282 :
283 568 : if (!mDocumentURI) {
284 568 : principal->GetURI(getter_AddRefs(mDocumentURI));
285 : // If we have the system principal, then we'll just use the null principals
286 : // uri.
287 568 : if (!mDocumentURI && !nsContentUtils::IsSystemPrincipal(principal)) {
288 0 : return NS_ERROR_INVALID_ARG;
289 : }
290 : }
291 :
292 568 : mScriptHandlingObject = do_GetWeakReference(aScriptObject);
293 568 : mPrincipal = principal;
294 : nsresult rv;
295 568 : if (!mPrincipal) {
296 0 : nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
297 0 : NS_ENSURE_TRUE(secMan, NS_ERROR_NOT_AVAILABLE);
298 : rv =
299 0 : secMan->GetCodebasePrincipal(mDocumentURI, getter_AddRefs(mPrincipal));
300 0 : NS_ENSURE_SUCCESS(rv, rv);
301 0 : mOriginalPrincipal = mPrincipal;
302 : } else {
303 568 : mOriginalPrincipal = mPrincipal;
304 568 : if (nsContentUtils::IsSystemPrincipal(mPrincipal)) {
305 : // Don't give DOMParsers the system principal. Use a null
306 : // principal instead.
307 70 : mPrincipal = do_CreateInstance("@mozilla.org/nullprincipal;1", &rv);
308 70 : NS_ENSURE_SUCCESS(rv, rv);
309 :
310 70 : if (!mDocumentURI) {
311 70 : rv = mPrincipal->GetURI(getter_AddRefs(mDocumentURI));
312 70 : NS_ENSURE_SUCCESS(rv, rv);
313 : }
314 : }
315 : }
316 :
317 568 : mBaseURI = baseURI;
318 : // Note: if mBaseURI is null, fine. Leave it like that; that will use the
319 : // documentURI as the base. Otherwise for null principals we'll get
320 : // nsDocument::SetBaseURI giving errors.
321 :
322 568 : NS_POSTCONDITION(mPrincipal, "Must have principal");
323 568 : NS_POSTCONDITION(mOriginalPrincipal, "Must have original principal");
324 568 : NS_POSTCONDITION(mDocumentURI, "Must have document URI");
325 568 : return NS_OK;
326 : }
327 :
328 : static nsQueryInterface
329 0 : JSvalToInterface(JSContext* cx, jsval val, nsIXPConnect* xpc, bool* wasNull)
330 : {
331 0 : if (val == JSVAL_NULL) {
332 0 : *wasNull = true;
333 0 : return nsQueryInterface(nsnull);
334 : }
335 :
336 0 : *wasNull = false;
337 0 : if (JSVAL_IS_OBJECT(val)) {
338 0 : JSObject* arg = JSVAL_TO_OBJECT(val);
339 :
340 0 : nsCOMPtr<nsIXPConnectWrappedNative> native;
341 0 : xpc->GetWrappedNativeOfJSObject(cx, arg, getter_AddRefs(native));
342 :
343 : // do_QueryWrappedNative is not null-safe
344 0 : if (native) {
345 0 : return do_QueryWrappedNative(native);
346 : }
347 : }
348 :
349 0 : return nsQueryInterface(nsnull);
350 : }
351 :
352 : static nsresult
353 0 : GetInitArgs(JSContext *cx, PRUint32 argc, jsval *argv,
354 : nsIPrincipal** aPrincipal, nsIURI** aDocumentURI,
355 : nsIURI** aBaseURI)
356 : {
357 : // Only proceed if the caller has UniversalXPConnect.
358 : bool haveUniversalXPConnect;
359 0 : nsresult rv = nsContentUtils::GetSecurityManager()->
360 0 : IsCapabilityEnabled("UniversalXPConnect", &haveUniversalXPConnect);
361 0 : NS_ENSURE_SUCCESS(rv, rv);
362 :
363 0 : if (!haveUniversalXPConnect) {
364 0 : return NS_ERROR_DOM_SECURITY_ERR;
365 : }
366 :
367 0 : nsIXPConnect* xpc = nsContentUtils::XPConnect();
368 :
369 : // First arg is our principal. If someone passes something that's
370 : // not a principal and not null, die to prevent privilege escalation.
371 : bool wasNull;
372 0 : nsCOMPtr<nsIPrincipal> prin = JSvalToInterface(cx, argv[0], xpc, &wasNull);
373 0 : if (!prin && !wasNull) {
374 0 : return NS_ERROR_INVALID_ARG;
375 : }
376 :
377 0 : nsCOMPtr<nsIURI> documentURI;
378 0 : nsCOMPtr<nsIURI> baseURI;
379 0 : if (argc > 1) {
380 : // Grab our document URI too. Again, if it's something unexpected bail
381 : // out.
382 0 : documentURI = JSvalToInterface(cx, argv[1], xpc, &wasNull);
383 0 : if (!documentURI && !wasNull) {
384 0 : return NS_ERROR_INVALID_ARG;
385 : }
386 :
387 0 : if (argc > 2) {
388 : // Grab our base URI as well
389 0 : baseURI = JSvalToInterface(cx, argv[2], xpc, &wasNull);
390 0 : if (!baseURI && !wasNull) {
391 0 : return NS_ERROR_INVALID_ARG;
392 : }
393 : }
394 : }
395 :
396 0 : NS_IF_ADDREF(*aPrincipal = prin);
397 0 : NS_IF_ADDREF(*aDocumentURI = documentURI);
398 0 : NS_IF_ADDREF(*aBaseURI = baseURI);
399 0 : return NS_OK;
400 : }
401 :
402 : NS_IMETHODIMP
403 0 : nsDOMParser::Initialize(nsISupports* aOwner, JSContext* cx, JSObject* obj,
404 : PRUint32 argc, jsval *argv)
405 : {
406 0 : AttemptedInitMarker marker(&mAttemptedInit);
407 0 : nsCOMPtr<nsIPrincipal> prin;
408 0 : nsCOMPtr<nsIURI> documentURI;
409 0 : nsCOMPtr<nsIURI> baseURI;
410 0 : if (argc > 0) {
411 0 : nsresult rv = GetInitArgs(cx, argc, argv, getter_AddRefs(prin),
412 0 : getter_AddRefs(documentURI),
413 0 : getter_AddRefs(baseURI));
414 0 : NS_ENSURE_SUCCESS(rv, rv);
415 : } else {
416 : // No arguments; use the subject principal
417 0 : nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
418 0 : NS_ENSURE_TRUE(secMan, NS_ERROR_UNEXPECTED);
419 :
420 0 : nsresult rv = secMan->GetSubjectPrincipal(getter_AddRefs(prin));
421 0 : NS_ENSURE_SUCCESS(rv, rv);
422 :
423 : // We're called from JS; there better be a subject principal, really.
424 0 : NS_ENSURE_TRUE(prin, NS_ERROR_UNEXPECTED);
425 : }
426 :
427 0 : NS_ASSERTION(prin, "Must have principal by now");
428 :
429 0 : if (!documentURI) {
430 : // No explicit documentURI; grab document and base URIs off the window our
431 : // constructor was called on. Error out if anything untoward happens.
432 :
433 : // Note that this is a behavior change as far as I can tell -- we're now
434 : // using the base URI and document URI of the window off of which the
435 : // DOMParser is created, not the window in which parse*() is called.
436 : // Does that matter?
437 :
438 : // Also note that |cx| matches what GetDocumentFromContext() would return,
439 : // while GetDocumentFromCaller() gives us the window that the DOMParser()
440 : // call was made on.
441 :
442 0 : nsCOMPtr<nsIDocument> doc;
443 0 : nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aOwner);
444 0 : if (aOwner) {
445 0 : nsCOMPtr<nsIDOMDocument> domdoc = window->GetExtantDocument();
446 0 : doc = do_QueryInterface(domdoc);
447 : }
448 :
449 0 : if (!doc) {
450 0 : return NS_ERROR_UNEXPECTED;
451 : }
452 :
453 0 : baseURI = doc->GetDocBaseURI();
454 0 : documentURI = doc->GetDocumentURI();
455 : }
456 :
457 0 : nsCOMPtr<nsIScriptGlobalObject> scriptglobal = do_QueryInterface(aOwner);
458 0 : return Init(prin, documentURI, baseURI, scriptglobal);
459 : }
460 :
461 : NS_IMETHODIMP
462 70 : nsDOMParser::Init(nsIPrincipal *aPrincipal, nsIURI *aDocumentURI,
463 : nsIURI *aBaseURI)
464 : {
465 140 : AttemptedInitMarker marker(&mAttemptedInit);
466 :
467 70 : JSContext *cx = nsContentUtils::GetCurrentJSContext();
468 70 : NS_ENSURE_TRUE(cx, NS_ERROR_UNEXPECTED);
469 :
470 70 : nsIScriptContext* scriptContext = GetScriptContextFromJSContext(cx);
471 :
472 140 : nsCOMPtr<nsIPrincipal> principal = aPrincipal;
473 :
474 70 : if (!principal && !aDocumentURI) {
475 70 : nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
476 70 : NS_ENSURE_TRUE(secMan, NS_ERROR_UNEXPECTED);
477 :
478 70 : nsresult rv = secMan->GetSubjectPrincipal(getter_AddRefs(principal));
479 70 : NS_ENSURE_SUCCESS(rv, rv);
480 :
481 : // We're called from JS; there better be a subject principal, really.
482 70 : NS_ENSURE_TRUE(principal, NS_ERROR_UNEXPECTED);
483 : }
484 :
485 : return Init(principal, aDocumentURI, aBaseURI,
486 70 : scriptContext ? scriptContext->GetGlobalObject() : nsnull);
487 : }
488 :
489 : nsresult
490 569 : nsDOMParser::SetUpDocument(DocumentFlavor aFlavor, nsIDOMDocument** aResult)
491 : {
492 : nsCOMPtr<nsIScriptGlobalObject> scriptHandlingObject =
493 1138 : do_QueryReferent(mScriptHandlingObject);
494 : nsresult rv;
495 569 : if (!mPrincipal) {
496 498 : NS_ENSURE_TRUE(!mAttemptedInit, NS_ERROR_NOT_INITIALIZED);
497 996 : AttemptedInitMarker marker(&mAttemptedInit);
498 :
499 : nsCOMPtr<nsIPrincipal> prin =
500 996 : do_CreateInstance("@mozilla.org/nullprincipal;1", &rv);
501 498 : NS_ENSURE_SUCCESS(rv, rv);
502 :
503 498 : rv = Init(prin, nsnull, nsnull, scriptHandlingObject);
504 498 : NS_ENSURE_SUCCESS(rv, rv);
505 : }
506 :
507 569 : NS_ASSERTION(mPrincipal, "Must have principal by now");
508 569 : NS_ASSERTION(mDocumentURI, "Must have document URI by now");
509 :
510 : // Here we have to cheat a little bit... Setting the base URI won't
511 : // work if the document has a null principal, so use
512 : // mOriginalPrincipal when creating the document, then reset the
513 : // principal.
514 1138 : return nsContentUtils::CreateDocument(EmptyString(), EmptyString(), nsnull,
515 : mDocumentURI, mBaseURI,
516 : mOriginalPrincipal,
517 : scriptHandlingObject,
518 : aFlavor,
519 1707 : aResult);
520 : }
|