summaryrefslogtreecommitdiffstats
path: root/dom/xslt/xslt/txExecutionState.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/xslt/xslt/txExecutionState.cpp')
-rw-r--r--dom/xslt/xslt/txExecutionState.cpp561
1 files changed, 561 insertions, 0 deletions
diff --git a/dom/xslt/xslt/txExecutionState.cpp b/dom/xslt/xslt/txExecutionState.cpp
new file mode 100644
index 000000000..869c3968b
--- /dev/null
+++ b/dom/xslt/xslt/txExecutionState.cpp
@@ -0,0 +1,561 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "txExecutionState.h"
+#include "txSingleNodeContext.h"
+#include "txInstructions.h"
+#include "txStylesheet.h"
+#include "txVariableMap.h"
+#include "txRtfHandler.h"
+#include "txXSLTProcessor.h"
+#include "txLog.h"
+#include "txURIUtils.h"
+#include "txXMLParser.h"
+
+const int32_t txExecutionState::kMaxRecursionDepth = 20000;
+
+nsresult
+txLoadedDocumentsHash::init(const txXPathNode& aSource)
+{
+ mSourceDocument = txXPathNodeUtils::getOwnerDocument(aSource);
+
+ nsAutoString baseURI;
+ nsresult rv = txXPathNodeUtils::getBaseURI(*mSourceDocument, baseURI);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Technically the hash holds documents, but we allow any node that we're transforming
+ // from. In particular, the document() function uses this hash and it can return the
+ // source document, but if we're transforming from a document fragment (through
+ // txMozillaXSLTProcessor::SetSourceContentModel/txMozillaXSLTProcessor::DoTransform)
+ // or from another type of node (through txMozillaXSLTProcessor::TransformToDocument
+ // or txMozillaXSLTProcessor::TransformToFragment) it makes more sense to return the
+ // real root of the source tree, which is the node where the transform started.
+ PutEntry(baseURI)->mDocument = txXPathNativeNode::createXPathNode(txXPathNativeNode::getNode(aSource));
+ return NS_OK;
+}
+
+txLoadedDocumentsHash::~txLoadedDocumentsHash()
+{
+ if (mSourceDocument) {
+ nsAutoString baseURI;
+ nsresult rv = txXPathNodeUtils::getBaseURI(*mSourceDocument, baseURI);
+ if (NS_SUCCEEDED(rv)) {
+ txLoadedDocumentEntry* entry = GetEntry(baseURI);
+ if (entry) {
+ delete entry->mDocument.forget();
+ }
+ }
+ }
+}
+
+txExecutionState::txExecutionState(txStylesheet* aStylesheet,
+ bool aDisableLoads)
+ : mOutputHandler(nullptr),
+ mResultHandler(nullptr),
+ mStylesheet(aStylesheet),
+ mNextInstruction(nullptr),
+ mLocalVariables(nullptr),
+ mRecursionDepth(0),
+ mEvalContext(nullptr),
+ mInitialEvalContext(nullptr),
+ mGlobalParams(nullptr),
+ mKeyHash(aStylesheet->getKeyMap()),
+ mDisableLoads(aDisableLoads)
+{
+ MOZ_COUNT_CTOR(txExecutionState);
+}
+
+txExecutionState::~txExecutionState()
+{
+ MOZ_COUNT_DTOR(txExecutionState);
+
+ delete mResultHandler;
+ delete mLocalVariables;
+ if (mEvalContext != mInitialEvalContext) {
+ delete mEvalContext;
+ }
+
+ txStackIterator varsIter(&mLocalVarsStack);
+ while (varsIter.hasNext()) {
+ delete (txVariableMap*)varsIter.next();
+ }
+
+ txStackIterator contextIter(&mEvalContextStack);
+ while (contextIter.hasNext()) {
+ txIEvalContext* context = (txIEvalContext*)contextIter.next();
+ if (context != mInitialEvalContext) {
+ delete context;
+ }
+ }
+
+ txStackIterator handlerIter(&mResultHandlerStack);
+ while (handlerIter.hasNext()) {
+ txAXMLEventHandler* handler = (txAXMLEventHandler*)handlerIter.next();
+ if (handler != mObsoleteHandler) {
+ delete handler;
+ }
+ }
+
+ txStackIterator paramIter(&mParamStack);
+ while (paramIter.hasNext()) {
+ delete (txVariableMap*)paramIter.next();
+ }
+
+ delete mInitialEvalContext;
+}
+
+nsresult
+txExecutionState::init(const txXPathNode& aNode,
+ txOwningExpandedNameMap<txIGlobalParameter>* aGlobalParams)
+{
+ nsresult rv = NS_OK;
+
+ mGlobalParams = aGlobalParams;
+
+ // Set up initial context
+ mEvalContext = new txSingleNodeContext(aNode, this);
+ mInitialEvalContext = mEvalContext;
+
+ // Set up output and result-handler
+ txAXMLEventHandler* handler;
+ rv = mOutputHandlerFactory->
+ createHandlerWith(mStylesheet->getOutputFormat(), &handler);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mOutputHandler = handler;
+ mResultHandler = handler;
+ mOutputHandler->startDocument();
+
+ // Set up loaded-documents-hash
+ rv = mLoadedDocuments.init(aNode);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Init members
+ rv = mKeyHash.init();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mRecycler = new txResultRecycler;
+
+ // The actual value here doesn't really matter since noone should use this
+ // value. But lets put something errorlike in just in case
+ mGlobalVarPlaceholderValue = new StringResult(NS_LITERAL_STRING("Error"), nullptr);
+
+ // Initiate first instruction. This has to be done last since findTemplate
+ // might use us.
+ txStylesheet::ImportFrame* frame = 0;
+ txExpandedName nullName;
+ txInstruction* templ = mStylesheet->findTemplate(aNode, nullName,
+ this, nullptr, &frame);
+ pushTemplateRule(frame, nullName, nullptr);
+
+ return runTemplate(templ);
+}
+
+nsresult
+txExecutionState::end(nsresult aResult)
+{
+ NS_ASSERTION(NS_FAILED(aResult) || mTemplateRules.Length() == 1,
+ "Didn't clean up template rules properly");
+ if (NS_SUCCEEDED(aResult)) {
+ popTemplateRule();
+ }
+ else if (!mOutputHandler) {
+ return NS_OK;
+ }
+ return mOutputHandler->endDocument(aResult);
+}
+
+void
+txExecutionState::popAndDeleteEvalContext()
+{
+ if (!mEvalContextStack.isEmpty()) {
+ auto ctx = popEvalContext();
+ if (ctx != mInitialEvalContext) {
+ delete ctx;
+ }
+ }
+}
+
+void
+txExecutionState::popAndDeleteEvalContextUntil(txIEvalContext* aContext)
+{
+ auto ctx = popEvalContext();
+ while (ctx && ctx != aContext) {
+ MOZ_RELEASE_ASSERT(ctx != mInitialEvalContext);
+ delete ctx;
+ ctx = popEvalContext();
+ }
+}
+
+nsresult
+txExecutionState::getVariable(int32_t aNamespace, nsIAtom* aLName,
+ txAExprResult*& aResult)
+{
+ nsresult rv = NS_OK;
+ txExpandedName name(aNamespace, aLName);
+
+ // look for a local variable
+ if (mLocalVariables) {
+ mLocalVariables->getVariable(name, &aResult);
+ if (aResult) {
+ return NS_OK;
+ }
+ }
+
+ // look for an evaluated global variable
+ mGlobalVariableValues.getVariable(name, &aResult);
+ if (aResult) {
+ if (aResult == mGlobalVarPlaceholderValue) {
+ // XXX ErrorReport: cyclic variable-value
+ NS_RELEASE(aResult);
+ return NS_ERROR_XSLT_BAD_RECURSION;
+ }
+ return NS_OK;
+ }
+
+ // Is there perchance a global variable not evaluated yet?
+ txStylesheet::GlobalVariable* var = mStylesheet->getGlobalVariable(name);
+ if (!var) {
+ // XXX ErrorReport: variable doesn't exist in this scope
+ return NS_ERROR_FAILURE;
+ }
+
+ NS_ASSERTION((var->mExpr && !var->mFirstInstruction) ||
+ (!var->mExpr && var->mFirstInstruction),
+ "global variable should have either instruction or expression");
+
+ // Is this a stylesheet parameter that has a value?
+ if (var->mIsParam && mGlobalParams) {
+ txIGlobalParameter* param = mGlobalParams->get(name);
+ if (param) {
+ rv = param->getValue(&aResult);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mGlobalVariableValues.bindVariable(name, aResult);
+ if (NS_FAILED(rv)) {
+ NS_RELEASE(aResult);
+ return rv;
+ }
+
+ return NS_OK;
+ }
+ }
+
+ // Insert a placeholdervalue to protect against recursion
+ rv = mGlobalVariableValues.bindVariable(name, mGlobalVarPlaceholderValue);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // evaluate the global variable
+ pushEvalContext(mInitialEvalContext);
+ if (var->mExpr) {
+ txVariableMap* oldVars = mLocalVariables;
+ mLocalVariables = nullptr;
+ rv = var->mExpr->evaluate(getEvalContext(), &aResult);
+ mLocalVariables = oldVars;
+
+ if (NS_FAILED(rv)) {
+ popAndDeleteEvalContextUntil(mInitialEvalContext);
+ return rv;
+ }
+ }
+ else {
+ nsAutoPtr<txRtfHandler> rtfHandler(new txRtfHandler);
+
+ rv = pushResultHandler(rtfHandler);
+ if (NS_FAILED(rv)) {
+ popAndDeleteEvalContextUntil(mInitialEvalContext);
+ return rv;
+ }
+
+ rtfHandler.forget();
+
+ txInstruction* prevInstr = mNextInstruction;
+ // set return to nullptr to stop execution
+ mNextInstruction = nullptr;
+ rv = runTemplate(var->mFirstInstruction);
+ if (NS_FAILED(rv)) {
+ popAndDeleteEvalContextUntil(mInitialEvalContext);
+ return rv;
+ }
+
+ pushTemplateRule(nullptr, txExpandedName(), nullptr);
+ rv = txXSLTProcessor::execute(*this);
+ if (NS_FAILED(rv)) {
+ popAndDeleteEvalContextUntil(mInitialEvalContext);
+ return rv;
+ }
+
+ popTemplateRule();
+
+ mNextInstruction = prevInstr;
+ rtfHandler = (txRtfHandler*)popResultHandler();
+ rv = rtfHandler->getAsRTF(&aResult);
+ if (NS_FAILED(rv)) {
+ popAndDeleteEvalContextUntil(mInitialEvalContext);
+ return rv;
+ }
+ }
+ popEvalContext();
+
+ // Remove the placeholder and insert the calculated value
+ mGlobalVariableValues.removeVariable(name);
+ rv = mGlobalVariableValues.bindVariable(name, aResult);
+ if (NS_FAILED(rv)) {
+ NS_RELEASE(aResult);
+
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+bool
+txExecutionState::isStripSpaceAllowed(const txXPathNode& aNode)
+{
+ return mStylesheet->isStripSpaceAllowed(aNode, this);
+}
+
+void*
+txExecutionState::getPrivateContext()
+{
+ return this;
+}
+
+txResultRecycler*
+txExecutionState::recycler()
+{
+ return mRecycler;
+}
+
+void
+txExecutionState::receiveError(const nsAString& aMsg, nsresult aRes)
+{
+ // XXX implement me
+}
+
+nsresult
+txExecutionState::pushEvalContext(txIEvalContext* aContext)
+{
+ nsresult rv = mEvalContextStack.push(mEvalContext);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mEvalContext = aContext;
+
+ return NS_OK;
+}
+
+txIEvalContext*
+txExecutionState::popEvalContext()
+{
+ txIEvalContext* prev = mEvalContext;
+ mEvalContext = (txIEvalContext*)mEvalContextStack.pop();
+
+ return prev;
+}
+
+nsresult
+txExecutionState::pushBool(bool aBool)
+{
+ return mBoolStack.AppendElement(aBool) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+}
+
+bool
+txExecutionState::popBool()
+{
+ NS_ASSERTION(mBoolStack.Length(), "popping from empty stack");
+ uint32_t last = mBoolStack.Length() - 1;
+ NS_ENSURE_TRUE(last != (uint32_t)-1, false);
+
+ bool res = mBoolStack.ElementAt(last);
+ mBoolStack.RemoveElementAt(last);
+
+ return res;
+}
+
+nsresult
+txExecutionState::pushResultHandler(txAXMLEventHandler* aHandler)
+{
+ nsresult rv = mResultHandlerStack.push(mResultHandler);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mResultHandler = aHandler;
+
+ return NS_OK;
+}
+
+txAXMLEventHandler*
+txExecutionState::popResultHandler()
+{
+ txAXMLEventHandler* oldHandler = mResultHandler;
+ mResultHandler = (txAXMLEventHandler*)mResultHandlerStack.pop();
+
+ return oldHandler;
+}
+
+void
+txExecutionState::pushTemplateRule(txStylesheet::ImportFrame* aFrame,
+ const txExpandedName& aMode,
+ txVariableMap* aParams)
+{
+ TemplateRule* rule = mTemplateRules.AppendElement();
+ rule->mFrame = aFrame;
+ rule->mModeNsId = aMode.mNamespaceID;
+ rule->mModeLocalName = aMode.mLocalName;
+ rule->mParams = aParams;
+}
+
+void
+txExecutionState::popTemplateRule()
+{
+ NS_PRECONDITION(!mTemplateRules.IsEmpty(), "No rules to pop");
+ mTemplateRules.RemoveElementAt(mTemplateRules.Length() - 1);
+}
+
+txIEvalContext*
+txExecutionState::getEvalContext()
+{
+ return mEvalContext;
+}
+
+const txXPathNode*
+txExecutionState::retrieveDocument(const nsAString& aUri)
+{
+ NS_ASSERTION(!aUri.Contains(char16_t('#')),
+ "Remove the fragment.");
+
+ if (mDisableLoads) {
+ return nullptr;
+ }
+
+ MOZ_LOG(txLog::xslt, LogLevel::Debug,
+ ("Retrieve Document %s", NS_LossyConvertUTF16toASCII(aUri).get()));
+
+ // try to get already loaded document
+ txLoadedDocumentEntry *entry = mLoadedDocuments.PutEntry(aUri);
+ if (!entry) {
+ return nullptr;
+ }
+
+ if (!entry->mDocument && !entry->LoadingFailed()) {
+ // open URI
+ nsAutoString errMsg;
+ // XXX we should get the loader from the actual node
+ // triggering the load, but this will do for the time being
+ entry->mLoadResult =
+ txParseDocumentFromURI(aUri, *mLoadedDocuments.mSourceDocument,
+ errMsg, getter_Transfers(entry->mDocument));
+
+ if (entry->LoadingFailed()) {
+ receiveError(NS_LITERAL_STRING("Couldn't load document '") +
+ aUri + NS_LITERAL_STRING("': ") + errMsg,
+ entry->mLoadResult);
+ }
+ }
+
+ return entry->mDocument;
+}
+
+nsresult
+txExecutionState::getKeyNodes(const txExpandedName& aKeyName,
+ const txXPathNode& aRoot,
+ const nsAString& aKeyValue,
+ bool aIndexIfNotFound,
+ txNodeSet** aResult)
+{
+ return mKeyHash.getKeyNodes(aKeyName, aRoot, aKeyValue,
+ aIndexIfNotFound, *this, aResult);
+}
+
+txExecutionState::TemplateRule*
+txExecutionState::getCurrentTemplateRule()
+{
+ NS_PRECONDITION(!mTemplateRules.IsEmpty(), "No current rule!");
+ return &mTemplateRules[mTemplateRules.Length() - 1];
+}
+
+txInstruction*
+txExecutionState::getNextInstruction()
+{
+ txInstruction* instr = mNextInstruction;
+ if (instr) {
+ mNextInstruction = instr->mNext;
+ }
+
+ return instr;
+}
+
+nsresult
+txExecutionState::runTemplate(txInstruction* aTemplate)
+{
+ NS_ENSURE_TRUE(++mRecursionDepth < kMaxRecursionDepth,
+ NS_ERROR_XSLT_BAD_RECURSION);
+
+ nsresult rv = mLocalVarsStack.push(mLocalVariables);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mReturnStack.push(mNextInstruction);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mLocalVariables = nullptr;
+ mNextInstruction = aTemplate;
+
+ return NS_OK;
+}
+
+void
+txExecutionState::gotoInstruction(txInstruction* aNext)
+{
+ mNextInstruction = aNext;
+}
+
+void
+txExecutionState::returnFromTemplate()
+{
+ --mRecursionDepth;
+ NS_ASSERTION(!mReturnStack.isEmpty() && !mLocalVarsStack.isEmpty(),
+ "return or variable stack is empty");
+ delete mLocalVariables;
+ mNextInstruction = (txInstruction*)mReturnStack.pop();
+ mLocalVariables = (txVariableMap*)mLocalVarsStack.pop();
+}
+
+nsresult
+txExecutionState::bindVariable(const txExpandedName& aName,
+ txAExprResult* aValue)
+{
+ if (!mLocalVariables) {
+ mLocalVariables = new txVariableMap;
+ }
+ return mLocalVariables->bindVariable(aName, aValue);
+}
+
+void
+txExecutionState::removeVariable(const txExpandedName& aName)
+{
+ mLocalVariables->removeVariable(aName);
+}
+
+nsresult
+txExecutionState::pushParamMap(txVariableMap* aParams)
+{
+ nsresult rv = mParamStack.push(mTemplateParams);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mTemplateParams.forget();
+ mTemplateParams = aParams;
+
+ return NS_OK;
+}
+
+txVariableMap*
+txExecutionState::popParamMap()
+{
+ txVariableMap* oldParams = mTemplateParams.forget();
+ mTemplateParams = (txVariableMap*)mParamStack.pop();
+
+ return oldParams;
+}