diff options
Diffstat (limited to 'dom/xslt/xslt/txExecutionState.cpp')
-rw-r--r-- | dom/xslt/xslt/txExecutionState.cpp | 561 |
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; +} |