diff options
Diffstat (limited to '')
-rw-r--r-- | dom/xslt/xslt/txExecutionState.cpp | 460 |
1 files changed, 460 insertions, 0 deletions
diff --git a/dom/xslt/xslt/txExecutionState.cpp b/dom/xslt/xslt/txExecutionState.cpp new file mode 100644 index 0000000000..a3cfddac1c --- /dev/null +++ b/dom/xslt/xslt/txExecutionState.cpp @@ -0,0 +1,460 @@ +/* -*- 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 "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" + +using mozilla::UniquePtr; +using mozilla::Unused; +using mozilla::WrapUnique; + +const int32_t txExecutionState::kMaxRecursionDepth = 20000; + +nsresult txLoadedDocumentsHash::init(const txXPathNode& aSource) { + mSourceDocument = WrapUnique(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 = WrapUnique( + 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.release(); + } + } + } +} + +txExecutionState::txExecutionState(txStylesheet* aStylesheet, + bool aDisableLoads) + : mOutputHandler(nullptr), + mResultHandler(nullptr), + mOutputHandlerFactory(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()) { + delete (txAXMLEventHandler*)handlerIter.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(u"Error"_ns, nullptr); + + // Initiate first instruction. This has to be done last since findTemplate + // might use us. + txStylesheet::ImportFrame* frame = 0; + txExpandedName nullName; + txInstruction* templ; + rv = + mStylesheet->findTemplate(aNode, nullName, this, nullptr, &templ, &frame); + NS_ENSURE_SUCCESS(rv, rv); + + 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::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, nsAtom* 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 { + pushResultHandler(new txRtfHandler); + + txInstruction* prevInstr = mNextInstruction; + // set return to nullptr to stop execution + mNextInstruction = nullptr; + rv = runTemplate(var->mFirstInstruction.get()); + 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; + UniquePtr<txRtfHandler> rtfHandler( + static_cast<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; +} + +nsresult txExecutionState::isStripSpaceAllowed(const txXPathNode& aNode, + bool& aAllowed) { + return mStylesheet->isStripSpaceAllowed(aNode, this, aAllowed); +} + +void* txExecutionState::getPrivateContext() { return this; } + +txResultRecycler* txExecutionState::recycler() { return mRecycler; } + +void txExecutionState::receiveError(const nsAString& aMsg, nsresult aRes) { + // XXX implement me +} + +void txExecutionState::pushEvalContext(txIEvalContext* aContext) { + mEvalContextStack.push(mEvalContext); + mEvalContext = aContext; +} + +txIEvalContext* txExecutionState::popEvalContext() { + txIEvalContext* prev = mEvalContext; + mEvalContext = (txIEvalContext*)mEvalContextStack.pop(); + + return prev; +} + +void txExecutionState::pushBool(bool aBool) { mBoolStack.AppendElement(aBool); } + +bool txExecutionState::popBool() { + NS_ASSERTION(mBoolStack.Length(), "popping from empty stack"); + + return mBoolStack.IsEmpty() ? false : mBoolStack.PopLastElement(); +} + +void txExecutionState::pushResultHandler(txAXMLEventHandler* aHandler) { + mResultHandlerStack.push(mResultHandler); + mResultHandler = aHandler; +} + +txAXMLEventHandler* txExecutionState::popResultHandler() { + txAXMLEventHandler* oldHandler = mResultHandler; + mResultHandler = (txAXMLEventHandler*)mResultHandlerStack.pop(); + + return oldHandler; +} + +void txExecutionState::pushTemplateRule(txStylesheet::ImportFrame* aFrame, + const txExpandedName& aMode, + txParameterMap* aParams) { + TemplateRule* rule = mTemplateRules.AppendElement(); + rule->mFrame = aFrame; + rule->mModeNsId = aMode.mNamespaceID; + rule->mModeLocalName = aMode.mLocalName; + rule->mParams = aParams; +} + +void txExecutionState::popTemplateRule() { + MOZ_ASSERT(!mTemplateRules.IsEmpty(), "No rules to pop"); + mTemplateRules.RemoveLastElement(); +} + +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, mozilla::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(u"Couldn't load document '"_ns + aUri + u"': "_ns + errMsg, + entry->mLoadResult); + } + } + + return entry->mDocument.get(); +} + +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() { + MOZ_ASSERT(!mTemplateRules.IsEmpty(), "No current rule!"); + return &mTemplateRules[mTemplateRules.Length() - 1]; +} + +mozilla::Result<txInstruction*, nsresult> +txExecutionState::getNextInstruction() { + if (mStopProcessing) { + return mozilla::Err(NS_ERROR_FAILURE); + } + + txInstruction* instr = mNextInstruction; + if (instr) { + mNextInstruction = instr->mNext.get(); + } + + return instr; +} + +nsresult txExecutionState::runTemplate(txInstruction* aTemplate) { + NS_ENSURE_TRUE(++mRecursionDepth < kMaxRecursionDepth, + NS_ERROR_XSLT_BAD_RECURSION); + + mLocalVarsStack.push(mLocalVariables); + mReturnStack.push(mNextInstruction); + + 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); +} + +void txExecutionState::pushParamMap(txParameterMap* aParams) { + mParamStack.AppendElement(mTemplateParams.forget()); + mTemplateParams = aParams; +} + +already_AddRefed<txParameterMap> txExecutionState::popParamMap() { + RefPtr<txParameterMap> oldParams = std::move(mTemplateParams); + mTemplateParams = mParamStack.PopLastElement(); + + return oldParams.forget(); +} |