/* -*- 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 #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(); UniquePtr nt(new txNodeTypeTest(txNodeTypeTest::NODE_TYPE)); UniquePtr 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(); // attribute/textnode template nt = MakeUnique(txNodeTypeTest::NODE_TYPE); nodeExpr = MakeUnique(nt.get(), LocationStep::SELF_AXIS); Unused << nt.release(); mCharactersTemplate = MakeUnique(std::move(nodeExpr), false); mCharactersTemplate->mNext = MakeUnique(); // pi/comment/namespace template mEmptyTemplate = MakeUnique(); return NS_OK; } txStylesheet::~txStylesheet() { // Delete all ImportFrames delete mRootFrame; txListIterator frameIter(&mImportFrames); while (frameIter.hasNext()) { delete static_cast(frameIter.next()); } txListIterator instrIter(&mTemplateInstructions); while (instrIter.hasNext()) { delete static_cast(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::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(frameIter.next()); while (curr != aImportedBy) { curr = static_cast(frameIter.next()); } endFrame = aImportedBy->mFirstNotImported; } #if defined(TX_TO_STRING) txPattern* match = 0; #endif ImportFrame* frame; while (!*aTemplate && (frame = static_cast(frameIter.next())) && frame != endFrame) { // get templatelist for this mode nsTArray* 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& 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(frameIter.next()))) { nsTArray frameStripSpaceTests; txListIterator itemIter(&frame->mToplevelItems); itemIter.resetToEnd(); txToplevelItem* item; while ((item = static_cast(itemIter.previous()))) { switch (item->getType()) { case txToplevelItem::attributeSet: { rv = addAttributeSet(static_cast(item)); NS_ENSURE_SUCCESS(rv, rv); break; } case txToplevelItem::dummy: case txToplevelItem::import: { break; } case txToplevelItem::output: { mOutputFormat.merge(static_cast(item)->mFormat); break; } case txToplevelItem::stripSpace: { rv = addStripSpace(static_cast(item), frameStripSpaceTests); NS_ENSURE_SUCCESS(rv, rv); break; } case txToplevelItem::templ: { rv = addTemplate(static_cast(item), frame); NS_ENSURE_SUCCESS(rv, rv); break; } case txToplevelItem::variable: { rv = addGlobalVariable(static_cast(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 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* templates = aImportFrame->mMatchableTemplates.get(aTemplate->mMode); if (!templates) { UniquePtr> newList( new nsTArray); 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 simple = std::move(aTemplate->mMatch); UniquePtr 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(aInsertIter.current()); nsresult rv = NS_OK; txListIterator iter(&frame->mToplevelItems); txToplevelItem* item; while ((item = static_cast(iter.next()))) { if (item->getType() == txToplevelItem::import) { txImportItem* import = static_cast(item); import->mFrame->mFirstNotImported = static_cast(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& 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 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 aMatch, UniquePtr 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&& 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(tlIter.next()); } } txStylesheet::GlobalVariable::GlobalVariable(UniquePtr&& aExpr, UniquePtr&& aInstr, bool aIsParam) : mExpr(std::move(aExpr)), mFirstInstruction(std::move(aInstr)), mIsParam(aIsParam) {}