Bug 1874684 - Part 4: Prefer const references instead of copying Instant values....
[gecko.git] / dom / xslt / xslt / txMozillaXSLTProcessor.cpp
blobc26aea1bb9a3011b5b7e52b6a1d83b710c617e7b
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 "txMozillaXSLTProcessor.h"
7 #include "nsError.h"
8 #include "mozilla/dom/Element.h"
9 #include "mozilla/dom/Document.h"
10 #include "nsIStringBundle.h"
11 #include "nsIURI.h"
12 #include "XPathResult.h"
13 #include "txExecutionState.h"
14 #include "txMozillaTextOutput.h"
15 #include "txMozillaXMLOutput.h"
16 #include "txURIUtils.h"
17 #include "txXMLUtils.h"
18 #include "txUnknownHandler.h"
19 #include "txXSLTMsgsURL.h"
20 #include "txXSLTProcessor.h"
21 #include "nsIPrincipal.h"
22 #include "nsThreadUtils.h"
23 #include "jsapi.h"
24 #include "txExprParser.h"
25 #include "nsJSUtils.h"
26 #include "nsIXPConnect.h"
27 #include "nsNameSpaceManager.h"
28 #include "nsVariant.h"
29 #include "nsTextNode.h"
30 #include "mozilla/Components.h"
31 #include "mozilla/dom/DocumentFragment.h"
32 #include "mozilla/dom/XSLTProcessorBinding.h"
34 using namespace mozilla;
35 using namespace mozilla::dom;
37 /**
38 * Output Handler Factories
40 class txToDocHandlerFactory : public txAOutputHandlerFactory {
41 public:
42 txToDocHandlerFactory(txExecutionState* aEs, Document* aSourceDocument,
43 nsITransformObserver* aObserver, bool aDocumentIsData)
44 : mEs(aEs),
45 mSourceDocument(aSourceDocument),
46 mObserver(aObserver),
47 mDocumentIsData(aDocumentIsData) {}
49 TX_DECL_TXAOUTPUTHANDLERFACTORY
51 private:
52 txExecutionState* mEs;
53 nsCOMPtr<Document> mSourceDocument;
54 nsCOMPtr<nsITransformObserver> mObserver;
55 bool mDocumentIsData;
58 class txToFragmentHandlerFactory : public txAOutputHandlerFactory {
59 public:
60 explicit txToFragmentHandlerFactory(DocumentFragment* aFragment)
61 : mFragment(aFragment) {}
63 TX_DECL_TXAOUTPUTHANDLERFACTORY
65 private:
66 RefPtr<DocumentFragment> mFragment;
69 nsresult txToDocHandlerFactory::createHandlerWith(
70 txOutputFormat* aFormat, txAXMLEventHandler** aHandler) {
71 *aHandler = nullptr;
72 switch (aFormat->mMethod) {
73 case eMethodNotSet:
74 case eXMLOutput: {
75 *aHandler = new txUnknownHandler(mEs);
76 return NS_OK;
79 case eHTMLOutput: {
80 UniquePtr<txMozillaXMLOutput> handler(
81 new txMozillaXMLOutput(mSourceDocument, aFormat, mObserver));
83 nsresult rv = handler->createResultDocument(
84 u""_ns, kNameSpaceID_None, mSourceDocument, mDocumentIsData);
85 if (NS_SUCCEEDED(rv)) {
86 *aHandler = handler.release();
89 return rv;
92 case eTextOutput: {
93 UniquePtr<txMozillaTextOutput> handler(
94 new txMozillaTextOutput(mSourceDocument, mObserver));
96 nsresult rv = handler->createResultDocument(mDocumentIsData);
97 if (NS_SUCCEEDED(rv)) {
98 *aHandler = handler.release();
101 return rv;
105 MOZ_CRASH("Unknown output method");
107 return NS_ERROR_FAILURE;
110 nsresult txToDocHandlerFactory::createHandlerWith(
111 txOutputFormat* aFormat, const nsAString& aName, int32_t aNsID,
112 txAXMLEventHandler** aHandler) {
113 *aHandler = nullptr;
114 switch (aFormat->mMethod) {
115 case eMethodNotSet: {
116 NS_ERROR("How can method not be known when root element is?");
117 return NS_ERROR_UNEXPECTED;
120 case eXMLOutput:
121 case eHTMLOutput: {
122 UniquePtr<txMozillaXMLOutput> handler(
123 new txMozillaXMLOutput(mSourceDocument, aFormat, mObserver));
125 nsresult rv = handler->createResultDocument(aName, aNsID, mSourceDocument,
126 mDocumentIsData);
127 if (NS_SUCCEEDED(rv)) {
128 *aHandler = handler.release();
131 return rv;
134 case eTextOutput: {
135 UniquePtr<txMozillaTextOutput> handler(
136 new txMozillaTextOutput(mSourceDocument, mObserver));
138 nsresult rv = handler->createResultDocument(mDocumentIsData);
139 if (NS_SUCCEEDED(rv)) {
140 *aHandler = handler.release();
143 return rv;
147 MOZ_CRASH("Unknown output method");
149 return NS_ERROR_FAILURE;
152 nsresult txToFragmentHandlerFactory::createHandlerWith(
153 txOutputFormat* aFormat, txAXMLEventHandler** aHandler) {
154 *aHandler = nullptr;
155 switch (aFormat->mMethod) {
156 case eMethodNotSet: {
157 txOutputFormat format;
158 format.merge(*aFormat);
159 nsCOMPtr<Document> doc = mFragment->OwnerDoc();
161 if (doc->IsHTMLDocument()) {
162 format.mMethod = eHTMLOutput;
163 } else {
164 format.mMethod = eXMLOutput;
167 *aHandler = new txMozillaXMLOutput(&format, mFragment, false);
168 break;
171 case eXMLOutput:
172 case eHTMLOutput: {
173 *aHandler = new txMozillaXMLOutput(aFormat, mFragment, false);
174 break;
177 case eTextOutput: {
178 *aHandler = new txMozillaTextOutput(mFragment);
179 break;
182 return NS_OK;
185 nsresult txToFragmentHandlerFactory::createHandlerWith(
186 txOutputFormat* aFormat, const nsAString& aName, int32_t aNsID,
187 txAXMLEventHandler** aHandler) {
188 *aHandler = nullptr;
189 NS_ASSERTION(aFormat->mMethod != eMethodNotSet,
190 "How can method not be known when root element is?");
191 NS_ENSURE_TRUE(aFormat->mMethod != eMethodNotSet, NS_ERROR_UNEXPECTED);
192 return createHandlerWith(aFormat, aHandler);
195 class txVariable : public txIGlobalParameter {
196 using XSLTParameterValue = txMozillaXSLTProcessor::XSLTParameterValue;
197 using OwningXSLTParameterValue =
198 txMozillaXSLTProcessor::OwningXSLTParameterValue;
200 public:
201 explicit txVariable(UniquePtr<OwningXSLTParameterValue>&& aValue)
202 : mUnionValue(std::move(aValue)) {}
203 nsresult getValue(txAExprResult** aValue) override {
204 if (!mValue) {
205 nsresult rv = convert(*mUnionValue, getter_AddRefs(mValue));
206 NS_ENSURE_SUCCESS(rv, rv);
209 NS_ADDREF(*aValue = mValue);
211 return NS_OK;
213 OwningXSLTParameterValue getUnionValue() {
214 return OwningXSLTParameterValue(*mUnionValue);
216 void setValue(UniquePtr<OwningXSLTParameterValue>&& aValue) {
217 mValue = nullptr;
218 mUnionValue = std::move(aValue);
221 static UniquePtr<OwningXSLTParameterValue> convertToOwning(
222 const XSLTParameterValue& aValue, ErrorResult& aError);
224 friend void ImplCycleCollectionTraverse(
225 nsCycleCollectionTraversalCallback& aCallback, txVariable& aVariable,
226 const char* aName, uint32_t aFlags);
228 private:
229 static nsresult convert(const OwningXSLTParameterValue& aUnionValue,
230 txAExprResult** aValue);
232 UniquePtr<OwningXSLTParameterValue> mUnionValue;
233 RefPtr<txAExprResult> mValue;
236 inline void ImplCycleCollectionTraverse(
237 nsCycleCollectionTraversalCallback& aCallback, txVariable& aVariable,
238 const char* aName, uint32_t aFlags) {
239 ImplCycleCollectionTraverse(aCallback, *aVariable.mUnionValue, aName, aFlags);
242 inline void ImplCycleCollectionUnlink(
243 txOwningExpandedNameMap<txIGlobalParameter>& aMap) {
244 aMap.clear();
247 inline void ImplCycleCollectionTraverse(
248 nsCycleCollectionTraversalCallback& aCallback,
249 txOwningExpandedNameMap<txIGlobalParameter>& aMap, const char* aName,
250 uint32_t aFlags = 0) {
251 aFlags |= CycleCollectionEdgeNameArrayFlag;
252 txOwningExpandedNameMap<txIGlobalParameter>::iterator iter(aMap);
253 while (iter.next()) {
254 ImplCycleCollectionTraverse(
255 aCallback, *static_cast<txVariable*>(iter.value()), aName, aFlags);
260 * txMozillaXSLTProcessor
263 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(txMozillaXSLTProcessor, mOwner,
264 mEmbeddedStylesheetRoot, mSource,
265 mVariables)
267 NS_IMPL_CYCLE_COLLECTING_ADDREF(txMozillaXSLTProcessor)
268 NS_IMPL_CYCLE_COLLECTING_RELEASE(txMozillaXSLTProcessor)
270 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(txMozillaXSLTProcessor)
271 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
272 NS_INTERFACE_MAP_ENTRY(nsIDocumentTransformer)
273 NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
274 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDocumentTransformer)
275 NS_INTERFACE_MAP_END
277 txMozillaXSLTProcessor::txMozillaXSLTProcessor()
278 : mOwner(nullptr),
279 mStylesheetDocument(nullptr),
280 mTransformResult(NS_OK),
281 mCompileResult(NS_OK),
282 mFlags(0) {}
284 txMozillaXSLTProcessor::txMozillaXSLTProcessor(nsISupports* aOwner)
285 : mOwner(aOwner),
286 mStylesheetDocument(nullptr),
287 mTransformResult(NS_OK),
288 mCompileResult(NS_OK),
289 mFlags(0) {}
291 txMozillaXSLTProcessor::~txMozillaXSLTProcessor() {
292 if (mStylesheetDocument) {
293 mStylesheetDocument->RemoveMutationObserver(this);
297 NS_IMETHODIMP
298 txMozillaXSLTProcessor::SetTransformObserver(nsITransformObserver* aObserver) {
299 mObserver = aObserver;
300 return NS_OK;
303 NS_IMETHODIMP
304 txMozillaXSLTProcessor::SetSourceContentModel(nsINode* aSource) {
305 mSource = aSource;
307 if (NS_FAILED(mTransformResult)) {
308 notifyError();
309 return NS_OK;
312 if (mStylesheet) {
313 return DoTransform();
316 return NS_OK;
319 NS_IMETHODIMP
320 txMozillaXSLTProcessor::AddXSLTParamNamespace(const nsString& aPrefix,
321 const nsString& aNamespace) {
322 RefPtr<nsAtom> pre = NS_Atomize(aPrefix);
323 return mParamNamespaceMap.mapNamespace(pre, aNamespace);
326 class txXSLTParamContext : public txIParseContext, public txIEvalContext {
327 public:
328 txXSLTParamContext(txNamespaceMap* aResolver, const txXPathNode& aContext,
329 txResultRecycler* aRecycler)
330 : mResolver(aResolver), mContext(aContext), mRecycler(aRecycler) {}
332 // txIParseContext
333 nsresult resolveNamespacePrefix(nsAtom* aPrefix, int32_t& aID) override {
334 aID = mResolver->lookupNamespace(aPrefix);
335 return aID == kNameSpaceID_Unknown ? NS_ERROR_DOM_NAMESPACE_ERR : NS_OK;
337 nsresult resolveFunctionCall(nsAtom* aName, int32_t aID,
338 FunctionCall** aFunction) override {
339 return NS_ERROR_XPATH_UNKNOWN_FUNCTION;
341 bool caseInsensitiveNameTests() override { return false; }
342 void SetErrorOffset(uint32_t aOffset) override {}
344 // txIEvalContext
345 nsresult getVariable(int32_t aNamespace, nsAtom* aLName,
346 txAExprResult*& aResult) override {
347 aResult = nullptr;
348 return NS_ERROR_INVALID_ARG;
350 nsresult isStripSpaceAllowed(const txXPathNode& aNode,
351 bool& aAllowed) override {
352 aAllowed = false;
354 return NS_OK;
356 void* getPrivateContext() override { return nullptr; }
357 txResultRecycler* recycler() override { return mRecycler; }
358 void receiveError(const nsAString& aMsg, nsresult aRes) override {}
359 const txXPathNode& getContextNode() override { return mContext; }
360 uint32_t size() override { return 1; }
361 uint32_t position() override { return 1; }
363 private:
364 txNamespaceMap* mResolver;
365 const txXPathNode& mContext;
366 txResultRecycler* mRecycler;
369 NS_IMETHODIMP
370 txMozillaXSLTProcessor::AddXSLTParam(const nsString& aName,
371 const nsString& aNamespace,
372 const nsString& aSelect,
373 const nsString& aValue,
374 nsINode* aContext) {
375 nsresult rv = NS_OK;
377 if (aSelect.IsVoid() == aValue.IsVoid()) {
378 // Ignore if neither or both are specified
379 return NS_ERROR_FAILURE;
382 RefPtr<txAExprResult> value;
383 uint16_t resultType;
384 if (!aSelect.IsVoid()) {
385 // Set up context
386 UniquePtr<txXPathNode> contextNode(
387 txXPathNativeNode::createXPathNode(aContext));
388 NS_ENSURE_TRUE(contextNode, NS_ERROR_OUT_OF_MEMORY);
390 if (!mRecycler) {
391 mRecycler = new txResultRecycler;
394 txXSLTParamContext paramContext(&mParamNamespaceMap, *contextNode,
395 mRecycler);
397 // Parse
398 UniquePtr<Expr> expr;
399 rv = txExprParser::createExpr(aSelect, &paramContext,
400 getter_Transfers(expr));
401 NS_ENSURE_SUCCESS(rv, rv);
403 // Evaluate
404 rv = expr->evaluate(&paramContext, getter_AddRefs(value));
405 NS_ENSURE_SUCCESS(rv, rv);
407 switch (value->getResultType()) {
408 case txAExprResult::NUMBER:
409 resultType = XPathResult::NUMBER_TYPE;
410 break;
411 case txAExprResult::STRING:
412 resultType = XPathResult::STRING_TYPE;
413 break;
414 case txAExprResult::BOOLEAN:
415 resultType = XPathResult::BOOLEAN_TYPE;
416 break;
417 case txAExprResult::NODESET:
418 resultType = XPathResult::UNORDERED_NODE_ITERATOR_TYPE;
419 break;
420 default:
421 MOZ_ASSERT_UNREACHABLE(
422 "We shouldn't have a txAExprResult::RESULT_TREE_FRAGMENT here.");
423 return NS_ERROR_FAILURE;
425 } else {
426 value = new StringResult(aValue, nullptr);
427 resultType = XPathResult::STRING_TYPE;
430 RefPtr<nsAtom> name = NS_Atomize(aName);
431 int32_t nsId = kNameSpaceID_Unknown;
432 rv = nsNameSpaceManager::GetInstance()->RegisterNameSpace(aNamespace, nsId);
433 NS_ENSURE_SUCCESS(rv, rv);
435 RefPtr<XPathResult> xpathResult = MakeRefPtr<XPathResult>(aContext);
437 ErrorResult error;
438 xpathResult->SetExprResult(value, resultType, aContext, error);
439 if (error.Failed()) {
440 return error.StealNSResult();
443 UniquePtr<OwningXSLTParameterValue> varValue =
444 MakeUnique<OwningXSLTParameterValue>();
445 varValue->SetAsXPathResult() = xpathResult.forget();
447 txExpandedName varName(nsId, name);
448 txVariable* var = static_cast<txVariable*>(mVariables.get(varName));
449 if (var) {
450 var->setValue(std::move(varValue));
452 return NS_OK;
455 var = new txVariable(std::move(varValue));
457 return mVariables.add(varName, var);
460 class nsTransformBlockerEvent : public mozilla::Runnable {
461 public:
462 RefPtr<txMozillaXSLTProcessor> mProcessor;
464 explicit nsTransformBlockerEvent(txMozillaXSLTProcessor* processor)
465 : mozilla::Runnable("nsTransformBlockerEvent"), mProcessor(processor) {}
467 ~nsTransformBlockerEvent() {
468 nsCOMPtr<Document> document =
469 mProcessor->GetSourceContentModel()->OwnerDoc();
470 document->UnblockOnload(true);
473 NS_IMETHOD Run() override {
474 mProcessor->TransformToDoc(nullptr, false);
475 return NS_OK;
479 nsresult txMozillaXSLTProcessor::DoTransform() {
480 NS_ENSURE_TRUE(mSource, NS_ERROR_UNEXPECTED);
481 NS_ENSURE_TRUE(mStylesheet, NS_ERROR_UNEXPECTED);
482 NS_ASSERTION(mObserver, "no observer");
483 NS_ASSERTION(NS_IsMainThread(), "should only be on main thread");
485 nsCOMPtr<nsIRunnable> event = new nsTransformBlockerEvent(this);
486 mSource->OwnerDoc()->BlockOnload();
487 nsresult rv = NS_DispatchToCurrentThread(event);
488 if (NS_FAILED(rv)) {
489 // XXX Maybe we should just display the source document in this case?
490 // Also, set up context information, see bug 204655.
491 reportError(rv, nullptr, nullptr);
494 return rv;
497 void txMozillaXSLTProcessor::ImportStylesheet(nsINode& aStyle,
498 mozilla::ErrorResult& aRv) {
499 // We don't support importing multiple stylesheets yet.
500 if (NS_WARN_IF(mStylesheetDocument || mStylesheet)) {
501 aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
502 return;
505 if (!nsContentUtils::SubjectPrincipalOrSystemIfNativeCaller()->Subsumes(
506 aStyle.NodePrincipal())) {
507 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
508 return;
511 if (NS_WARN_IF(!aStyle.IsElement() && !aStyle.IsDocument())) {
512 aRv.Throw(NS_ERROR_INVALID_ARG);
513 return;
516 nsresult rv =
517 TX_CompileStylesheet(&aStyle, this, getter_AddRefs(mStylesheet));
518 // XXX set up exception context, bug 204658
519 if (NS_WARN_IF(NS_FAILED(rv))) {
520 aRv.Throw(rv);
521 return;
524 mStylesheetDocument = aStyle.OwnerDoc();
525 if (aStyle.IsElement()) {
526 mEmbeddedStylesheetRoot = aStyle.AsElement();
529 mStylesheetDocument->AddMutationObserver(this);
532 already_AddRefed<Document> txMozillaXSLTProcessor::TransformToDocument(
533 nsINode& aSource, ErrorResult& aRv) {
534 if (NS_WARN_IF(NS_FAILED(mCompileResult))) {
535 aRv.Throw(mCompileResult);
536 return nullptr;
539 if (!nsContentUtils::CanCallerAccess(&aSource)) {
540 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
541 return nullptr;
544 nsresult rv = ensureStylesheet();
545 if (NS_WARN_IF(NS_FAILED(rv))) {
546 aRv.Throw(rv);
547 return nullptr;
550 mSource = &aSource;
552 nsCOMPtr<Document> doc;
553 rv = TransformToDoc(getter_AddRefs(doc), true);
554 if (NS_FAILED(rv)) {
555 aRv.Throw(rv);
556 return nullptr;
558 return doc.forget();
561 class XSLTProcessRequest final : public nsIRequest {
562 public:
563 explicit XSLTProcessRequest(txExecutionState* aState) : mState(aState) {}
565 NS_DECL_ISUPPORTS
566 NS_DECL_NSIREQUEST
568 void Done() { mState = nullptr; }
570 private:
571 ~XSLTProcessRequest() {}
572 txExecutionState* mState;
574 NS_IMPL_ISUPPORTS(XSLTProcessRequest, nsIRequest)
576 NS_IMETHODIMP
577 XSLTProcessRequest::GetName(nsACString& aResult) {
578 aResult.AssignLiteral("about:xslt-load-blocker");
579 return NS_OK;
582 NS_IMETHODIMP
583 XSLTProcessRequest::IsPending(bool* _retval) {
584 *_retval = true;
585 return NS_OK;
588 NS_IMETHODIMP
589 XSLTProcessRequest::GetStatus(nsresult* status) {
590 *status = NS_OK;
591 return NS_OK;
594 NS_IMETHODIMP XSLTProcessRequest::SetCanceledReason(const nsACString& aReason) {
595 return SetCanceledReasonImpl(aReason);
598 NS_IMETHODIMP XSLTProcessRequest::GetCanceledReason(nsACString& aReason) {
599 return GetCanceledReasonImpl(aReason);
602 NS_IMETHODIMP XSLTProcessRequest::CancelWithReason(nsresult aStatus,
603 const nsACString& aReason) {
604 return CancelWithReasonImpl(aStatus, aReason);
607 NS_IMETHODIMP
608 XSLTProcessRequest::Cancel(nsresult status) {
609 mState->stopProcessing();
610 return NS_OK;
613 NS_IMETHODIMP
614 XSLTProcessRequest::Suspend(void) { return NS_OK; }
616 NS_IMETHODIMP
617 XSLTProcessRequest::Resume(void) { return NS_OK; }
619 NS_IMETHODIMP
620 XSLTProcessRequest::GetLoadGroup(nsILoadGroup** aLoadGroup) {
621 *aLoadGroup = nullptr;
622 return NS_OK;
625 NS_IMETHODIMP
626 XSLTProcessRequest::SetLoadGroup(nsILoadGroup* aLoadGroup) { return NS_OK; }
628 NS_IMETHODIMP
629 XSLTProcessRequest::GetLoadFlags(nsLoadFlags* aLoadFlags) {
630 *aLoadFlags = nsIRequest::LOAD_NORMAL;
631 return NS_OK;
634 NS_IMETHODIMP
635 XSLTProcessRequest::SetLoadFlags(nsLoadFlags aLoadFlags) { return NS_OK; }
637 NS_IMETHODIMP
638 XSLTProcessRequest::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
639 return GetTRRModeImpl(aTRRMode);
642 NS_IMETHODIMP
643 XSLTProcessRequest::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
644 return SetTRRModeImpl(aTRRMode);
647 nsresult txMozillaXSLTProcessor::TransformToDoc(Document** aResult,
648 bool aCreateDataDocument) {
649 UniquePtr<txXPathNode> sourceNode(
650 txXPathNativeNode::createXPathNode(mSource));
651 if (!sourceNode) {
652 return NS_ERROR_OUT_OF_MEMORY;
655 txExecutionState es(mStylesheet, IsLoadDisabled());
657 Document* sourceDoc = mSource->OwnerDoc();
658 nsCOMPtr<nsILoadGroup> loadGroup = sourceDoc->GetDocumentLoadGroup();
659 if (!loadGroup) {
660 nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(mOwner);
661 if (win && win->IsCurrentInnerWindow()) {
662 Document* doc = win->GetDoc();
663 if (doc) {
664 loadGroup = doc->GetDocumentLoadGroup();
668 if (!loadGroup) {
669 return NS_ERROR_FAILURE;
673 RefPtr<XSLTProcessRequest> xsltProcessRequest = new XSLTProcessRequest(&es);
674 loadGroup->AddRequest(xsltProcessRequest, nullptr);
676 // XXX Need to add error observers
678 // If aResult is non-null, we're a data document
679 txToDocHandlerFactory handlerFactory(&es, sourceDoc, mObserver,
680 aCreateDataDocument);
681 es.mOutputHandlerFactory = &handlerFactory;
683 nsresult rv = es.init(*sourceNode, &mVariables);
685 // Process root of XML source document
686 if (NS_SUCCEEDED(rv)) {
687 rv = txXSLTProcessor::execute(es);
690 xsltProcessRequest->Done();
691 loadGroup->RemoveRequest(xsltProcessRequest, nullptr, NS_OK);
693 nsresult endRv = es.end(rv);
694 if (NS_SUCCEEDED(rv)) {
695 rv = endRv;
698 if (NS_SUCCEEDED(rv)) {
699 if (aResult) {
700 txAOutputXMLEventHandler* handler =
701 static_cast<txAOutputXMLEventHandler*>(es.mOutputHandler);
702 nsCOMPtr<Document> doc;
703 handler->getOutputDocument(getter_AddRefs(doc));
704 MOZ_ASSERT(doc->GetReadyStateEnum() == Document::READYSTATE_INTERACTIVE,
705 "Bad readyState");
706 doc->SetReadyStateInternal(Document::READYSTATE_COMPLETE);
707 doc.forget(aResult);
709 } else if (mObserver) {
710 // XXX set up context information, bug 204655
711 reportError(rv, nullptr, nullptr);
714 return rv;
717 already_AddRefed<DocumentFragment> txMozillaXSLTProcessor::TransformToFragment(
718 nsINode& aSource, Document& aOutput, ErrorResult& aRv) {
719 if (NS_WARN_IF(NS_FAILED(mCompileResult))) {
720 aRv.Throw(mCompileResult);
721 return nullptr;
724 nsIPrincipal* subject =
725 nsContentUtils::SubjectPrincipalOrSystemIfNativeCaller();
726 if (!subject->Subsumes(aSource.NodePrincipal()) ||
727 !subject->Subsumes(aOutput.NodePrincipal())) {
728 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
729 return nullptr;
732 nsresult rv = ensureStylesheet();
733 if (NS_WARN_IF(NS_FAILED(rv))) {
734 aRv.Throw(rv);
735 return nullptr;
738 UniquePtr<txXPathNode> sourceNode(
739 txXPathNativeNode::createXPathNode(&aSource));
740 if (!sourceNode) {
741 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
742 return nullptr;
745 txExecutionState es(mStylesheet, IsLoadDisabled());
747 // XXX Need to add error observers
749 RefPtr<DocumentFragment> frag = aOutput.CreateDocumentFragment();
750 txToFragmentHandlerFactory handlerFactory(frag);
751 es.mOutputHandlerFactory = &handlerFactory;
753 rv = es.init(*sourceNode, &mVariables);
755 // Process root of XML source document
756 if (NS_SUCCEEDED(rv)) {
757 rv = txXSLTProcessor::execute(es);
759 // XXX setup exception context, bug 204658
760 nsresult endRv = es.end(rv);
761 if (NS_SUCCEEDED(rv)) {
762 rv = endRv;
765 if (NS_FAILED(rv)) {
766 aRv.Throw(rv);
767 return nullptr;
770 return frag.forget();
773 void txMozillaXSLTProcessor::SetParameter(const nsAString& aNamespaceURI,
774 const nsAString& aLocalName,
775 const XSLTParameterValue& aValue,
776 ErrorResult& aError) {
777 if (aValue.IsNode()) {
778 if (!nsContentUtils::CanCallerAccess(&aValue.GetAsNode())) {
779 aError.ThrowSecurityError("Caller is not allowed to access node.");
780 return;
782 } else if (aValue.IsNodeSequence()) {
783 const Sequence<OwningNonNull<nsINode>>& values = aValue.GetAsNodeSequence();
784 for (const auto& node : values) {
785 if (!nsContentUtils::CanCallerAccess(node.get())) {
786 aError.ThrowSecurityError(
787 "Caller is not allowed to access node in sequence.");
788 return;
791 } else if (aValue.IsXPathResult()) {
792 XPathResult& xpathResult = aValue.GetAsXPathResult();
793 RefPtr<txAExprResult> result;
794 aError = xpathResult.GetExprResult(getter_AddRefs(result));
795 if (aError.Failed()) {
796 return;
799 if (result->getResultType() == txAExprResult::NODESET) {
800 txNodeSet* nodeSet =
801 static_cast<txNodeSet*>(static_cast<txAExprResult*>(result));
803 int32_t i, count = nodeSet->size();
804 for (i = 0; i < count; ++i) {
805 nsINode* node = txXPathNativeNode::getNode(nodeSet->get(i));
806 if (!nsContentUtils::CanCallerAccess(node)) {
807 aError.ThrowSecurityError(
808 "Caller is not allowed to access node in node-set.");
809 return;
815 int32_t nsId = kNameSpaceID_Unknown;
816 aError =
817 nsNameSpaceManager::GetInstance()->RegisterNameSpace(aNamespaceURI, nsId);
818 if (aError.Failed()) {
819 return;
822 RefPtr<nsAtom> localName = NS_Atomize(aLocalName);
823 txExpandedName varName(nsId, localName);
825 UniquePtr<OwningXSLTParameterValue> value =
826 txVariable::convertToOwning(aValue, aError);
827 if (aError.Failed()) {
828 return;
831 txVariable* var = static_cast<txVariable*>(mVariables.get(varName));
832 if (var) {
833 var->setValue(std::move(value));
834 return;
837 UniquePtr<txVariable> newVar = MakeUnique<txVariable>(std::move(value));
838 mVariables.add(varName, newVar.release());
841 void txMozillaXSLTProcessor::GetParameter(
842 const nsAString& aNamespaceURI, const nsAString& aLocalName,
843 Nullable<OwningXSLTParameterValue>& aValue, ErrorResult& aRv) {
844 int32_t nsId = kNameSpaceID_Unknown;
845 nsresult rv =
846 nsNameSpaceManager::GetInstance()->RegisterNameSpace(aNamespaceURI, nsId);
847 if (NS_WARN_IF(NS_FAILED(rv))) {
848 aRv.Throw(rv);
849 return;
851 RefPtr<nsAtom> localName = NS_Atomize(aLocalName);
852 txExpandedName varName(nsId, localName);
854 txVariable* var = static_cast<txVariable*>(mVariables.get(varName));
855 if (!var) {
856 return;
859 aValue.SetValue(var->getUnionValue());
862 void txMozillaXSLTProcessor::RemoveParameter(const nsAString& aNamespaceURI,
863 const nsAString& aLocalName,
864 ErrorResult& aRv) {
865 int32_t nsId = kNameSpaceID_Unknown;
866 nsresult rv =
867 nsNameSpaceManager::GetInstance()->RegisterNameSpace(aNamespaceURI, nsId);
868 if (NS_WARN_IF(NS_FAILED(rv))) {
869 aRv.Throw(rv);
870 return;
872 RefPtr<nsAtom> localName = NS_Atomize(aLocalName);
873 txExpandedName varName(nsId, localName);
875 mVariables.remove(varName);
878 void txMozillaXSLTProcessor::ClearParameters() { mVariables.clear(); }
880 void txMozillaXSLTProcessor::Reset() {
881 if (mStylesheetDocument) {
882 mStylesheetDocument->RemoveMutationObserver(this);
884 mStylesheet = nullptr;
885 mStylesheetDocument = nullptr;
886 mEmbeddedStylesheetRoot = nullptr;
887 mCompileResult = NS_OK;
888 mVariables.clear();
891 void txMozillaXSLTProcessor::SetFlags(uint32_t aFlags, SystemCallerGuarantee) {
892 mFlags = aFlags;
895 uint32_t txMozillaXSLTProcessor::Flags(SystemCallerGuarantee) { return mFlags; }
897 NS_IMETHODIMP
898 txMozillaXSLTProcessor::LoadStyleSheet(nsIURI* aUri,
899 Document* aLoaderDocument) {
900 mozilla::dom::ReferrerPolicy refpol = mozilla::dom::ReferrerPolicy::_empty;
901 if (mStylesheetDocument) {
902 refpol = mStylesheetDocument->GetReferrerPolicy();
905 nsresult rv = TX_LoadSheet(aUri, this, aLoaderDocument, refpol);
906 if (NS_FAILED(rv) && mObserver) {
907 // This is most likely a network or security error, just
908 // use the uri as context.
909 nsAutoCString spec;
910 aUri->GetSpec(spec);
911 CopyUTF8toUTF16(spec, mSourceText);
912 nsresult status = NS_ERROR_GET_MODULE(rv) == NS_ERROR_MODULE_XSLT
913 ? rv
914 : NS_ERROR_XSLT_NETWORK_ERROR;
915 reportError(status, nullptr, nullptr);
917 return rv;
920 nsresult txMozillaXSLTProcessor::setStylesheet(txStylesheet* aStylesheet) {
921 mStylesheet = aStylesheet;
922 if (mSource) {
923 return DoTransform();
925 return NS_OK;
928 void txMozillaXSLTProcessor::reportError(nsresult aResult,
929 const char16_t* aErrorText,
930 const char16_t* aSourceText) {
931 if (!mObserver) {
932 return;
935 mTransformResult = aResult;
937 if (aErrorText) {
938 mErrorText.Assign(aErrorText);
939 } else {
940 nsCOMPtr<nsIStringBundleService> sbs =
941 mozilla::components::StringBundle::Service();
942 if (sbs) {
943 nsString errorText;
944 sbs->FormatStatusMessage(aResult, u"", errorText);
946 nsAutoString errorMessage;
947 nsCOMPtr<nsIStringBundle> bundle;
948 sbs->CreateBundle(XSLT_MSGS_URL, getter_AddRefs(bundle));
950 if (bundle) {
951 AutoTArray<nsString, 1> error = {errorText};
952 if (mStylesheet) {
953 bundle->FormatStringFromName("TransformError", error, errorMessage);
954 } else {
955 bundle->FormatStringFromName("LoadingError", error, errorMessage);
958 mErrorText.Assign(errorMessage);
962 if (aSourceText) {
963 mSourceText.Assign(aSourceText);
966 if (mSource) {
967 notifyError();
971 void txMozillaXSLTProcessor::notifyError() {
972 nsCOMPtr<Document> document;
974 nsresult rv = NS_NewXMLDocument(getter_AddRefs(document), nullptr, nullptr);
975 NS_ENSURE_SUCCESS_VOID(rv);
978 URIUtils::ResetWithSource(document, mSource);
980 MOZ_ASSERT(
981 document->GetReadyStateEnum() == Document::READYSTATE_UNINITIALIZED,
982 "Bad readyState.");
983 document->SetReadyStateInternal(Document::READYSTATE_LOADING);
985 constexpr auto ns =
986 u"http://www.mozilla.org/newlayout/xml/parsererror.xml"_ns;
988 IgnoredErrorResult rv;
989 ElementCreationOptionsOrString options;
990 Unused << options.SetAsString();
992 nsCOMPtr<Element> element =
993 document->CreateElementNS(ns, u"parsererror"_ns, options, rv);
994 if (rv.Failed()) {
995 return;
998 document->AppendChild(*element, rv);
999 if (rv.Failed()) {
1000 return;
1003 RefPtr<nsTextNode> text = document->CreateTextNode(mErrorText);
1005 element->AppendChild(*text, rv);
1006 if (rv.Failed()) {
1007 return;
1010 if (!mSourceText.IsEmpty()) {
1011 ElementCreationOptionsOrString options;
1012 Unused << options.SetAsString();
1014 nsCOMPtr<Element> sourceElement =
1015 document->CreateElementNS(ns, u"sourcetext"_ns, options, rv);
1016 if (rv.Failed()) {
1017 return;
1020 element->AppendChild(*sourceElement, rv);
1021 if (rv.Failed()) {
1022 return;
1025 text = document->CreateTextNode(mSourceText);
1027 sourceElement->AppendChild(*text, rv);
1028 if (rv.Failed()) {
1029 return;
1033 MOZ_ASSERT(document->GetReadyStateEnum() == Document::READYSTATE_LOADING,
1034 "Bad readyState.");
1035 document->SetReadyStateInternal(Document::READYSTATE_INTERACTIVE);
1036 mObserver->OnTransformDone(mSource->OwnerDoc(), mTransformResult, document);
1039 nsresult txMozillaXSLTProcessor::ensureStylesheet() {
1040 if (mStylesheet) {
1041 return NS_OK;
1044 NS_ENSURE_TRUE(mStylesheetDocument, NS_ERROR_NOT_INITIALIZED);
1046 nsINode* style = mEmbeddedStylesheetRoot;
1047 if (!style) {
1048 style = mStylesheetDocument;
1051 return TX_CompileStylesheet(style, this, getter_AddRefs(mStylesheet));
1054 void txMozillaXSLTProcessor::NodeWillBeDestroyed(nsINode* aNode) {
1055 nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
1056 if (NS_FAILED(mCompileResult)) {
1057 return;
1060 mCompileResult = ensureStylesheet();
1061 mStylesheetDocument = nullptr;
1062 mEmbeddedStylesheetRoot = nullptr;
1065 void txMozillaXSLTProcessor::CharacterDataChanged(
1066 nsIContent* aContent, const CharacterDataChangeInfo&) {
1067 mStylesheet = nullptr;
1070 void txMozillaXSLTProcessor::AttributeChanged(Element* aElement,
1071 int32_t aNameSpaceID,
1072 nsAtom* aAttribute,
1073 int32_t aModType,
1074 const nsAttrValue* aOldValue) {
1075 mStylesheet = nullptr;
1078 void txMozillaXSLTProcessor::ContentAppended(nsIContent* aFirstNewContent) {
1079 mStylesheet = nullptr;
1082 void txMozillaXSLTProcessor::ContentInserted(nsIContent* aChild) {
1083 mStylesheet = nullptr;
1086 void txMozillaXSLTProcessor::ContentRemoved(nsIContent* aChild,
1087 nsIContent* aPreviousSibling) {
1088 mStylesheet = nullptr;
1091 /* virtual */
1092 JSObject* txMozillaXSLTProcessor::WrapObject(
1093 JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
1094 return XSLTProcessor_Binding::Wrap(aCx, this, aGivenProto);
1097 DocGroup* txMozillaXSLTProcessor::GetDocGroup() const {
1098 return mStylesheetDocument ? mStylesheetDocument->GetDocGroup() : nullptr;
1101 /* static */
1102 already_AddRefed<txMozillaXSLTProcessor> txMozillaXSLTProcessor::Constructor(
1103 const GlobalObject& aGlobal) {
1104 RefPtr<txMozillaXSLTProcessor> processor =
1105 new txMozillaXSLTProcessor(aGlobal.GetAsSupports());
1106 return processor.forget();
1109 /* static*/
1110 nsresult txMozillaXSLTProcessor::Startup() {
1111 if (!txXSLTProcessor::init()) {
1112 return NS_ERROR_OUT_OF_MEMORY;
1115 return NS_OK;
1118 /* static*/
1119 void txMozillaXSLTProcessor::Shutdown() { txXSLTProcessor::shutdown(); }
1121 /* static */
1122 UniquePtr<txVariable::OwningXSLTParameterValue> txVariable::convertToOwning(
1123 const XSLTParameterValue& aValue, ErrorResult& aError) {
1124 UniquePtr<OwningXSLTParameterValue> value =
1125 MakeUnique<OwningXSLTParameterValue>();
1126 if (aValue.IsUnrestrictedDouble()) {
1127 value->SetAsUnrestrictedDouble() = aValue.GetAsUnrestrictedDouble();
1128 } else if (aValue.IsBoolean()) {
1129 value->SetAsBoolean() = aValue.GetAsBoolean();
1130 } else if (aValue.IsString()) {
1131 value->SetAsString() = aValue.GetAsString();
1132 } else if (aValue.IsNode()) {
1133 value->SetAsNode() = aValue.GetAsNode();
1134 } else if (aValue.IsNodeSequence()) {
1135 value->SetAsNodeSequence() = aValue.GetAsNodeSequence();
1136 } else if (aValue.IsXPathResult()) {
1137 // Clone the XPathResult so that mutations don't affect this variable.
1138 RefPtr<XPathResult> clone = aValue.GetAsXPathResult().Clone(aError);
1139 if (aError.Failed()) {
1140 return nullptr;
1142 value->SetAsXPathResult() = *clone;
1143 } else {
1144 MOZ_ASSERT(false, "Unknown type?");
1146 return value;
1149 /* static */
1150 nsresult txVariable::convert(const OwningXSLTParameterValue& aUnionValue,
1151 txAExprResult** aValue) {
1152 if (aUnionValue.IsUnrestrictedDouble()) {
1153 NS_ADDREF(*aValue = new NumberResult(aUnionValue.GetAsUnrestrictedDouble(),
1154 nullptr));
1155 return NS_OK;
1158 if (aUnionValue.IsBoolean()) {
1159 NS_ADDREF(*aValue = new BooleanResult(aUnionValue.GetAsBoolean()));
1160 return NS_OK;
1163 if (aUnionValue.IsString()) {
1164 NS_ADDREF(*aValue = new StringResult(aUnionValue.GetAsString(), nullptr));
1165 return NS_OK;
1168 if (aUnionValue.IsNode()) {
1169 nsINode& node = aUnionValue.GetAsNode();
1170 UniquePtr<txXPathNode> xpathNode(txXPathNativeNode::createXPathNode(&node));
1171 if (!xpathNode) {
1172 return NS_ERROR_FAILURE;
1175 NS_ADDREF(*aValue = new txNodeSet(*xpathNode, nullptr));
1176 return NS_OK;
1179 if (aUnionValue.IsNodeSequence()) {
1180 RefPtr<txNodeSet> nodeSet(new txNodeSet(nullptr));
1181 const Sequence<OwningNonNull<nsINode>>& values =
1182 aUnionValue.GetAsNodeSequence();
1183 for (const auto& node : values) {
1184 UniquePtr<txXPathNode> xpathNode(
1185 txXPathNativeNode::createXPathNode(node.get()));
1186 if (!xpathNode) {
1187 return NS_ERROR_FAILURE;
1190 nodeSet->append(*xpathNode);
1192 nodeSet.forget(aValue);
1193 return NS_OK;
1196 MOZ_ASSERT(aUnionValue.IsXPathResult());
1198 XPathResult& xpathResult = aUnionValue.GetAsXPathResult();
1199 if (xpathResult.ResultType() == XPathResult::NUMBER_TYPE) {
1200 IgnoredErrorResult rv;
1201 NS_ADDREF(*aValue =
1202 new NumberResult(xpathResult.GetNumberValue(rv), nullptr));
1203 MOZ_ASSERT(!rv.Failed());
1204 return NS_OK;
1207 if (xpathResult.ResultType() == XPathResult::BOOLEAN_TYPE) {
1208 IgnoredErrorResult rv;
1209 NS_ADDREF(*aValue = new BooleanResult(xpathResult.GetBooleanValue(rv)));
1210 MOZ_ASSERT(!rv.Failed());
1211 return NS_OK;
1214 if (xpathResult.ResultType() == XPathResult::STRING_TYPE) {
1215 IgnoredErrorResult rv;
1216 nsString value;
1217 xpathResult.GetStringValue(value, rv);
1218 NS_ADDREF(*aValue = new StringResult(value, nullptr));
1219 MOZ_ASSERT(!rv.Failed());
1220 return NS_OK;
1223 // If the XPathResult holds a nodeset, then it will keep the nodes alive and
1224 // we'll hold the XPathResult alive.
1225 return xpathResult.GetExprResult(aValue);