summaryrefslogtreecommitdiffstats
path: root/dom/xslt/xslt/txMozillaXSLTProcessor.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/xslt/xslt/txMozillaXSLTProcessor.cpp')
-rw-r--r--dom/xslt/xslt/txMozillaXSLTProcessor.cpp1227
1 files changed, 1227 insertions, 0 deletions
diff --git a/dom/xslt/xslt/txMozillaXSLTProcessor.cpp b/dom/xslt/xslt/txMozillaXSLTProcessor.cpp
new file mode 100644
index 0000000000..f3d4272e6f
--- /dev/null
+++ b/dom/xslt/xslt/txMozillaXSLTProcessor.cpp
@@ -0,0 +1,1227 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "txMozillaXSLTProcessor.h"
+#include "nsContentCID.h"
+#include "nsError.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/Document.h"
+#include "nsIStringBundle.h"
+#include "nsIURI.h"
+#include "XPathResult.h"
+#include "txExecutionState.h"
+#include "txMozillaTextOutput.h"
+#include "txMozillaXMLOutput.h"
+#include "txURIUtils.h"
+#include "txXMLUtils.h"
+#include "txUnknownHandler.h"
+#include "txXSLTMsgsURL.h"
+#include "txXSLTProcessor.h"
+#include "nsIPrincipal.h"
+#include "nsThreadUtils.h"
+#include "jsapi.h"
+#include "txExprParser.h"
+#include "nsJSUtils.h"
+#include "nsIXPConnect.h"
+#include "nsNameSpaceManager.h"
+#include "nsVariant.h"
+#include "nsTextNode.h"
+#include "mozilla/Components.h"
+#include "mozilla/dom/DocumentFragment.h"
+#include "mozilla/dom/XSLTProcessorBinding.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+/**
+ * Output Handler Factories
+ */
+class txToDocHandlerFactory : public txAOutputHandlerFactory {
+ public:
+ txToDocHandlerFactory(txExecutionState* aEs, Document* aSourceDocument,
+ nsITransformObserver* aObserver, bool aDocumentIsData)
+ : mEs(aEs),
+ mSourceDocument(aSourceDocument),
+ mObserver(aObserver),
+ mDocumentIsData(aDocumentIsData) {}
+
+ TX_DECL_TXAOUTPUTHANDLERFACTORY
+
+ private:
+ txExecutionState* mEs;
+ nsCOMPtr<Document> mSourceDocument;
+ nsCOMPtr<nsITransformObserver> mObserver;
+ bool mDocumentIsData;
+};
+
+class txToFragmentHandlerFactory : public txAOutputHandlerFactory {
+ public:
+ explicit txToFragmentHandlerFactory(DocumentFragment* aFragment)
+ : mFragment(aFragment) {}
+
+ TX_DECL_TXAOUTPUTHANDLERFACTORY
+
+ private:
+ RefPtr<DocumentFragment> mFragment;
+};
+
+nsresult txToDocHandlerFactory::createHandlerWith(
+ txOutputFormat* aFormat, txAXMLEventHandler** aHandler) {
+ *aHandler = nullptr;
+ switch (aFormat->mMethod) {
+ case eMethodNotSet:
+ case eXMLOutput: {
+ *aHandler = new txUnknownHandler(mEs);
+ return NS_OK;
+ }
+
+ case eHTMLOutput: {
+ UniquePtr<txMozillaXMLOutput> handler(
+ new txMozillaXMLOutput(mSourceDocument, aFormat, mObserver));
+
+ nsresult rv = handler->createResultDocument(
+ u""_ns, kNameSpaceID_None, mSourceDocument, mDocumentIsData);
+ if (NS_SUCCEEDED(rv)) {
+ *aHandler = handler.release();
+ }
+
+ return rv;
+ }
+
+ case eTextOutput: {
+ UniquePtr<txMozillaTextOutput> handler(
+ new txMozillaTextOutput(mSourceDocument, mObserver));
+
+ nsresult rv = handler->createResultDocument(mDocumentIsData);
+ if (NS_SUCCEEDED(rv)) {
+ *aHandler = handler.release();
+ }
+
+ return rv;
+ }
+ }
+
+ MOZ_CRASH("Unknown output method");
+
+ return NS_ERROR_FAILURE;
+}
+
+nsresult txToDocHandlerFactory::createHandlerWith(
+ txOutputFormat* aFormat, const nsAString& aName, int32_t aNsID,
+ txAXMLEventHandler** aHandler) {
+ *aHandler = nullptr;
+ switch (aFormat->mMethod) {
+ case eMethodNotSet: {
+ NS_ERROR("How can method not be known when root element is?");
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ case eXMLOutput:
+ case eHTMLOutput: {
+ UniquePtr<txMozillaXMLOutput> handler(
+ new txMozillaXMLOutput(mSourceDocument, aFormat, mObserver));
+
+ nsresult rv = handler->createResultDocument(aName, aNsID, mSourceDocument,
+ mDocumentIsData);
+ if (NS_SUCCEEDED(rv)) {
+ *aHandler = handler.release();
+ }
+
+ return rv;
+ }
+
+ case eTextOutput: {
+ UniquePtr<txMozillaTextOutput> handler(
+ new txMozillaTextOutput(mSourceDocument, mObserver));
+
+ nsresult rv = handler->createResultDocument(mDocumentIsData);
+ if (NS_SUCCEEDED(rv)) {
+ *aHandler = handler.release();
+ }
+
+ return rv;
+ }
+ }
+
+ MOZ_CRASH("Unknown output method");
+
+ return NS_ERROR_FAILURE;
+}
+
+nsresult txToFragmentHandlerFactory::createHandlerWith(
+ txOutputFormat* aFormat, txAXMLEventHandler** aHandler) {
+ *aHandler = nullptr;
+ switch (aFormat->mMethod) {
+ case eMethodNotSet: {
+ txOutputFormat format;
+ format.merge(*aFormat);
+ nsCOMPtr<Document> doc = mFragment->OwnerDoc();
+
+ if (doc->IsHTMLDocument()) {
+ format.mMethod = eHTMLOutput;
+ } else {
+ format.mMethod = eXMLOutput;
+ }
+
+ *aHandler = new txMozillaXMLOutput(&format, mFragment, false);
+ break;
+ }
+
+ case eXMLOutput:
+ case eHTMLOutput: {
+ *aHandler = new txMozillaXMLOutput(aFormat, mFragment, false);
+ break;
+ }
+
+ case eTextOutput: {
+ *aHandler = new txMozillaTextOutput(mFragment);
+ break;
+ }
+ }
+ return NS_OK;
+}
+
+nsresult txToFragmentHandlerFactory::createHandlerWith(
+ txOutputFormat* aFormat, const nsAString& aName, int32_t aNsID,
+ txAXMLEventHandler** aHandler) {
+ *aHandler = nullptr;
+ NS_ASSERTION(aFormat->mMethod != eMethodNotSet,
+ "How can method not be known when root element is?");
+ NS_ENSURE_TRUE(aFormat->mMethod != eMethodNotSet, NS_ERROR_UNEXPECTED);
+ return createHandlerWith(aFormat, aHandler);
+}
+
+class txVariable : public txIGlobalParameter {
+ using XSLTParameterValue = txMozillaXSLTProcessor::XSLTParameterValue;
+ using OwningXSLTParameterValue =
+ txMozillaXSLTProcessor::OwningXSLTParameterValue;
+
+ public:
+ explicit txVariable(UniquePtr<OwningXSLTParameterValue>&& aValue)
+ : mUnionValue(std::move(aValue)) {}
+ nsresult getValue(txAExprResult** aValue) override {
+ if (!mValue) {
+ nsresult rv = convert(*mUnionValue, getter_AddRefs(mValue));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ NS_ADDREF(*aValue = mValue);
+
+ return NS_OK;
+ }
+ OwningXSLTParameterValue getUnionValue() {
+ return OwningXSLTParameterValue(*mUnionValue);
+ }
+ void setValue(UniquePtr<OwningXSLTParameterValue>&& aValue) {
+ mValue = nullptr;
+ mUnionValue = std::move(aValue);
+ }
+
+ static UniquePtr<OwningXSLTParameterValue> convertToOwning(
+ const XSLTParameterValue& aValue, ErrorResult& aError);
+
+ friend void ImplCycleCollectionTraverse(
+ nsCycleCollectionTraversalCallback& aCallback, txVariable& aVariable,
+ const char* aName, uint32_t aFlags);
+
+ private:
+ static nsresult convert(const OwningXSLTParameterValue& aUnionValue,
+ txAExprResult** aValue);
+
+ UniquePtr<OwningXSLTParameterValue> mUnionValue;
+ RefPtr<txAExprResult> mValue;
+};
+
+inline void ImplCycleCollectionTraverse(
+ nsCycleCollectionTraversalCallback& aCallback, txVariable& aVariable,
+ const char* aName, uint32_t aFlags) {
+ ImplCycleCollectionTraverse(aCallback, *aVariable.mUnionValue, aName, aFlags);
+}
+
+inline void ImplCycleCollectionUnlink(
+ txOwningExpandedNameMap<txIGlobalParameter>& aMap) {
+ aMap.clear();
+}
+
+inline void ImplCycleCollectionTraverse(
+ nsCycleCollectionTraversalCallback& aCallback,
+ txOwningExpandedNameMap<txIGlobalParameter>& aMap, const char* aName,
+ uint32_t aFlags = 0) {
+ aFlags |= CycleCollectionEdgeNameArrayFlag;
+ txOwningExpandedNameMap<txIGlobalParameter>::iterator iter(aMap);
+ while (iter.next()) {
+ ImplCycleCollectionTraverse(
+ aCallback, *static_cast<txVariable*>(iter.value()), aName, aFlags);
+ }
+}
+
+/**
+ * txMozillaXSLTProcessor
+ */
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(txMozillaXSLTProcessor, mOwner,
+ mEmbeddedStylesheetRoot, mSource,
+ mVariables)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(txMozillaXSLTProcessor)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(txMozillaXSLTProcessor)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(txMozillaXSLTProcessor)
+ NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+ NS_INTERFACE_MAP_ENTRY(nsIDocumentTransformer)
+ NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDocumentTransformer)
+NS_INTERFACE_MAP_END
+
+txMozillaXSLTProcessor::txMozillaXSLTProcessor()
+ : mOwner(nullptr),
+ mStylesheetDocument(nullptr),
+ mTransformResult(NS_OK),
+ mCompileResult(NS_OK),
+ mFlags(0) {}
+
+txMozillaXSLTProcessor::txMozillaXSLTProcessor(nsISupports* aOwner)
+ : mOwner(aOwner),
+ mStylesheetDocument(nullptr),
+ mTransformResult(NS_OK),
+ mCompileResult(NS_OK),
+ mFlags(0) {}
+
+txMozillaXSLTProcessor::~txMozillaXSLTProcessor() {
+ if (mStylesheetDocument) {
+ mStylesheetDocument->RemoveMutationObserver(this);
+ }
+}
+
+NS_IMETHODIMP
+txMozillaXSLTProcessor::SetTransformObserver(nsITransformObserver* aObserver) {
+ mObserver = aObserver;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+txMozillaXSLTProcessor::SetSourceContentModel(nsINode* aSource) {
+ mSource = aSource;
+
+ if (NS_FAILED(mTransformResult)) {
+ notifyError();
+ return NS_OK;
+ }
+
+ if (mStylesheet) {
+ return DoTransform();
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+txMozillaXSLTProcessor::AddXSLTParamNamespace(const nsString& aPrefix,
+ const nsString& aNamespace) {
+ RefPtr<nsAtom> pre = NS_Atomize(aPrefix);
+ return mParamNamespaceMap.mapNamespace(pre, aNamespace);
+}
+
+class txXSLTParamContext : public txIParseContext, public txIEvalContext {
+ public:
+ txXSLTParamContext(txNamespaceMap* aResolver, const txXPathNode& aContext,
+ txResultRecycler* aRecycler)
+ : mResolver(aResolver), mContext(aContext), mRecycler(aRecycler) {}
+
+ // txIParseContext
+ nsresult resolveNamespacePrefix(nsAtom* aPrefix, int32_t& aID) override {
+ aID = mResolver->lookupNamespace(aPrefix);
+ return aID == kNameSpaceID_Unknown ? NS_ERROR_DOM_NAMESPACE_ERR : NS_OK;
+ }
+ nsresult resolveFunctionCall(nsAtom* aName, int32_t aID,
+ FunctionCall** aFunction) override {
+ return NS_ERROR_XPATH_UNKNOWN_FUNCTION;
+ }
+ bool caseInsensitiveNameTests() override { return false; }
+ void SetErrorOffset(uint32_t aOffset) override {}
+
+ // txIEvalContext
+ nsresult getVariable(int32_t aNamespace, nsAtom* aLName,
+ txAExprResult*& aResult) override {
+ aResult = nullptr;
+ return NS_ERROR_INVALID_ARG;
+ }
+ nsresult isStripSpaceAllowed(const txXPathNode& aNode,
+ bool& aAllowed) override {
+ aAllowed = false;
+
+ return NS_OK;
+ }
+ void* getPrivateContext() override { return nullptr; }
+ txResultRecycler* recycler() override { return mRecycler; }
+ void receiveError(const nsAString& aMsg, nsresult aRes) override {}
+ const txXPathNode& getContextNode() override { return mContext; }
+ uint32_t size() override { return 1; }
+ uint32_t position() override { return 1; }
+
+ private:
+ txNamespaceMap* mResolver;
+ const txXPathNode& mContext;
+ txResultRecycler* mRecycler;
+};
+
+NS_IMETHODIMP
+txMozillaXSLTProcessor::AddXSLTParam(const nsString& aName,
+ const nsString& aNamespace,
+ const nsString& aSelect,
+ const nsString& aValue,
+ nsINode* aContext) {
+ nsresult rv = NS_OK;
+
+ if (aSelect.IsVoid() == aValue.IsVoid()) {
+ // Ignore if neither or both are specified
+ return NS_ERROR_FAILURE;
+ }
+
+ RefPtr<txAExprResult> value;
+ uint16_t resultType;
+ if (!aSelect.IsVoid()) {
+ // Set up context
+ UniquePtr<txXPathNode> contextNode(
+ txXPathNativeNode::createXPathNode(aContext));
+ NS_ENSURE_TRUE(contextNode, NS_ERROR_OUT_OF_MEMORY);
+
+ if (!mRecycler) {
+ mRecycler = new txResultRecycler;
+ }
+
+ txXSLTParamContext paramContext(&mParamNamespaceMap, *contextNode,
+ mRecycler);
+
+ // Parse
+ UniquePtr<Expr> expr;
+ rv = txExprParser::createExpr(aSelect, &paramContext,
+ getter_Transfers(expr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Evaluate
+ rv = expr->evaluate(&paramContext, getter_AddRefs(value));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ switch (value->getResultType()) {
+ case txAExprResult::NUMBER:
+ resultType = XPathResult::NUMBER_TYPE;
+ break;
+ case txAExprResult::STRING:
+ resultType = XPathResult::STRING_TYPE;
+ break;
+ case txAExprResult::BOOLEAN:
+ resultType = XPathResult::BOOLEAN_TYPE;
+ break;
+ case txAExprResult::NODESET:
+ resultType = XPathResult::UNORDERED_NODE_ITERATOR_TYPE;
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE(
+ "We shouldn't have a txAExprResult::RESULT_TREE_FRAGMENT here.");
+ return NS_ERROR_FAILURE;
+ }
+ } else {
+ value = new StringResult(aValue, nullptr);
+ resultType = XPathResult::STRING_TYPE;
+ }
+
+ RefPtr<nsAtom> name = NS_Atomize(aName);
+ int32_t nsId = kNameSpaceID_Unknown;
+ rv = nsNameSpaceManager::GetInstance()->RegisterNameSpace(aNamespace, nsId);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ RefPtr<XPathResult> xpathResult = MakeRefPtr<XPathResult>(aContext);
+
+ ErrorResult error;
+ xpathResult->SetExprResult(value, resultType, aContext, error);
+ if (error.Failed()) {
+ return error.StealNSResult();
+ }
+
+ UniquePtr<OwningXSLTParameterValue> varValue =
+ MakeUnique<OwningXSLTParameterValue>();
+ varValue->SetAsXPathResult() = xpathResult.forget();
+
+ txExpandedName varName(nsId, name);
+ txVariable* var = static_cast<txVariable*>(mVariables.get(varName));
+ if (var) {
+ var->setValue(std::move(varValue));
+
+ return NS_OK;
+ }
+
+ var = new txVariable(std::move(varValue));
+
+ return mVariables.add(varName, var);
+}
+
+class nsTransformBlockerEvent : public mozilla::Runnable {
+ public:
+ RefPtr<txMozillaXSLTProcessor> mProcessor;
+
+ explicit nsTransformBlockerEvent(txMozillaXSLTProcessor* processor)
+ : mozilla::Runnable("nsTransformBlockerEvent"), mProcessor(processor) {}
+
+ ~nsTransformBlockerEvent() {
+ nsCOMPtr<Document> document =
+ mProcessor->GetSourceContentModel()->OwnerDoc();
+ document->UnblockOnload(true);
+ }
+
+ NS_IMETHOD Run() override {
+ mProcessor->TransformToDoc(nullptr, false);
+ return NS_OK;
+ }
+};
+
+nsresult txMozillaXSLTProcessor::DoTransform() {
+ NS_ENSURE_TRUE(mSource, NS_ERROR_UNEXPECTED);
+ NS_ENSURE_TRUE(mStylesheet, NS_ERROR_UNEXPECTED);
+ NS_ASSERTION(mObserver, "no observer");
+ NS_ASSERTION(NS_IsMainThread(), "should only be on main thread");
+
+ nsCOMPtr<nsIRunnable> event = new nsTransformBlockerEvent(this);
+ mSource->OwnerDoc()->BlockOnload();
+ nsresult rv = NS_DispatchToCurrentThread(event);
+ if (NS_FAILED(rv)) {
+ // XXX Maybe we should just display the source document in this case?
+ // Also, set up context information, see bug 204655.
+ reportError(rv, nullptr, nullptr);
+ }
+
+ return rv;
+}
+
+void txMozillaXSLTProcessor::ImportStylesheet(nsINode& aStyle,
+ mozilla::ErrorResult& aRv) {
+ // We don't support importing multiple stylesheets yet.
+ if (NS_WARN_IF(mStylesheetDocument || mStylesheet)) {
+ aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
+ return;
+ }
+
+ if (!nsContentUtils::SubjectPrincipalOrSystemIfNativeCaller()->Subsumes(
+ aStyle.NodePrincipal())) {
+ aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
+ return;
+ }
+
+ if (NS_WARN_IF(!aStyle.IsElement() && !aStyle.IsDocument())) {
+ aRv.Throw(NS_ERROR_INVALID_ARG);
+ return;
+ }
+
+ nsresult rv =
+ TX_CompileStylesheet(&aStyle, this, getter_AddRefs(mStylesheet));
+ // XXX set up exception context, bug 204658
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ aRv.Throw(rv);
+ return;
+ }
+
+ mStylesheetDocument = aStyle.OwnerDoc();
+ if (aStyle.IsElement()) {
+ mEmbeddedStylesheetRoot = aStyle.AsElement();
+ }
+
+ mStylesheetDocument->AddMutationObserver(this);
+}
+
+already_AddRefed<Document> txMozillaXSLTProcessor::TransformToDocument(
+ nsINode& aSource, ErrorResult& aRv) {
+ if (NS_WARN_IF(NS_FAILED(mCompileResult))) {
+ aRv.Throw(mCompileResult);
+ return nullptr;
+ }
+
+ if (!nsContentUtils::CanCallerAccess(&aSource)) {
+ aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
+ return nullptr;
+ }
+
+ nsresult rv = ensureStylesheet();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ aRv.Throw(rv);
+ return nullptr;
+ }
+
+ mSource = &aSource;
+
+ nsCOMPtr<Document> doc;
+ rv = TransformToDoc(getter_AddRefs(doc), true);
+ if (NS_FAILED(rv)) {
+ aRv.Throw(rv);
+ return nullptr;
+ }
+ return doc.forget();
+}
+
+class XSLTProcessRequest final : public nsIRequest {
+ public:
+ explicit XSLTProcessRequest(txExecutionState* aState) : mState(aState) {}
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIREQUEST
+
+ void Done() { mState = nullptr; }
+
+ private:
+ ~XSLTProcessRequest() {}
+ txExecutionState* mState;
+};
+NS_IMPL_ISUPPORTS(XSLTProcessRequest, nsIRequest)
+
+NS_IMETHODIMP
+XSLTProcessRequest::GetName(nsACString& aResult) {
+ aResult.AssignLiteral("about:xslt-load-blocker");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+XSLTProcessRequest::IsPending(bool* _retval) {
+ *_retval = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+XSLTProcessRequest::GetStatus(nsresult* status) {
+ *status = NS_OK;
+ return NS_OK;
+}
+
+NS_IMETHODIMP XSLTProcessRequest::SetCanceledReason(const nsACString& aReason) {
+ return SetCanceledReasonImpl(aReason);
+}
+
+NS_IMETHODIMP XSLTProcessRequest::GetCanceledReason(nsACString& aReason) {
+ return GetCanceledReasonImpl(aReason);
+}
+
+NS_IMETHODIMP XSLTProcessRequest::CancelWithReason(nsresult aStatus,
+ const nsACString& aReason) {
+ return CancelWithReasonImpl(aStatus, aReason);
+}
+
+NS_IMETHODIMP
+XSLTProcessRequest::Cancel(nsresult status) {
+ mState->stopProcessing();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+XSLTProcessRequest::Suspend(void) { return NS_OK; }
+
+NS_IMETHODIMP
+XSLTProcessRequest::Resume(void) { return NS_OK; }
+
+NS_IMETHODIMP
+XSLTProcessRequest::GetLoadGroup(nsILoadGroup** aLoadGroup) {
+ *aLoadGroup = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+XSLTProcessRequest::SetLoadGroup(nsILoadGroup* aLoadGroup) { return NS_OK; }
+
+NS_IMETHODIMP
+XSLTProcessRequest::GetLoadFlags(nsLoadFlags* aLoadFlags) {
+ *aLoadFlags = nsIRequest::LOAD_NORMAL;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+XSLTProcessRequest::SetLoadFlags(nsLoadFlags aLoadFlags) { return NS_OK; }
+
+NS_IMETHODIMP
+XSLTProcessRequest::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
+ return GetTRRModeImpl(aTRRMode);
+}
+
+NS_IMETHODIMP
+XSLTProcessRequest::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
+ return SetTRRModeImpl(aTRRMode);
+}
+
+nsresult txMozillaXSLTProcessor::TransformToDoc(Document** aResult,
+ bool aCreateDataDocument) {
+ UniquePtr<txXPathNode> sourceNode(
+ txXPathNativeNode::createXPathNode(mSource));
+ if (!sourceNode) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ txExecutionState es(mStylesheet, IsLoadDisabled());
+
+ Document* sourceDoc = mSource->OwnerDoc();
+ nsCOMPtr<nsILoadGroup> loadGroup = sourceDoc->GetDocumentLoadGroup();
+ if (!loadGroup) {
+ nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(mOwner);
+ if (win && win->IsCurrentInnerWindow()) {
+ Document* doc = win->GetDoc();
+ if (doc) {
+ loadGroup = doc->GetDocumentLoadGroup();
+ }
+ }
+
+ if (!loadGroup) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ RefPtr<XSLTProcessRequest> xsltProcessRequest = new XSLTProcessRequest(&es);
+ loadGroup->AddRequest(xsltProcessRequest, nullptr);
+
+ // XXX Need to add error observers
+
+ // If aResult is non-null, we're a data document
+ txToDocHandlerFactory handlerFactory(&es, sourceDoc, mObserver,
+ aCreateDataDocument);
+ es.mOutputHandlerFactory = &handlerFactory;
+
+ nsresult rv = es.init(*sourceNode, &mVariables);
+
+ // Process root of XML source document
+ if (NS_SUCCEEDED(rv)) {
+ rv = txXSLTProcessor::execute(es);
+ }
+
+ xsltProcessRequest->Done();
+ loadGroup->RemoveRequest(xsltProcessRequest, nullptr, NS_OK);
+
+ nsresult endRv = es.end(rv);
+ if (NS_SUCCEEDED(rv)) {
+ rv = endRv;
+ }
+
+ if (NS_SUCCEEDED(rv)) {
+ if (aResult) {
+ txAOutputXMLEventHandler* handler =
+ static_cast<txAOutputXMLEventHandler*>(es.mOutputHandler);
+ nsCOMPtr<Document> doc;
+ handler->getOutputDocument(getter_AddRefs(doc));
+ MOZ_ASSERT(doc->GetReadyStateEnum() == Document::READYSTATE_INTERACTIVE,
+ "Bad readyState");
+ doc->SetReadyStateInternal(Document::READYSTATE_COMPLETE);
+ doc.forget(aResult);
+ }
+ } else if (mObserver) {
+ // XXX set up context information, bug 204655
+ reportError(rv, nullptr, nullptr);
+ }
+
+ return rv;
+}
+
+already_AddRefed<DocumentFragment> txMozillaXSLTProcessor::TransformToFragment(
+ nsINode& aSource, Document& aOutput, ErrorResult& aRv) {
+ if (NS_WARN_IF(NS_FAILED(mCompileResult))) {
+ aRv.Throw(mCompileResult);
+ return nullptr;
+ }
+
+ nsIPrincipal* subject =
+ nsContentUtils::SubjectPrincipalOrSystemIfNativeCaller();
+ if (!subject->Subsumes(aSource.NodePrincipal()) ||
+ !subject->Subsumes(aOutput.NodePrincipal())) {
+ aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
+ return nullptr;
+ }
+
+ nsresult rv = ensureStylesheet();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ aRv.Throw(rv);
+ return nullptr;
+ }
+
+ UniquePtr<txXPathNode> sourceNode(
+ txXPathNativeNode::createXPathNode(&aSource));
+ if (!sourceNode) {
+ aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
+ return nullptr;
+ }
+
+ txExecutionState es(mStylesheet, IsLoadDisabled());
+
+ // XXX Need to add error observers
+
+ RefPtr<DocumentFragment> frag = aOutput.CreateDocumentFragment();
+ txToFragmentHandlerFactory handlerFactory(frag);
+ es.mOutputHandlerFactory = &handlerFactory;
+
+ rv = es.init(*sourceNode, &mVariables);
+
+ // Process root of XML source document
+ if (NS_SUCCEEDED(rv)) {
+ rv = txXSLTProcessor::execute(es);
+ }
+ // XXX setup exception context, bug 204658
+ nsresult endRv = es.end(rv);
+ if (NS_SUCCEEDED(rv)) {
+ rv = endRv;
+ }
+
+ if (NS_FAILED(rv)) {
+ aRv.Throw(rv);
+ return nullptr;
+ }
+
+ return frag.forget();
+}
+
+void txMozillaXSLTProcessor::SetParameter(const nsAString& aNamespaceURI,
+ const nsAString& aLocalName,
+ const XSLTParameterValue& aValue,
+ ErrorResult& aError) {
+ if (aValue.IsNode()) {
+ if (!nsContentUtils::CanCallerAccess(&aValue.GetAsNode())) {
+ aError.ThrowSecurityError("Caller is not allowed to access node.");
+ return;
+ }
+ } else if (aValue.IsNodeSequence()) {
+ const Sequence<OwningNonNull<nsINode>>& values = aValue.GetAsNodeSequence();
+ for (const auto& node : values) {
+ if (!nsContentUtils::CanCallerAccess(node.get())) {
+ aError.ThrowSecurityError(
+ "Caller is not allowed to access node in sequence.");
+ return;
+ }
+ }
+ } else if (aValue.IsXPathResult()) {
+ XPathResult& xpathResult = aValue.GetAsXPathResult();
+ RefPtr<txAExprResult> result;
+ aError = xpathResult.GetExprResult(getter_AddRefs(result));
+ if (aError.Failed()) {
+ return;
+ }
+
+ if (result->getResultType() == txAExprResult::NODESET) {
+ txNodeSet* nodeSet =
+ static_cast<txNodeSet*>(static_cast<txAExprResult*>(result));
+
+ int32_t i, count = nodeSet->size();
+ for (i = 0; i < count; ++i) {
+ nsINode* node = txXPathNativeNode::getNode(nodeSet->get(i));
+ if (!nsContentUtils::CanCallerAccess(node)) {
+ aError.ThrowSecurityError(
+ "Caller is not allowed to access node in node-set.");
+ return;
+ }
+ }
+ }
+ }
+
+ int32_t nsId = kNameSpaceID_Unknown;
+ aError =
+ nsNameSpaceManager::GetInstance()->RegisterNameSpace(aNamespaceURI, nsId);
+ if (aError.Failed()) {
+ return;
+ }
+
+ RefPtr<nsAtom> localName = NS_Atomize(aLocalName);
+ txExpandedName varName(nsId, localName);
+
+ UniquePtr<OwningXSLTParameterValue> value =
+ txVariable::convertToOwning(aValue, aError);
+ if (aError.Failed()) {
+ return;
+ }
+
+ txVariable* var = static_cast<txVariable*>(mVariables.get(varName));
+ if (var) {
+ var->setValue(std::move(value));
+ return;
+ }
+
+ UniquePtr<txVariable> newVar = MakeUnique<txVariable>(std::move(value));
+ mVariables.add(varName, newVar.release());
+}
+
+void txMozillaXSLTProcessor::GetParameter(
+ const nsAString& aNamespaceURI, const nsAString& aLocalName,
+ Nullable<OwningXSLTParameterValue>& aValue, ErrorResult& aRv) {
+ int32_t nsId = kNameSpaceID_Unknown;
+ nsresult rv =
+ nsNameSpaceManager::GetInstance()->RegisterNameSpace(aNamespaceURI, nsId);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ aRv.Throw(rv);
+ return;
+ }
+ RefPtr<nsAtom> localName = NS_Atomize(aLocalName);
+ txExpandedName varName(nsId, localName);
+
+ txVariable* var = static_cast<txVariable*>(mVariables.get(varName));
+ if (!var) {
+ return;
+ }
+
+ aValue.SetValue(var->getUnionValue());
+}
+
+void txMozillaXSLTProcessor::RemoveParameter(const nsAString& aNamespaceURI,
+ const nsAString& aLocalName,
+ ErrorResult& aRv) {
+ int32_t nsId = kNameSpaceID_Unknown;
+ nsresult rv =
+ nsNameSpaceManager::GetInstance()->RegisterNameSpace(aNamespaceURI, nsId);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ aRv.Throw(rv);
+ return;
+ }
+ RefPtr<nsAtom> localName = NS_Atomize(aLocalName);
+ txExpandedName varName(nsId, localName);
+
+ mVariables.remove(varName);
+}
+
+void txMozillaXSLTProcessor::ClearParameters() { mVariables.clear(); }
+
+void txMozillaXSLTProcessor::Reset() {
+ if (mStylesheetDocument) {
+ mStylesheetDocument->RemoveMutationObserver(this);
+ }
+ mStylesheet = nullptr;
+ mStylesheetDocument = nullptr;
+ mEmbeddedStylesheetRoot = nullptr;
+ mCompileResult = NS_OK;
+ mVariables.clear();
+}
+
+void txMozillaXSLTProcessor::SetFlags(uint32_t aFlags, SystemCallerGuarantee) {
+ mFlags = aFlags;
+}
+
+uint32_t txMozillaXSLTProcessor::Flags(SystemCallerGuarantee) { return mFlags; }
+
+NS_IMETHODIMP
+txMozillaXSLTProcessor::LoadStyleSheet(nsIURI* aUri,
+ Document* aLoaderDocument) {
+ mozilla::dom::ReferrerPolicy refpol = mozilla::dom::ReferrerPolicy::_empty;
+ if (mStylesheetDocument) {
+ refpol = mStylesheetDocument->GetReferrerPolicy();
+ }
+
+ nsresult rv = TX_LoadSheet(aUri, this, aLoaderDocument, refpol);
+ if (NS_FAILED(rv) && mObserver) {
+ // This is most likely a network or security error, just
+ // use the uri as context.
+ nsAutoCString spec;
+ aUri->GetSpec(spec);
+ CopyUTF8toUTF16(spec, mSourceText);
+ nsresult status = NS_ERROR_GET_MODULE(rv) == NS_ERROR_MODULE_XSLT
+ ? rv
+ : NS_ERROR_XSLT_NETWORK_ERROR;
+ reportError(status, nullptr, nullptr);
+ }
+ return rv;
+}
+
+nsresult txMozillaXSLTProcessor::setStylesheet(txStylesheet* aStylesheet) {
+ mStylesheet = aStylesheet;
+ if (mSource) {
+ return DoTransform();
+ }
+ return NS_OK;
+}
+
+void txMozillaXSLTProcessor::reportError(nsresult aResult,
+ const char16_t* aErrorText,
+ const char16_t* aSourceText) {
+ if (!mObserver) {
+ return;
+ }
+
+ mTransformResult = aResult;
+
+ if (aErrorText) {
+ mErrorText.Assign(aErrorText);
+ } else {
+ nsCOMPtr<nsIStringBundleService> sbs =
+ mozilla::components::StringBundle::Service();
+ if (sbs) {
+ nsString errorText;
+ sbs->FormatStatusMessage(aResult, u"", errorText);
+
+ nsAutoString errorMessage;
+ nsCOMPtr<nsIStringBundle> bundle;
+ sbs->CreateBundle(XSLT_MSGS_URL, getter_AddRefs(bundle));
+
+ if (bundle) {
+ AutoTArray<nsString, 1> error = {errorText};
+ if (mStylesheet) {
+ bundle->FormatStringFromName("TransformError", error, errorMessage);
+ } else {
+ bundle->FormatStringFromName("LoadingError", error, errorMessage);
+ }
+ }
+ mErrorText.Assign(errorMessage);
+ }
+ }
+
+ if (aSourceText) {
+ mSourceText.Assign(aSourceText);
+ }
+
+ if (mSource) {
+ notifyError();
+ }
+}
+
+void txMozillaXSLTProcessor::notifyError() {
+ nsCOMPtr<Document> document;
+ {
+ nsresult rv = NS_NewXMLDocument(getter_AddRefs(document));
+ NS_ENSURE_SUCCESS_VOID(rv);
+ }
+
+ URIUtils::ResetWithSource(document, mSource);
+
+ MOZ_ASSERT(
+ document->GetReadyStateEnum() == Document::READYSTATE_UNINITIALIZED,
+ "Bad readyState.");
+ document->SetReadyStateInternal(Document::READYSTATE_LOADING);
+
+ constexpr auto ns =
+ u"http://www.mozilla.org/newlayout/xml/parsererror.xml"_ns;
+
+ IgnoredErrorResult rv;
+ ElementCreationOptionsOrString options;
+ options.SetAsString();
+
+ nsCOMPtr<Element> element =
+ document->CreateElementNS(ns, u"parsererror"_ns, options, rv);
+ if (rv.Failed()) {
+ return;
+ }
+
+ document->AppendChild(*element, rv);
+ if (rv.Failed()) {
+ return;
+ }
+
+ RefPtr<nsTextNode> text = document->CreateTextNode(mErrorText);
+
+ element->AppendChild(*text, rv);
+ if (rv.Failed()) {
+ return;
+ }
+
+ if (!mSourceText.IsEmpty()) {
+ ElementCreationOptionsOrString options;
+ options.SetAsString();
+
+ nsCOMPtr<Element> sourceElement =
+ document->CreateElementNS(ns, u"sourcetext"_ns, options, rv);
+ if (rv.Failed()) {
+ return;
+ }
+
+ element->AppendChild(*sourceElement, rv);
+ if (rv.Failed()) {
+ return;
+ }
+
+ text = document->CreateTextNode(mSourceText);
+
+ sourceElement->AppendChild(*text, rv);
+ if (rv.Failed()) {
+ return;
+ }
+ }
+
+ MOZ_ASSERT(document->GetReadyStateEnum() == Document::READYSTATE_LOADING,
+ "Bad readyState.");
+ document->SetReadyStateInternal(Document::READYSTATE_INTERACTIVE);
+ mObserver->OnTransformDone(mSource->OwnerDoc(), mTransformResult, document);
+}
+
+nsresult txMozillaXSLTProcessor::ensureStylesheet() {
+ if (mStylesheet) {
+ return NS_OK;
+ }
+
+ NS_ENSURE_TRUE(mStylesheetDocument, NS_ERROR_NOT_INITIALIZED);
+
+ nsINode* style = mEmbeddedStylesheetRoot;
+ if (!style) {
+ style = mStylesheetDocument;
+ }
+
+ return TX_CompileStylesheet(style, this, getter_AddRefs(mStylesheet));
+}
+
+void txMozillaXSLTProcessor::NodeWillBeDestroyed(nsINode* aNode) {
+ nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
+ if (NS_FAILED(mCompileResult)) {
+ return;
+ }
+
+ mCompileResult = ensureStylesheet();
+ mStylesheetDocument = nullptr;
+ mEmbeddedStylesheetRoot = nullptr;
+}
+
+void txMozillaXSLTProcessor::CharacterDataChanged(
+ nsIContent* aContent, const CharacterDataChangeInfo&) {
+ mStylesheet = nullptr;
+}
+
+void txMozillaXSLTProcessor::AttributeChanged(Element* aElement,
+ int32_t aNameSpaceID,
+ nsAtom* aAttribute,
+ int32_t aModType,
+ const nsAttrValue* aOldValue) {
+ mStylesheet = nullptr;
+}
+
+void txMozillaXSLTProcessor::ContentAppended(nsIContent* aFirstNewContent) {
+ mStylesheet = nullptr;
+}
+
+void txMozillaXSLTProcessor::ContentInserted(nsIContent* aChild) {
+ mStylesheet = nullptr;
+}
+
+void txMozillaXSLTProcessor::ContentRemoved(nsIContent* aChild,
+ nsIContent* aPreviousSibling) {
+ mStylesheet = nullptr;
+}
+
+/* virtual */
+JSObject* txMozillaXSLTProcessor::WrapObject(
+ JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
+ return XSLTProcessor_Binding::Wrap(aCx, this, aGivenProto);
+}
+
+DocGroup* txMozillaXSLTProcessor::GetDocGroup() const {
+ return mStylesheetDocument ? mStylesheetDocument->GetDocGroup() : nullptr;
+}
+
+/* static */
+already_AddRefed<txMozillaXSLTProcessor> txMozillaXSLTProcessor::Constructor(
+ const GlobalObject& aGlobal) {
+ RefPtr<txMozillaXSLTProcessor> processor =
+ new txMozillaXSLTProcessor(aGlobal.GetAsSupports());
+ return processor.forget();
+}
+
+/* static*/
+nsresult txMozillaXSLTProcessor::Startup() {
+ if (!txXSLTProcessor::init()) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ return NS_OK;
+}
+
+/* static*/
+void txMozillaXSLTProcessor::Shutdown() { txXSLTProcessor::shutdown(); }
+
+/* static */
+UniquePtr<txVariable::OwningXSLTParameterValue> txVariable::convertToOwning(
+ const XSLTParameterValue& aValue, ErrorResult& aError) {
+ UniquePtr<OwningXSLTParameterValue> value =
+ MakeUnique<OwningXSLTParameterValue>();
+ if (aValue.IsUnrestrictedDouble()) {
+ value->SetAsUnrestrictedDouble() = aValue.GetAsUnrestrictedDouble();
+ } else if (aValue.IsBoolean()) {
+ value->SetAsBoolean() = aValue.GetAsBoolean();
+ } else if (aValue.IsString()) {
+ value->SetAsString() = aValue.GetAsString();
+ } else if (aValue.IsNode()) {
+ value->SetAsNode() = aValue.GetAsNode();
+ } else if (aValue.IsNodeSequence()) {
+ value->SetAsNodeSequence() = aValue.GetAsNodeSequence();
+ } else if (aValue.IsXPathResult()) {
+ // Clone the XPathResult so that mutations don't affect this variable.
+ RefPtr<XPathResult> clone = aValue.GetAsXPathResult().Clone(aError);
+ if (aError.Failed()) {
+ return nullptr;
+ }
+ value->SetAsXPathResult() = *clone;
+ } else {
+ MOZ_ASSERT(false, "Unknown type?");
+ }
+ return value;
+}
+
+/* static */
+nsresult txVariable::convert(const OwningXSLTParameterValue& aUnionValue,
+ txAExprResult** aValue) {
+ if (aUnionValue.IsUnrestrictedDouble()) {
+ NS_ADDREF(*aValue = new NumberResult(aUnionValue.GetAsUnrestrictedDouble(),
+ nullptr));
+ return NS_OK;
+ }
+
+ if (aUnionValue.IsBoolean()) {
+ NS_ADDREF(*aValue = new BooleanResult(aUnionValue.GetAsBoolean()));
+ return NS_OK;
+ }
+
+ if (aUnionValue.IsString()) {
+ NS_ADDREF(*aValue = new StringResult(aUnionValue.GetAsString(), nullptr));
+ return NS_OK;
+ }
+
+ if (aUnionValue.IsNode()) {
+ nsINode& node = aUnionValue.GetAsNode();
+ UniquePtr<txXPathNode> xpathNode(txXPathNativeNode::createXPathNode(&node));
+ if (!xpathNode) {
+ return NS_ERROR_FAILURE;
+ }
+
+ NS_ADDREF(*aValue = new txNodeSet(*xpathNode, nullptr));
+ return NS_OK;
+ }
+
+ if (aUnionValue.IsNodeSequence()) {
+ RefPtr<txNodeSet> nodeSet(new txNodeSet(nullptr));
+ const Sequence<OwningNonNull<nsINode>>& values =
+ aUnionValue.GetAsNodeSequence();
+ for (const auto& node : values) {
+ UniquePtr<txXPathNode> xpathNode(
+ txXPathNativeNode::createXPathNode(node.get()));
+ if (!xpathNode) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nodeSet->append(*xpathNode);
+ }
+ nodeSet.forget(aValue);
+ return NS_OK;
+ }
+
+ MOZ_ASSERT(aUnionValue.IsXPathResult());
+
+ XPathResult& xpathResult = aUnionValue.GetAsXPathResult();
+ if (xpathResult.ResultType() == XPathResult::NUMBER_TYPE) {
+ IgnoredErrorResult rv;
+ NS_ADDREF(*aValue =
+ new NumberResult(xpathResult.GetNumberValue(rv), nullptr));
+ MOZ_ASSERT(!rv.Failed());
+ return NS_OK;
+ }
+
+ if (xpathResult.ResultType() == XPathResult::BOOLEAN_TYPE) {
+ IgnoredErrorResult rv;
+ NS_ADDREF(*aValue = new BooleanResult(xpathResult.GetBooleanValue(rv)));
+ MOZ_ASSERT(!rv.Failed());
+ return NS_OK;
+ }
+
+ if (xpathResult.ResultType() == XPathResult::STRING_TYPE) {
+ IgnoredErrorResult rv;
+ nsString value;
+ xpathResult.GetStringValue(value, rv);
+ NS_ADDREF(*aValue = new StringResult(value, nullptr));
+ MOZ_ASSERT(!rv.Failed());
+ return NS_OK;
+ }
+
+ // If the XPathResult holds a nodeset, then it will keep the nodes alive and
+ // we'll hold the XPathResult alive.
+ return xpathResult.GetExprResult(aValue);
+}