Source code

Revision control

Copy as Markdown

Other Tools

/* -*- 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) {}