Bug 1874684 - Part 4: Prefer const references instead of copying Instant values....
[gecko.git] / dom / xslt / xslt / txMozillaStylesheetCompiler.cpp
blob252347492d513622f763d68ee86b1e590fd7e91a
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"
11 #include "nsParser.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"
18 #include "nsIURI.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"
25 #include "txLog.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"
32 #include "nsError.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) {
45 if (!aChannel) {
46 return;
49 nsCOMPtr<nsIURI> uri;
50 aChannel->GetOriginalURI(getter_AddRefs(uri));
51 if (!uri) {
52 return;
55 nsAutoCString spec;
56 uri->GetSpec(spec);
57 AppendUTF8toUTF16(spec, aSpec);
60 class txStylesheetSink final : public nsIXMLContentSink,
61 public nsIExpatSink,
62 public nsIStreamListener,
63 public nsIInterfaceRequestor {
64 public:
65 txStylesheetSink(txStylesheetCompiler* aCompiler, nsParser* aParser);
67 NS_DECL_ISUPPORTS
68 NS_DECL_NSIEXPATSINK
69 NS_DECL_NSISTREAMLISTENER
70 NS_DECL_NSIREQUESTOBSERVER
71 NS_DECL_NSIINTERFACEREQUESTOR
73 // nsIContentSink
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; }
84 private:
85 RefPtr<txStylesheetCompiler> mCompiler;
86 nsCOMPtr<nsIStreamListener> mListener;
87 RefPtr<nsParser> mParser;
88 bool mCheckedForXML;
90 protected:
91 ~txStylesheetSink() = default;
93 // This exists solely to suppress a warning from nsDerivedSafe
94 txStylesheetSink();
97 txStylesheetSink::txStylesheetSink(txStylesheetCompiler* aCompiler,
98 nsParser* aParser)
99 : mCompiler(aCompiler),
100 mListener(aParser),
101 mParser(aParser),
102 mCheckedForXML(false) {}
104 NS_IMPL_ISUPPORTS(txStylesheetSink, nsIXMLContentSink, nsIContentSink,
105 nsIExpatSink, nsIStreamListener, nsIRequestObserver,
106 nsIInterfaceRequestor)
108 NS_IMETHODIMP
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);
116 if (NS_FAILED(rv)) {
117 mCompiler->cancel(rv);
119 return rv;
122 return NS_OK;
125 NS_IMETHODIMP
126 txStylesheetSink::HandleEndElement(const char16_t* aName) {
127 nsresult rv = mCompiler->endElement();
128 if (NS_FAILED(rv)) {
129 mCompiler->cancel(rv);
131 return rv;
134 return NS_OK;
137 NS_IMETHODIMP
138 txStylesheetSink::HandleComment(const char16_t* aName) { return NS_OK; }
140 NS_IMETHODIMP
141 txStylesheetSink::HandleCDataSection(const char16_t* aData, uint32_t aLength) {
142 return HandleCharacterData(aData, aLength);
145 NS_IMETHODIMP
146 txStylesheetSink::HandleDoctypeDecl(const nsAString& aSubset,
147 const nsAString& aName,
148 const nsAString& aSystemId,
149 const nsAString& aPublicId,
150 nsISupports* aCatalogData) {
151 return NS_OK;
154 NS_IMETHODIMP
155 txStylesheetSink::HandleCharacterData(const char16_t* aData, uint32_t aLength) {
156 nsresult rv = mCompiler->characters(Substring(aData, aData + aLength));
157 if (NS_FAILED(rv)) {
158 mCompiler->cancel(rv);
159 return rv;
162 return NS_OK;
165 NS_IMETHODIMP
166 txStylesheetSink::HandleProcessingInstruction(const char16_t* aTarget,
167 const char16_t* aData) {
168 return NS_OK;
171 NS_IMETHODIMP
172 txStylesheetSink::HandleXMLDeclaration(const char16_t* aVersion,
173 const char16_t* aEncoding,
174 int32_t aStandalone) {
175 return NS_OK;
178 NS_IMETHODIMP
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.
185 *_retval = true;
187 mCompiler->cancel(NS_ERROR_FAILURE, aErrorText, aSourceText);
189 return NS_OK;
192 NS_IMETHODIMP
193 txStylesheetSink::DidBuildModel(bool aTerminated) {
194 return mCompiler->doneLoading();
197 NS_IMETHODIMP
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);
206 nsAutoString spec;
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);
217 NS_IMETHODIMP
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);
228 if (encoding) {
229 charsetSource = kCharsetFromChannel;
233 if (!encoding) {
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)) {
248 nsresult rv;
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);
265 NS_IMETHODIMP
266 txStylesheetSink::OnStopRequest(nsIRequest* aRequest, nsresult aStatusCode) {
267 bool success = true;
269 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
270 if (httpChannel) {
271 Unused << httpChannel->GetRequestSucceeded(&success);
274 nsresult result = aStatusCode;
275 if (!success) {
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);
289 nsAutoString spec;
290 getSpec(channel, spec);
291 mCompiler->cancel(result, nullptr, spec.get());
294 nsresult rv = mListener->OnStopRequest(aRequest, aStatusCode);
295 mListener = nullptr;
296 mParser = nullptr;
297 return rv;
300 NS_IMETHODIMP
301 txStylesheetSink::GetInterface(const nsIID& aIID, void** aResult) {
302 if (aIID.Equals(NS_GET_IID(nsIAuthPrompt))) {
303 NS_ENSURE_ARG(aResult);
304 *aResult = nullptr;
306 nsresult rv;
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);
317 return NS_OK;
320 return NS_ERROR_NO_INTERFACE;
323 class txCompileObserver final : public txACompileObserver {
324 public:
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);
335 private:
336 RefPtr<txMozillaXSLTProcessor> mProcessor;
337 nsCOMPtr<Document> mLoaderDocument;
339 // This exists solely to suppress a warning from nsDerivedSafe
340 txCompileObserver();
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,
375 nsresult aResult,
376 const char16_t* aErrorText,
377 const char16_t* aParam) {
378 if (NS_SUCCEEDED(aResult)) {
379 mProcessor->setStylesheet(aCompiler->getStylesheet());
380 } else {
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();
390 if (!loadGroup) {
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
401 loadGroup);
403 NS_ENSURE_SUCCESS(rv, rv);
405 channel->SetContentType("text/xml"_ns);
407 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
408 if (httpChannel) {
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);
425 parser->Parse(aUri);
427 return channel->AsyncOpen(sink);
430 nsresult TX_LoadSheet(nsIURI* aUri, txMozillaXSLTProcessor* aProcessor,
431 Document* aLoaderDocument,
432 ReferrerPolicy aReferrerPolicy) {
433 nsIPrincipal* principal = aLoaderDocument->NodePrincipal();
435 nsAutoCString spec;
436 aUri->GetSpec(spec);
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) {
453 nsresult rv = NS_OK;
455 if (aNode->IsElement()) {
456 dom::Element* element = aNode->AsElement();
458 uint32_t attsCount = element->GetAttrCount();
459 UniquePtr<txStylesheetAttr[]> atts;
460 if (attsCount > 0) {
461 atts = MakeUnique<txStylesheetAttr[]>(attsCount);
462 uint32_t counter;
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
480 atts = nullptr;
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()) {
491 nsAutoString chars;
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);
503 return NS_OK;
506 class txSyncCompileObserver final : public txACompileObserver {
507 public:
508 explicit txSyncCompileObserver(txMozillaXSLTProcessor* aProcessor);
510 TX_DECL_ACOMPILEOBSERVER
511 NS_INLINE_DECL_REFCOUNTING(txSyncCompileObserver, override)
513 private:
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
544 // make sense.
545 nsCOMPtr<nsINode> source;
546 if (mProcessor) {
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);
561 if (NS_FAILED(rv)) {
562 nsAutoCString spec;
563 uri->GetSpec(spec);
564 aCompiler->cancel(rv, nullptr, NS_ConvertUTF8toUTF16(spec).get());
565 return rv;
568 rv = aCompiler->doneLoading();
569 return rv;
572 void txSyncCompileObserver::onDoneCompiling(txStylesheetCompiler* aCompiler,
573 nsresult aResult,
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);
586 nsAutoCString spec;
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);
599 uri->GetSpec(spec);
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);
610 if (NS_FAILED(rv)) {
611 compiler->cancel(rv);
612 return rv;
615 rv = compiler->doneLoading();
616 NS_ENSURE_SUCCESS(rv, rv);
618 *aStylesheet = compiler->getStylesheet();
619 NS_ADDREF(*aStylesheet);
621 return NS_OK;