diff options
Diffstat (limited to 'dom/xslt/xslt/txStylesheet.cpp')
-rw-r--r-- | dom/xslt/xslt/txStylesheet.cpp | 550 |
1 files changed, 550 insertions, 0 deletions
diff --git a/dom/xslt/xslt/txStylesheet.cpp b/dom/xslt/xslt/txStylesheet.cpp new file mode 100644 index 0000000000..5226ef5e08 --- /dev/null +++ b/dom/xslt/xslt/txStylesheet.cpp @@ -0,0 +1,550 @@ +/* -*- 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 "txStylesheet.h" + +#include <utility> + +#include "mozilla/FloatingPoint.h" +#include "txExpr.h" +#include "txInstructions.h" +#include "txKey.h" +#include "txLog.h" +#include "txToplevelItems.h" +#include "txXPathTreeWalker.h" +#include "txXSLTFunctions.h" +#include "txXSLTPatterns.h" + +using mozilla::LogLevel; +using mozilla::MakeUnique; +using mozilla::UniquePtr; +using mozilla::Unused; +using mozilla::WrapUnique; + +txStylesheet::txStylesheet() : mRootFrame(nullptr) {} + +nsresult txStylesheet::init() { + mRootFrame = new ImportFrame; + + // Create default templates + // element/root template + mContainerTemplate = MakeUnique<txPushParams>(); + + UniquePtr<txNodeTest> nt(new txNodeTypeTest(txNodeTypeTest::NODE_TYPE)); + UniquePtr<Expr> nodeExpr( + new LocationStep(nt.get(), LocationStep::CHILD_AXIS)); + Unused << nt.release(); + + txPushNewContext* pushContext = new txPushNewContext(std::move(nodeExpr)); + mContainerTemplate->mNext = WrapUnique(pushContext); + + txApplyDefaultElementTemplate* applyTemplates = + new txApplyDefaultElementTemplate; + pushContext->mNext = WrapUnique(applyTemplates); + + txLoopNodeSet* loopNodeSet = new txLoopNodeSet(applyTemplates); + applyTemplates->mNext = WrapUnique(loopNodeSet); + + txPopParams* popParams = new txPopParams; + loopNodeSet->mNext = WrapUnique(popParams); + pushContext->mBailTarget = loopNodeSet->mNext.get(); + + popParams->mNext = MakeUnique<txReturn>(); + + // attribute/textnode template + nt = MakeUnique<txNodeTypeTest>(txNodeTypeTest::NODE_TYPE); + nodeExpr = MakeUnique<LocationStep>(nt.get(), LocationStep::SELF_AXIS); + Unused << nt.release(); + + mCharactersTemplate = MakeUnique<txValueOf>(std::move(nodeExpr), false); + mCharactersTemplate->mNext = MakeUnique<txReturn>(); + + // pi/comment/namespace template + mEmptyTemplate = MakeUnique<txReturn>(); + + return NS_OK; +} + +txStylesheet::~txStylesheet() { + // Delete all ImportFrames + delete mRootFrame; + txListIterator frameIter(&mImportFrames); + while (frameIter.hasNext()) { + delete static_cast<ImportFrame*>(frameIter.next()); + } + + txListIterator instrIter(&mTemplateInstructions); + while (instrIter.hasNext()) { + delete static_cast<txInstruction*>(instrIter.next()); + } + + // We can't make the map own its values because then we wouldn't be able + // to merge attributesets of the same name + txExpandedNameMap<txInstruction>::iterator attrSetIter(mAttributeSets); + while (attrSetIter.next()) { + delete attrSetIter.value(); + } +} + +nsresult txStylesheet::findTemplate(const txXPathNode& aNode, + const txExpandedName& aMode, + txIMatchContext* aContext, + ImportFrame* aImportedBy, + txInstruction** aTemplate, + ImportFrame** aImportFrame) { + NS_ASSERTION(aImportFrame, "missing ImportFrame pointer"); + + *aTemplate = nullptr; + *aImportFrame = nullptr; + ImportFrame* endFrame = nullptr; + txListIterator frameIter(&mImportFrames); + + if (aImportedBy) { + ImportFrame* curr = static_cast<ImportFrame*>(frameIter.next()); + while (curr != aImportedBy) { + curr = static_cast<ImportFrame*>(frameIter.next()); + } + endFrame = aImportedBy->mFirstNotImported; + } + +#if defined(TX_TO_STRING) + txPattern* match = 0; +#endif + + ImportFrame* frame; + while (!*aTemplate && (frame = static_cast<ImportFrame*>(frameIter.next())) && + frame != endFrame) { + // get templatelist for this mode + nsTArray<MatchableTemplate>* templates = + frame->mMatchableTemplates.get(aMode); + + if (templates) { + // Find template with highest priority + uint32_t i, len = templates->Length(); + for (i = 0; i < len && !*aTemplate; ++i) { + MatchableTemplate& templ = (*templates)[i]; + bool matched; + nsresult rv = templ.mMatch->matches(aNode, aContext, matched); + NS_ENSURE_SUCCESS(rv, rv); + + if (matched) { + *aTemplate = templ.mFirstInstruction; + *aImportFrame = frame; +#if defined(TX_TO_STRING) + match = templ.mMatch.get(); +#endif + } + } + } + } + + if (MOZ_LOG_TEST(txLog::xslt, LogLevel::Debug)) { + nsAutoString mode, nodeName; + if (aMode.mLocalName) { + aMode.mLocalName->ToString(mode); + } + txXPathNodeUtils::getNodeName(aNode, nodeName); + if (*aTemplate) { + nsAutoString matchAttr; +#ifdef TX_TO_STRING + match->toString(matchAttr); +#endif + MOZ_LOG(txLog::xslt, LogLevel::Debug, + ("MatchTemplate, Pattern %s, Mode %s, Node %s\n", + NS_LossyConvertUTF16toASCII(matchAttr).get(), + NS_LossyConvertUTF16toASCII(mode).get(), + NS_LossyConvertUTF16toASCII(nodeName).get())); + } else { + MOZ_LOG(txLog::xslt, LogLevel::Debug, + ("No match, Node %s, Mode %s\n", + NS_LossyConvertUTF16toASCII(nodeName).get(), + NS_LossyConvertUTF16toASCII(mode).get())); + } + } + + if (!*aTemplate) { + // Test for these first since a node can be both a text node + // and a root (if it is orphaned) + if (txXPathNodeUtils::isAttribute(aNode) || + txXPathNodeUtils::isText(aNode)) { + *aTemplate = mCharactersTemplate.get(); + } else if (txXPathNodeUtils::isElement(aNode) || + txXPathNodeUtils::isRoot(aNode)) { + *aTemplate = mContainerTemplate.get(); + } else { + *aTemplate = mEmptyTemplate.get(); + } + } + + return NS_OK; +} + +txDecimalFormat* txStylesheet::getDecimalFormat(const txExpandedName& aName) { + return mDecimalFormats.get(aName); +} + +txInstruction* txStylesheet::getAttributeSet(const txExpandedName& aName) { + return mAttributeSets.get(aName); +} + +txInstruction* txStylesheet::getNamedTemplate(const txExpandedName& aName) { + return mNamedTemplates.get(aName); +} + +txOutputFormat* txStylesheet::getOutputFormat() { return &mOutputFormat; } + +txStylesheet::GlobalVariable* txStylesheet::getGlobalVariable( + const txExpandedName& aName) { + return mGlobalVariables.get(aName); +} + +const txOwningExpandedNameMap<txXSLKey>& txStylesheet::getKeyMap() { + return mKeys; +} + +nsresult txStylesheet::isStripSpaceAllowed(const txXPathNode& aNode, + txIMatchContext* aContext, + bool& aAllowed) { + int32_t frameCount = mStripSpaceTests.Length(); + if (frameCount == 0) { + aAllowed = false; + + return NS_OK; + } + + txXPathTreeWalker walker(aNode); + + if (txXPathNodeUtils::isText(walker.getCurrentPosition()) && + (!txXPathNodeUtils::isWhitespace(aNode) || !walker.moveToParent())) { + aAllowed = false; + + return NS_OK; + } + + const txXPathNode& node = walker.getCurrentPosition(); + + if (!txXPathNodeUtils::isElement(node)) { + aAllowed = false; + + return NS_OK; + } + + // check Whitespace stipping handling list against given Node + int32_t i; + for (i = 0; i < frameCount; ++i) { + const auto& sst = mStripSpaceTests[i]; + bool matched; + nsresult rv = sst->matches(node, aContext, matched); + NS_ENSURE_SUCCESS(rv, rv); + + if (matched) { + aAllowed = sst->stripsSpace() && !XMLUtils::getXMLSpacePreserve(node); + + return NS_OK; + } + } + + aAllowed = false; + + return NS_OK; +} + +nsresult txStylesheet::doneCompiling() { + nsresult rv = NS_OK; + // Collect all importframes into a single ordered list + txListIterator frameIter(&mImportFrames); + frameIter.addAfter(mRootFrame); + + mRootFrame = nullptr; + frameIter.next(); + rv = addFrames(frameIter); + NS_ENSURE_SUCCESS(rv, rv); + + // Loop through importframes in decreasing-precedence-order and process + // all items + frameIter.reset(); + ImportFrame* frame; + while ((frame = static_cast<ImportFrame*>(frameIter.next()))) { + nsTArray<txStripSpaceTest*> frameStripSpaceTests; + + txListIterator itemIter(&frame->mToplevelItems); + itemIter.resetToEnd(); + txToplevelItem* item; + while ((item = static_cast<txToplevelItem*>(itemIter.previous()))) { + switch (item->getType()) { + case txToplevelItem::attributeSet: { + rv = addAttributeSet(static_cast<txAttributeSetItem*>(item)); + NS_ENSURE_SUCCESS(rv, rv); + break; + } + case txToplevelItem::dummy: + case txToplevelItem::import: { + break; + } + case txToplevelItem::output: { + mOutputFormat.merge(static_cast<txOutputItem*>(item)->mFormat); + break; + } + case txToplevelItem::stripSpace: { + rv = addStripSpace(static_cast<txStripSpaceItem*>(item), + frameStripSpaceTests); + NS_ENSURE_SUCCESS(rv, rv); + break; + } + case txToplevelItem::templ: { + rv = addTemplate(static_cast<txTemplateItem*>(item), frame); + NS_ENSURE_SUCCESS(rv, rv); + + break; + } + case txToplevelItem::variable: { + rv = addGlobalVariable(static_cast<txVariableItem*>(item)); + NS_ENSURE_SUCCESS(rv, rv); + + break; + } + } + delete item; + itemIter.remove(); // remove() moves to the previous + itemIter.next(); + } + mStripSpaceTests.AppendElements(frameStripSpaceTests); + frameStripSpaceTests.Clear(); + } + + if (!mDecimalFormats.get(txExpandedName())) { + UniquePtr<txDecimalFormat> format(new txDecimalFormat); + rv = mDecimalFormats.add(txExpandedName(), format.get()); + NS_ENSURE_SUCCESS(rv, rv); + + Unused << format.release(); + } + + return NS_OK; +} + +nsresult txStylesheet::addTemplate(txTemplateItem* aTemplate, + ImportFrame* aImportFrame) { + NS_ASSERTION(aTemplate, "missing template"); + + txInstruction* instr = aTemplate->mFirstInstruction.get(); + mTemplateInstructions.add(instr); + + // mTemplateInstructions now owns the instructions + Unused << aTemplate->mFirstInstruction.release(); + + if (!aTemplate->mName.isNull()) { + nsresult rv = mNamedTemplates.add(aTemplate->mName, instr); + NS_ENSURE_TRUE(NS_SUCCEEDED(rv) || rv == NS_ERROR_XSLT_ALREADY_SET, rv); + } + + if (!aTemplate->mMatch) { + // This is no error, see section 6 Named Templates + + return NS_OK; + } + + // get the txList for the right mode + nsTArray<MatchableTemplate>* templates = + aImportFrame->mMatchableTemplates.get(aTemplate->mMode); + + if (!templates) { + UniquePtr<nsTArray<MatchableTemplate>> newList( + new nsTArray<MatchableTemplate>); + nsresult rv = + aImportFrame->mMatchableTemplates.set(aTemplate->mMode, newList.get()); + NS_ENSURE_SUCCESS(rv, rv); + + templates = newList.release(); + } + + // Add the simple patterns to the list of matchable templates, according + // to default priority + UniquePtr<txPattern> simple = std::move(aTemplate->mMatch); + UniquePtr<txPattern> unionPattern; + if (simple->getType() == txPattern::UNION_PATTERN) { + unionPattern = std::move(simple); + simple = WrapUnique(unionPattern->getSubPatternAt(0)); + unionPattern->setSubPatternAt(0, nullptr); + } + + uint32_t unionPos = 1; // only used when unionPattern is set + while (simple) { + double priority = aTemplate->mPrio; + if (std::isnan(priority)) { + priority = simple->getDefaultPriority(); + NS_ASSERTION(!std::isnan(priority), + "simple pattern without default priority"); + } + + uint32_t i, len = templates->Length(); + for (i = 0; i < len; ++i) { + if (priority > (*templates)[i].mPriority) { + break; + } + } + + MatchableTemplate* nt = templates->InsertElementAt(i); + nt->mFirstInstruction = instr; + nt->mMatch = std::move(simple); + nt->mPriority = priority; + + if (unionPattern) { + simple = WrapUnique(unionPattern->getSubPatternAt(unionPos)); + if (simple) { + unionPattern->setSubPatternAt(unionPos, nullptr); + } + ++unionPos; + } + } + + return NS_OK; +} + +nsresult txStylesheet::addFrames(txListIterator& aInsertIter) { + ImportFrame* frame = static_cast<ImportFrame*>(aInsertIter.current()); + nsresult rv = NS_OK; + txListIterator iter(&frame->mToplevelItems); + txToplevelItem* item; + while ((item = static_cast<txToplevelItem*>(iter.next()))) { + if (item->getType() == txToplevelItem::import) { + txImportItem* import = static_cast<txImportItem*>(item); + import->mFrame->mFirstNotImported = + static_cast<ImportFrame*>(aInsertIter.next()); + aInsertIter.addBefore(import->mFrame.release()); + aInsertIter.previous(); + rv = addFrames(aInsertIter); + NS_ENSURE_SUCCESS(rv, rv); + aInsertIter.previous(); + } + } + + return NS_OK; +} + +nsresult txStylesheet::addStripSpace( + txStripSpaceItem* aStripSpaceItem, + nsTArray<txStripSpaceTest*>& aFrameStripSpaceTests) { + int32_t testCount = aStripSpaceItem->mStripSpaceTests.Length(); + for (; testCount > 0; --testCount) { + txStripSpaceTest* sst = aStripSpaceItem->mStripSpaceTests[testCount - 1]; + double priority = sst->getDefaultPriority(); + int32_t i, frameCount = aFrameStripSpaceTests.Length(); + for (i = 0; i < frameCount; ++i) { + if (aFrameStripSpaceTests[i]->getDefaultPriority() < priority) { + break; + } + } + aFrameStripSpaceTests.InsertElementAt(i, sst); + aStripSpaceItem->mStripSpaceTests.RemoveElementAt(testCount - 1); + } + + return NS_OK; +} + +nsresult txStylesheet::addAttributeSet(txAttributeSetItem* aAttributeSetItem) { + nsresult rv = NS_OK; + txInstruction* oldInstr = mAttributeSets.get(aAttributeSetItem->mName); + if (!oldInstr) { + rv = mAttributeSets.add(aAttributeSetItem->mName, + aAttributeSetItem->mFirstInstruction.get()); + NS_ENSURE_SUCCESS(rv, rv); + + Unused << aAttributeSetItem->mFirstInstruction.release(); + + return NS_OK; + } + + // We need to prepend the new instructions before the existing ones. + txInstruction* instr = aAttributeSetItem->mFirstInstruction.get(); + txInstruction* lastNonReturn = nullptr; + while (instr->mNext) { + lastNonReturn = instr; + instr = instr->mNext.get(); + } + + if (!lastNonReturn) { + // The new attributeset is empty, so lets just ignore it. + return NS_OK; + } + + rv = mAttributeSets.set(aAttributeSetItem->mName, + aAttributeSetItem->mFirstInstruction.get()); + NS_ENSURE_SUCCESS(rv, rv); + + Unused << aAttributeSetItem->mFirstInstruction.release(); + + lastNonReturn->mNext = + WrapUnique(oldInstr); // ...and link up the old instructions. + + return NS_OK; +} + +nsresult txStylesheet::addGlobalVariable(txVariableItem* aVariable) { + if (mGlobalVariables.get(aVariable->mName)) { + return NS_OK; + } + UniquePtr<GlobalVariable> var(new GlobalVariable( + std::move(aVariable->mValue), std::move(aVariable->mFirstInstruction), + aVariable->mIsParam)); + nsresult rv = mGlobalVariables.add(aVariable->mName, var.get()); + NS_ENSURE_SUCCESS(rv, rv); + + Unused << var.release(); + + return NS_OK; +} + +nsresult txStylesheet::addKey(const txExpandedName& aName, + UniquePtr<txPattern> aMatch, + UniquePtr<Expr> aUse) { + nsresult rv = NS_OK; + + txXSLKey* xslKey = mKeys.get(aName); + if (!xslKey) { + xslKey = new txXSLKey(aName); + rv = mKeys.add(aName, xslKey); + if (NS_FAILED(rv)) { + delete xslKey; + return rv; + } + } + if (!xslKey->addKey(std::move(aMatch), std::move(aUse))) { + return NS_ERROR_OUT_OF_MEMORY; + } + return NS_OK; +} + +nsresult txStylesheet::addDecimalFormat(const txExpandedName& aName, + UniquePtr<txDecimalFormat>&& aFormat) { + txDecimalFormat* existing = mDecimalFormats.get(aName); + if (existing) { + NS_ENSURE_TRUE(existing->isEqual(aFormat.get()), + NS_ERROR_XSLT_PARSE_FAILURE); + + return NS_OK; + } + + nsresult rv = mDecimalFormats.add(aName, aFormat.get()); + NS_ENSURE_SUCCESS(rv, rv); + + Unused << aFormat.release(); + + return NS_OK; +} + +txStylesheet::ImportFrame::~ImportFrame() { + txListIterator tlIter(&mToplevelItems); + while (tlIter.hasNext()) { + delete static_cast<txToplevelItem*>(tlIter.next()); + } +} + +txStylesheet::GlobalVariable::GlobalVariable(UniquePtr<Expr>&& aExpr, + UniquePtr<txInstruction>&& aInstr, + bool aIsParam) + : mExpr(std::move(aExpr)), + mFirstInstruction(std::move(aInstr)), + mIsParam(aIsParam) {} |