summaryrefslogtreecommitdiffstats
path: root/dom/xslt/xslt/txStylesheet.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/xslt/xslt/txStylesheet.cpp')
-rw-r--r--dom/xslt/xslt/txStylesheet.cpp550
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) {}