1 /* -*- Mode: C++; tab-width: 4; 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 "nsIAuthPrompt.h"
7 #include "mozilla/dom/Document.h"
8 #include "nsIExpatSink.h"
9 #include "nsIInterfaceRequestor.h"
10 #include "nsILoadGroup.h"
12 #include "nsCharsetSource.h"
13 #include "nsIRequestObserver.h"
14 #include "nsContentPolicyUtils.h"
15 #include "nsIStreamConverterService.h"
16 #include "nsSyncLoadService.h"
17 #include "nsIHttpChannel.h"
19 #include "nsIPrincipal.h"
20 #include "nsIWindowWatcher.h"
21 #include "nsIXMLContentSink.h"
22 #include "nsMimeTypes.h"
23 #include "nsNetUtil.h"
24 #include "nsGkAtoms.h"
26 #include "txMozillaXSLTProcessor.h"
27 #include "txStylesheetCompiler.h"
28 #include "txXMLUtils.h"
29 #include "nsAttrName.h"
30 #include "nsComponentManagerUtils.h"
31 #include "nsIScriptError.h"
33 #include "mozilla/Attributes.h"
34 #include "mozilla/dom/Element.h"
35 #include "mozilla/dom/Text.h"
36 #include "mozilla/Encoding.h"
37 #include "mozilla/UniquePtr.h"
38 #include "ReferrerInfo.h"
40 using namespace mozilla
;
41 using mozilla::dom::Document
;
42 using mozilla::dom::ReferrerPolicy
;
44 static void getSpec(nsIChannel
* aChannel
, nsAString
& aSpec
) {
50 aChannel
->GetOriginalURI(getter_AddRefs(uri
));
57 AppendUTF8toUTF16(spec
, aSpec
);
60 class txStylesheetSink final
: public nsIXMLContentSink
,
62 public nsIStreamListener
,
63 public nsIInterfaceRequestor
{
65 txStylesheetSink(txStylesheetCompiler
* aCompiler
, nsParser
* aParser
);
69 NS_DECL_NSISTREAMLISTENER
70 NS_DECL_NSIREQUESTOBSERVER
71 NS_DECL_NSIINTERFACEREQUESTOR
74 NS_IMETHOD
WillParse(void) override
{ return NS_OK
; }
75 NS_IMETHOD
DidBuildModel(bool aTerminated
) override
;
76 NS_IMETHOD
WillInterrupt(void) override
{ return NS_OK
; }
77 void WillResume() override
{}
78 NS_IMETHOD
SetParser(nsParserBase
* aParser
) override
{ return NS_OK
; }
79 virtual void FlushPendingNotifications(mozilla::FlushType aType
) override
{}
80 virtual void SetDocumentCharset(NotNull
<const Encoding
*> aEncoding
) override
{
82 virtual nsISupports
* GetTarget() override
{ return nullptr; }
85 RefPtr
<txStylesheetCompiler
> mCompiler
;
86 nsCOMPtr
<nsIStreamListener
> mListener
;
87 RefPtr
<nsParser
> mParser
;
91 ~txStylesheetSink() = default;
93 // This exists solely to suppress a warning from nsDerivedSafe
97 txStylesheetSink::txStylesheetSink(txStylesheetCompiler
* aCompiler
,
99 : mCompiler(aCompiler
),
102 mCheckedForXML(false) {}
104 NS_IMPL_ISUPPORTS(txStylesheetSink
, nsIXMLContentSink
, nsIContentSink
,
105 nsIExpatSink
, nsIStreamListener
, nsIRequestObserver
,
106 nsIInterfaceRequestor
)
109 txStylesheetSink::HandleStartElement(const char16_t
* aName
,
110 const char16_t
** aAtts
,
111 uint32_t aAttsCount
, uint32_t aLineNumber
,
112 uint32_t aColumnNumber
) {
113 MOZ_ASSERT(aAttsCount
% 2 == 0, "incorrect aAttsCount");
115 nsresult rv
= mCompiler
->startElement(aName
, aAtts
, aAttsCount
/ 2);
117 mCompiler
->cancel(rv
);
126 txStylesheetSink::HandleEndElement(const char16_t
* aName
) {
127 nsresult rv
= mCompiler
->endElement();
129 mCompiler
->cancel(rv
);
138 txStylesheetSink::HandleComment(const char16_t
* aName
) { return NS_OK
; }
141 txStylesheetSink::HandleCDataSection(const char16_t
* aData
, uint32_t aLength
) {
142 return HandleCharacterData(aData
, aLength
);
146 txStylesheetSink::HandleDoctypeDecl(const nsAString
& aSubset
,
147 const nsAString
& aName
,
148 const nsAString
& aSystemId
,
149 const nsAString
& aPublicId
,
150 nsISupports
* aCatalogData
) {
155 txStylesheetSink::HandleCharacterData(const char16_t
* aData
, uint32_t aLength
) {
156 nsresult rv
= mCompiler
->characters(Substring(aData
, aData
+ aLength
));
158 mCompiler
->cancel(rv
);
166 txStylesheetSink::HandleProcessingInstruction(const char16_t
* aTarget
,
167 const char16_t
* aData
) {
172 txStylesheetSink::HandleXMLDeclaration(const char16_t
* aVersion
,
173 const char16_t
* aEncoding
,
174 int32_t aStandalone
) {
179 txStylesheetSink::ReportError(const char16_t
* aErrorText
,
180 const char16_t
* aSourceText
,
181 nsIScriptError
* aError
, bool* _retval
) {
182 MOZ_ASSERT(aError
&& aSourceText
&& aErrorText
, "Check arguments!!!");
184 // The expat driver should report the error.
187 mCompiler
->cancel(NS_ERROR_FAILURE
, aErrorText
, aSourceText
);
193 txStylesheetSink::DidBuildModel(bool aTerminated
) {
194 return mCompiler
->doneLoading();
198 txStylesheetSink::OnDataAvailable(nsIRequest
* aRequest
,
199 nsIInputStream
* aInputStream
,
200 uint64_t aOffset
, uint32_t aCount
) {
201 if (!mCheckedForXML
) {
202 Maybe
<bool> isForXML
= mParser
->IsForParsingXML();
203 mCheckedForXML
= isForXML
.isSome();
204 if (mCheckedForXML
&& !isForXML
.value()) {
205 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(aRequest
);
207 getSpec(channel
, spec
);
208 mCompiler
->cancel(NS_ERROR_XSLT_WRONG_MIME_TYPE
, nullptr, spec
.get());
210 return NS_ERROR_XSLT_WRONG_MIME_TYPE
;
214 return mListener
->OnDataAvailable(aRequest
, aInputStream
, aOffset
, aCount
);
218 txStylesheetSink::OnStartRequest(nsIRequest
* aRequest
) {
219 int32_t charsetSource
= kCharsetFromDocTypeDefault
;
221 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(aRequest
);
223 // check channel's charset...
224 const Encoding
* encoding
= nullptr;
225 nsAutoCString charsetVal
;
226 if (NS_SUCCEEDED(channel
->GetContentCharset(charsetVal
))) {
227 encoding
= Encoding::ForLabel(charsetVal
);
229 charsetSource
= kCharsetFromChannel
;
234 encoding
= UTF_8_ENCODING
;
237 mParser
->SetDocumentCharset(WrapNotNull(encoding
), charsetSource
, false);
239 nsAutoCString contentType
;
240 channel
->GetContentType(contentType
);
242 // Time to sniff! Note: this should go away once file channels do
243 // sniffing themselves.
244 nsCOMPtr
<nsIURI
> uri
;
245 channel
->GetURI(getter_AddRefs(uri
));
246 if (uri
->SchemeIs("file") &&
247 contentType
.EqualsLiteral(UNKNOWN_CONTENT_TYPE
)) {
249 nsCOMPtr
<nsIStreamConverterService
> serv
=
250 do_GetService("@mozilla.org/streamConverters;1", &rv
);
251 if (NS_SUCCEEDED(rv
)) {
252 nsCOMPtr
<nsIStreamListener
> converter
;
253 rv
= serv
->AsyncConvertData(UNKNOWN_CONTENT_TYPE
, "*/*", mListener
,
254 NS_ISUPPORTS_CAST(nsIParser
*, mParser
),
255 getter_AddRefs(converter
));
256 if (NS_SUCCEEDED(rv
)) {
257 mListener
= converter
;
262 return mListener
->OnStartRequest(aRequest
);
266 txStylesheetSink::OnStopRequest(nsIRequest
* aRequest
, nsresult aStatusCode
) {
269 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(aRequest
);
271 Unused
<< httpChannel
->GetRequestSucceeded(&success
);
274 nsresult result
= aStatusCode
;
276 // XXX We sometimes want to use aStatusCode here, but the parser resets
277 // it to NS_ERROR_NOINTERFACE because we don't implement
278 // nsIHTMLContentSink.
279 result
= NS_ERROR_XSLT_NETWORK_ERROR
;
280 } else if (!mCheckedForXML
) {
281 Maybe
<bool> isForXML
= mParser
->IsForParsingXML();
282 if (isForXML
.isSome() && !isForXML
.value()) {
283 result
= NS_ERROR_XSLT_WRONG_MIME_TYPE
;
287 if (NS_FAILED(result
)) {
288 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(aRequest
);
290 getSpec(channel
, spec
);
291 mCompiler
->cancel(result
, nullptr, spec
.get());
294 nsresult rv
= mListener
->OnStopRequest(aRequest
, aStatusCode
);
301 txStylesheetSink::GetInterface(const nsIID
& aIID
, void** aResult
) {
302 if (aIID
.Equals(NS_GET_IID(nsIAuthPrompt
))) {
303 NS_ENSURE_ARG(aResult
);
307 nsCOMPtr
<nsIWindowWatcher
> wwatcher
=
308 do_GetService(NS_WINDOWWATCHER_CONTRACTID
, &rv
);
309 NS_ENSURE_SUCCESS(rv
, rv
);
311 nsCOMPtr
<nsIAuthPrompt
> prompt
;
312 rv
= wwatcher
->GetNewAuthPrompter(nullptr, getter_AddRefs(prompt
));
313 NS_ENSURE_SUCCESS(rv
, rv
);
315 prompt
.forget(aResult
);
320 return NS_ERROR_NO_INTERFACE
;
323 class txCompileObserver final
: public txACompileObserver
{
325 txCompileObserver(txMozillaXSLTProcessor
* aProcessor
,
326 Document
* aLoaderDocument
);
328 TX_DECL_ACOMPILEOBSERVER
329 NS_INLINE_DECL_REFCOUNTING(txCompileObserver
, override
)
331 nsresult
startLoad(nsIURI
* aUri
, txStylesheetCompiler
* aCompiler
,
332 nsIPrincipal
* aSourcePrincipal
,
333 ReferrerPolicy aReferrerPolicy
);
336 RefPtr
<txMozillaXSLTProcessor
> mProcessor
;
337 nsCOMPtr
<Document
> mLoaderDocument
;
339 // This exists solely to suppress a warning from nsDerivedSafe
342 // Private destructor, to discourage deletion outside of Release():
343 ~txCompileObserver() = default;
346 txCompileObserver::txCompileObserver(txMozillaXSLTProcessor
* aProcessor
,
347 Document
* aLoaderDocument
)
348 : mProcessor(aProcessor
), mLoaderDocument(aLoaderDocument
) {}
350 nsresult
txCompileObserver::loadURI(const nsAString
& aUri
,
351 const nsAString
& aReferrerUri
,
352 ReferrerPolicy aReferrerPolicy
,
353 txStylesheetCompiler
* aCompiler
) {
354 if (mProcessor
->IsLoadDisabled()) {
355 return NS_ERROR_XSLT_LOAD_BLOCKED_ERROR
;
358 nsCOMPtr
<nsIURI
> uri
;
359 nsresult rv
= NS_NewURI(getter_AddRefs(uri
), aUri
);
360 NS_ENSURE_SUCCESS(rv
, rv
);
362 nsCOMPtr
<nsIURI
> referrerUri
;
363 rv
= NS_NewURI(getter_AddRefs(referrerUri
), aReferrerUri
);
364 NS_ENSURE_SUCCESS(rv
, rv
);
366 OriginAttributes attrs
;
367 nsCOMPtr
<nsIPrincipal
> referrerPrincipal
=
368 BasePrincipal::CreateContentPrincipal(referrerUri
, attrs
);
369 NS_ENSURE_TRUE(referrerPrincipal
, NS_ERROR_FAILURE
);
371 return startLoad(uri
, aCompiler
, referrerPrincipal
, aReferrerPolicy
);
374 void txCompileObserver::onDoneCompiling(txStylesheetCompiler
* aCompiler
,
376 const char16_t
* aErrorText
,
377 const char16_t
* aParam
) {
378 if (NS_SUCCEEDED(aResult
)) {
379 mProcessor
->setStylesheet(aCompiler
->getStylesheet());
381 mProcessor
->reportError(aResult
, aErrorText
, aParam
);
385 nsresult
txCompileObserver::startLoad(nsIURI
* aUri
,
386 txStylesheetCompiler
* aCompiler
,
387 nsIPrincipal
* aReferrerPrincipal
,
388 ReferrerPolicy aReferrerPolicy
) {
389 nsCOMPtr
<nsILoadGroup
> loadGroup
= mLoaderDocument
->GetDocumentLoadGroup();
391 return NS_ERROR_FAILURE
;
394 nsCOMPtr
<nsIChannel
> channel
;
395 nsresult rv
= NS_NewChannelWithTriggeringPrincipal(
396 getter_AddRefs(channel
), aUri
, mLoaderDocument
,
397 aReferrerPrincipal
, // triggeringPrincipal
398 nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT
,
399 nsIContentPolicy::TYPE_XSLT
,
400 nullptr, // aPerformanceStorage
403 NS_ENSURE_SUCCESS(rv
, rv
);
405 channel
->SetContentType("text/xml"_ns
);
407 nsCOMPtr
<nsIHttpChannel
> httpChannel(do_QueryInterface(channel
));
409 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
;
410 nsresult rv
= aReferrerPrincipal
->CreateReferrerInfo(
411 aReferrerPolicy
, getter_AddRefs(referrerInfo
));
412 if (NS_SUCCEEDED(rv
)) {
413 rv
= httpChannel
->SetReferrerInfoWithoutClone(referrerInfo
);
414 MOZ_ASSERT(NS_SUCCEEDED(rv
));
418 RefPtr
<nsParser
> parser
= new nsParser();
419 RefPtr
<txStylesheetSink
> sink
= new txStylesheetSink(aCompiler
, parser
);
421 channel
->SetNotificationCallbacks(sink
);
423 parser
->SetCommand(kLoadAsData
);
424 parser
->SetContentSink(sink
);
427 return channel
->AsyncOpen(sink
);
430 nsresult
TX_LoadSheet(nsIURI
* aUri
, txMozillaXSLTProcessor
* aProcessor
,
431 Document
* aLoaderDocument
,
432 ReferrerPolicy aReferrerPolicy
) {
433 nsIPrincipal
* principal
= aLoaderDocument
->NodePrincipal();
437 MOZ_LOG(txLog::xslt
, LogLevel::Info
, ("TX_LoadSheet: %s\n", spec
.get()));
439 RefPtr
<txCompileObserver
> observer
=
440 new txCompileObserver(aProcessor
, aLoaderDocument
);
442 RefPtr
<txStylesheetCompiler
> compiler
= new txStylesheetCompiler(
443 NS_ConvertUTF8toUTF16(spec
), aReferrerPolicy
, observer
);
445 return observer
->startLoad(aUri
, compiler
, principal
, aReferrerPolicy
);
449 * handling DOM->txStylesheet
450 * Observer needs to do synchronous loads.
452 static nsresult
handleNode(nsINode
* aNode
, txStylesheetCompiler
* aCompiler
) {
455 if (aNode
->IsElement()) {
456 dom::Element
* element
= aNode
->AsElement();
458 uint32_t attsCount
= element
->GetAttrCount();
459 UniquePtr
<txStylesheetAttr
[]> atts
;
461 atts
= MakeUnique
<txStylesheetAttr
[]>(attsCount
);
463 for (counter
= 0; counter
< attsCount
; ++counter
) {
464 txStylesheetAttr
& att
= atts
[counter
];
465 const nsAttrName
* name
= element
->GetAttrNameAt(counter
);
466 att
.mNamespaceID
= name
->NamespaceID();
467 att
.mLocalName
= name
->LocalName();
468 att
.mPrefix
= name
->GetPrefix();
469 element
->GetAttr(att
.mNamespaceID
, att
.mLocalName
, att
.mValue
);
473 mozilla::dom::NodeInfo
* ni
= element
->NodeInfo();
475 rv
= aCompiler
->startElement(ni
->NamespaceID(), ni
->NameAtom(),
476 ni
->GetPrefixAtom(), atts
.get(), attsCount
);
477 NS_ENSURE_SUCCESS(rv
, rv
);
479 // explicitly destroy the attrs here since we no longer need it
482 for (nsIContent
* child
= element
->GetFirstChild(); child
;
483 child
= child
->GetNextSibling()) {
484 rv
= handleNode(child
, aCompiler
);
485 NS_ENSURE_SUCCESS(rv
, rv
);
488 rv
= aCompiler
->endElement();
489 NS_ENSURE_SUCCESS(rv
, rv
);
490 } else if (dom::Text
* text
= aNode
->GetAsText()) {
492 text
->AppendTextTo(chars
);
493 rv
= aCompiler
->characters(chars
);
494 NS_ENSURE_SUCCESS(rv
, rv
);
495 } else if (aNode
->IsDocument()) {
496 for (nsIContent
* child
= aNode
->GetFirstChild(); child
;
497 child
= child
->GetNextSibling()) {
498 rv
= handleNode(child
, aCompiler
);
499 NS_ENSURE_SUCCESS(rv
, rv
);
506 class txSyncCompileObserver final
: public txACompileObserver
{
508 explicit txSyncCompileObserver(txMozillaXSLTProcessor
* aProcessor
);
510 TX_DECL_ACOMPILEOBSERVER
511 NS_INLINE_DECL_REFCOUNTING(txSyncCompileObserver
, override
)
514 // Private destructor, to discourage deletion outside of Release():
515 ~txSyncCompileObserver() = default;
517 RefPtr
<txMozillaXSLTProcessor
> mProcessor
;
520 txSyncCompileObserver::txSyncCompileObserver(txMozillaXSLTProcessor
* aProcessor
)
521 : mProcessor(aProcessor
) {}
523 nsresult
txSyncCompileObserver::loadURI(const nsAString
& aUri
,
524 const nsAString
& aReferrerUri
,
525 ReferrerPolicy aReferrerPolicy
,
526 txStylesheetCompiler
* aCompiler
) {
527 if (mProcessor
->IsLoadDisabled()) {
528 return NS_ERROR_XSLT_LOAD_BLOCKED_ERROR
;
531 nsCOMPtr
<nsIURI
> uri
;
532 nsresult rv
= NS_NewURI(getter_AddRefs(uri
), aUri
);
533 NS_ENSURE_SUCCESS(rv
, rv
);
535 nsCOMPtr
<nsIURI
> referrerUri
;
536 rv
= NS_NewURI(getter_AddRefs(referrerUri
), aReferrerUri
);
537 NS_ENSURE_SUCCESS(rv
, rv
);
539 nsCOMPtr
<nsIPrincipal
> referrerPrincipal
=
540 BasePrincipal::CreateContentPrincipal(referrerUri
, OriginAttributes());
541 NS_ENSURE_TRUE(referrerPrincipal
, NS_ERROR_FAILURE
);
543 // This is probably called by js, a loadGroup for the channel doesn't
545 nsCOMPtr
<nsINode
> source
;
547 source
= mProcessor
->GetSourceContentModel();
549 dom::nsAutoSyncOperation
sync(source
? source
->OwnerDoc() : nullptr,
550 dom::SyncOperationBehavior::eSuspendInput
);
551 nsCOMPtr
<Document
> document
;
553 rv
= nsSyncLoadService::LoadDocument(
554 uri
, nsIContentPolicy::TYPE_XSLT
, referrerPrincipal
,
555 nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT
, nullptr,
556 source
? source
->OwnerDoc()->CookieJarSettings() : nullptr, false,
557 aReferrerPolicy
, getter_AddRefs(document
));
558 NS_ENSURE_SUCCESS(rv
, rv
);
560 rv
= handleNode(document
, aCompiler
);
564 aCompiler
->cancel(rv
, nullptr, NS_ConvertUTF8toUTF16(spec
).get());
568 rv
= aCompiler
->doneLoading();
572 void txSyncCompileObserver::onDoneCompiling(txStylesheetCompiler
* aCompiler
,
574 const char16_t
* aErrorText
,
575 const char16_t
* aParam
) {}
577 nsresult
TX_CompileStylesheet(nsINode
* aNode
,
578 txMozillaXSLTProcessor
* aProcessor
,
579 txStylesheet
** aStylesheet
) {
580 // If we move GetBaseURI to nsINode this can be simplified.
581 nsCOMPtr
<Document
> doc
= aNode
->OwnerDoc();
583 nsIURI
* nodeBaseURI
= aNode
->GetBaseURI();
584 NS_ENSURE_TRUE(nodeBaseURI
, NS_ERROR_FAILURE
);
587 nodeBaseURI
->GetSpec(spec
);
588 NS_ConvertUTF8toUTF16
baseURI(spec
);
590 nsIURI
* docUri
= doc
->GetDocumentURI();
591 NS_ENSURE_TRUE(docUri
, NS_ERROR_FAILURE
);
593 // We need to remove the ref, a URI with a ref would mean that we have an
594 // embedded stylesheet.
595 nsCOMPtr
<nsIURI
> uri
;
596 NS_GetURIWithoutRef(docUri
, getter_AddRefs(uri
));
597 NS_ENSURE_TRUE(uri
, NS_ERROR_FAILURE
);
600 NS_ConvertUTF8toUTF16
stylesheetURI(spec
);
602 RefPtr
<txSyncCompileObserver
> obs
= new txSyncCompileObserver(aProcessor
);
604 RefPtr
<txStylesheetCompiler
> compiler
=
605 new txStylesheetCompiler(stylesheetURI
, doc
->GetReferrerPolicy(), obs
);
607 compiler
->setBaseURI(baseURI
);
609 nsresult rv
= handleNode(aNode
, compiler
);
611 compiler
->cancel(rv
);
615 rv
= compiler
->doneLoading();
616 NS_ENSURE_SUCCESS(rv
, rv
);
618 *aStylesheet
= compiler
->getStylesheet();
619 NS_ADDREF(*aStylesheet
);