/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * The contents of this file are subject to the Mozilla Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is TransforMiiX XSLT processor. * * The Initial Developer of the Original Code is The MITRE Corporation. * Portions created by MITRE are Copyright (C) 1999 The MITRE Corporation. * * Portions created by Keith Visco as a Non MITRE employee, * (C) 1999, 2000 Keith Visco. All Rights Reserved. * * Contributor(s): * Keith Visco, kvisco@ziplink.net * -- original author. * * Bob Miller, kbob@oblix.com * -- plugged core leak. * * Pierre Phaneuf, pp@ludusdesign.com * -- fixed some XPCOM usage. * * Marina Mechtcheriakova, mmarina@mindspring.com * -- Added call to recurisvely attribute-set processing on * xsl:attribute-set itself * -- Added call to handle attribute-set processing for xsl:copy * * Nathan Pride, npride@wavo.com * -- fixed a document base issue * * Olivier Gerardin * -- Changed behavior of passing parameters to templates * */ #include "XSLTProcessor.h" #include "Names.h" #include "Tokenizer.h" #include "txAtoms.h" #include "TxLog.h" #include "txNodeSetContext.h" #include "txNodeSorter.h" #include "txRtfHandler.h" #include "txTextHandler.h" #include "txURIUtils.h" #include "txVariableMap.h" #include "txXSLTNumber.h" #include "XMLDOMUtils.h" #include "XMLUtils.h" #ifndef TX_EXE #include "nsContentCID.h" #include "nsIConsoleService.h" #include "nsIDOMDocument.h" #include "nsIServiceManagerUtils.h" static NS_DEFINE_CID(kXMLDocumentCID, NS_XMLDOCUMENT_CID); #endif Expr* txXSLTProcessor::gNodeExpr = 0; /* * Implement static variables for atomservice and dom. */ #ifdef TX_EXE TX_IMPL_DOM_STATICS; #endif TX_LG_IMPL; /* static */ MBool txXSLTProcessor::txInit() { TX_LG_CREATE; #ifdef TX_EXE if (!txNamespaceManager::init()) return MB_FALSE; #endif // Create default expressions // "node()" txNodeTest* nt = new txNodeTypeTest(txNodeTypeTest::NODE_TYPE); if (!nt) { return MB_FALSE; } gNodeExpr = new LocationStep(nt, LocationStep::CHILD_AXIS); if (!gNodeExpr) { return MB_FALSE; } if (!txHTMLAtoms::init()) return MB_FALSE; if (!txXMLAtoms::init()) return MB_FALSE; if (!txXPathAtoms::init()) return MB_FALSE; return txXSLTAtoms::init(); } /* static */ MBool txXSLTProcessor::txShutdown() { TX_LG_DELETE; #ifdef TX_EXE txNamespaceManager::shutdown(); #endif delete gNodeExpr; txHTMLAtoms::shutdown(); txXMLAtoms::shutdown(); txXPathAtoms::shutdown(); txXSLTAtoms::shutdown(); return MB_TRUE; } void txXSLTProcessor::copyNode(Node* aSourceNode, ProcessorState* aPs) { if (!aSourceNode) return; switch (aSourceNode->getNodeType()) { case Node::ATTRIBUTE_NODE: { NS_ASSERTION(aPs->mResultHandler, "mResultHandler must not be NULL!"); aPs->mResultHandler->attribute(aSourceNode->getNodeName(), aSourceNode->getNamespaceID(), aSourceNode->getNodeValue()); break; } case Node::COMMENT_NODE: { NS_ASSERTION(aPs->mResultHandler, "mResultHandler must not be NULL!"); aPs->mResultHandler->comment(aSourceNode->getNodeValue()); break; } case Node::DOCUMENT_NODE: case Node::DOCUMENT_FRAGMENT_NODE: { // Copy children Node* child = aSourceNode->getFirstChild(); while (child) { copyNode(child, aPs); child = child->getNextSibling(); } break; } case Node::ELEMENT_NODE: { Element* element = (Element*)aSourceNode; const String& name = element->getNodeName(); PRInt32 nsID = element->getNamespaceID(); NS_ASSERTION(aPs->mResultHandler, "mResultHandler must not be NULL!"); aPs->mResultHandler->startElement(name, nsID); // Copy attributes NamedNodeMap* attList = element->getAttributes(); if (attList) { PRUint32 i = 0; for (i = 0; i < attList->getLength(); i++) { Attr* attr = (Attr*)attList->item(i); NS_ASSERTION(aPs->mResultHandler, "mResultHandler must not be NULL!"); aPs->mResultHandler->attribute(attr->getNodeName(), attr->getNamespaceID(), attr->getNodeValue()); } } // Copy children Node* child = element->getFirstChild(); while (child) { copyNode(child, aPs); child = child->getNextSibling(); } NS_ASSERTION(aPs->mResultHandler, "mResultHandler must not be NULL!"); aPs->mResultHandler->endElement(name, nsID); break; } case Node::PROCESSING_INSTRUCTION_NODE: { ProcessingInstruction* pi = (ProcessingInstruction*)aSourceNode; NS_ASSERTION(aPs->mResultHandler, "mResultHandler must not be NULL!"); aPs->mResultHandler->processingInstruction(pi->getNodeName(), pi->getNodeValue()); break; } case Node::TEXT_NODE: case Node::CDATA_SECTION_NODE: { NS_ASSERTION(aPs->mResultHandler, "mResultHandler must not be NULL!"); aPs->mResultHandler->characters(aSourceNode->getNodeValue()); break; } } } Document* txXSLTProcessor::createRTFDocument(txOutputMethod aMethod) { #ifdef TX_EXE return new Document(); #else nsresult rv; nsCOMPtr domDoc = do_CreateInstance(kXMLDocumentCID, &rv); NS_ENSURE_SUCCESS(rv, nsnull); return new Document(domDoc); #endif } void txXSLTProcessor::logMessage(const String& aMessage) { #ifdef TX_EXE cout << "xsl:message - "<< aMessage << endl; #else nsresult rv; nsCOMPtr consoleSvc = do_GetService("@mozilla.org/consoleservice;1", &rv); NS_ASSERTION(NS_SUCCEEDED(rv), "xsl:message couldn't get console service"); if (consoleSvc) { nsAutoString logString(NS_LITERAL_STRING("xsl:message - ")); logString.Append(aMessage.getConstNSString()); rv = consoleSvc->LogStringMessage(logString.get()); NS_ASSERTION(NS_SUCCEEDED(rv), "xsl:message couldn't log"); } #endif } void txXSLTProcessor::processAction(Node* aAction, ProcessorState* aPs) { nsresult rv = NS_OK; NS_ASSERTION(aAction, "We need an action to process."); if (!aAction) return; unsigned short nodeType = aAction->getNodeType(); // Handle text nodes if (nodeType == Node::TEXT_NODE || nodeType == Node::CDATA_SECTION_NODE) { const String& textValue = aAction->getNodeValue(); if (!XMLUtils::isWhitespace(textValue) || XMLUtils::getXMLSpacePreserve(aAction)) { NS_ASSERTION(aPs->mResultHandler, "mResultHandler must not be NULL!"); aPs->mResultHandler->characters(textValue); } return; } if (nodeType != Node::ELEMENT_NODE) { return; } // Handle element nodes Element* actionElement = (Element*)aAction; PRInt32 nsID = aAction->getNamespaceID(); if (nsID != kNameSpaceID_XSLT) { // Literal result element // XXX TODO Check for excluded namespaces and aliased namespaces (element and attributes) const String& nodeName = aAction->getNodeName(); NS_ASSERTION(aPs->mResultHandler, "mResultHandler must not be NULL!"); aPs->mResultHandler->startElement(nodeName, nsID); processAttributeSets(actionElement, aPs); // Handle attributes NamedNodeMap* atts = actionElement->getAttributes(); if (atts) { // Process all non XSLT attributes PRUint32 i; for (i = 0; i < atts->getLength(); ++i) { Attr* attr = (Attr*)atts->item(i); if (attr->getNamespaceID() == kNameSpaceID_XSLT) continue; // Process Attribute Value Templates String value; aPs->processAttrValueTemplate(attr->getNodeValue(), actionElement, value); NS_ASSERTION(aPs->mResultHandler, "mResultHandler must not be NULL!"); aPs->mResultHandler->attribute(attr->getNodeName(), attr->getNamespaceID(), value); } } // Process children processChildren(actionElement, aPs); NS_ASSERTION(aPs->mResultHandler, "mResultHandler must not be NULL!"); aPs->mResultHandler->endElement(nodeName, nsID); return; } Expr* expr = 0; txAtom* localName; aAction->getLocalName(&localName); // xsl:apply-imports if (localName == txXSLTAtoms::applyImports) { ProcessorState::TemplateRule* curr; Node* xslTemplate; ProcessorState::ImportFrame *frame; curr = aPs->getCurrentTemplateRule(); if (!curr) { String err(NS_LITERAL_STRING("apply-imports not allowed here")); aPs->receiveError(err, NS_ERROR_FAILURE); TX_RELEASE_ATOM(localName); return; } xslTemplate = aPs->findTemplate(aPs->getEvalContext()->getContextNode(), *curr->mMode, curr->mFrame, &frame); processMatchedTemplate(xslTemplate, curr->mParams, *curr->mMode, frame, aPs); } // xsl:apply-templates else if (localName == txXSLTAtoms::applyTemplates) { if (actionElement->hasAttr(txXSLTAtoms::select, kNameSpaceID_None)) expr = aPs->getExpr(actionElement, ProcessorState::SelectAttr); else expr = gNodeExpr; if (!expr) { TX_RELEASE_ATOM(localName); return; } ExprResult* exprResult = expr->evaluate(aPs->getEvalContext()); if (!exprResult) { TX_RELEASE_ATOM(localName); return; } if (exprResult->getResultType() == ExprResult::NODESET) { NodeSet* nodeSet = (NodeSet*)exprResult; if (nodeSet->isEmpty()) { delete nodeSet; TX_RELEASE_ATOM(localName); return; } // Look for xsl:sort elements txNodeSorter sorter(aPs); Node* child = actionElement->getFirstChild(); while (child) { if (child->getNodeType() == Node::ELEMENT_NODE && child->getNamespaceID() == kNameSpaceID_XSLT) { txAtom* childLocalName; child->getLocalName(&childLocalName); if (childLocalName == txXSLTAtoms::sort) { sorter.addSortElement((Element*)child); } TX_IF_RELEASE_ATOM(childLocalName); } child = child->getNextSibling(); } sorter.sortNodeSet(nodeSet); // Process xsl:with-param elements txVariableMap params(0); processParameters(actionElement, ¶ms, aPs); // Get mode String modeStr; txExpandedName mode; if (actionElement->getAttr(txXSLTAtoms::mode, kNameSpaceID_None, modeStr)) { rv = mode.init(modeStr, actionElement, MB_FALSE); if (NS_FAILED(rv)) { String err(NS_LITERAL_STRING("malformed mode-name in xsl:apply-templates")); aPs->receiveError(err); TX_IF_RELEASE_ATOM(localName); return; } } txNodeSetContext evalContext(nodeSet, aPs); txIEvalContext* priorEC = aPs->setEvalContext(&evalContext); while (evalContext.hasNext()) { evalContext.next(); ProcessorState::ImportFrame *frame; Node* currNode = evalContext.getContextNode(); Node* xslTemplate; xslTemplate = aPs->findTemplate(currNode, mode, &frame); processMatchedTemplate(xslTemplate, ¶ms, mode, frame, aPs); } aPs->setEvalContext(priorEC); } else { String err(NS_LITERAL_STRING("error processing apply-templates")); aPs->receiveError(err, NS_ERROR_FAILURE); } //-- clean up delete exprResult; } // xsl:attribute else if (localName == txXSLTAtoms::attribute) { String nameAttr; if (!actionElement->getAttr(txXSLTAtoms::name, kNameSpaceID_None, nameAttr)) { String err(NS_LITERAL_STRING("missing required name attribute for xsl:attribute")); aPs->receiveError(err, NS_ERROR_FAILURE); TX_RELEASE_ATOM(localName); return; } // Process name as an AttributeValueTemplate String name; aPs->processAttrValueTemplate(nameAttr, actionElement, name); // Check name validity (must be valid QName and not xmlns) if (!XMLUtils::isValidQName(name)) { String err(NS_LITERAL_STRING("error processing xsl:attribute, ")); err.Append(name); err.Append(NS_LITERAL_STRING(" is not a valid QName.")); aPs->receiveError(err, NS_ERROR_FAILURE); TX_RELEASE_ATOM(localName); return; } txAtom* nameAtom = TX_GET_ATOM(name); if (nameAtom == txXMLAtoms::xmlns) { TX_RELEASE_ATOM(nameAtom); String err(NS_LITERAL_STRING("error processing xsl:attribute, name is xmlns.")); aPs->receiveError(err, NS_ERROR_FAILURE); TX_RELEASE_ATOM(localName); return; } TX_IF_RELEASE_ATOM(nameAtom); // Determine namespace URI from the namespace attribute or // from the prefix of the name (using the xslt action element). String resultNs; PRInt32 resultNsID = kNameSpaceID_None; if (actionElement->getAttr(txXSLTAtoms::_namespace, kNameSpaceID_None, resultNs)) { String nsURI; aPs->processAttrValueTemplate(resultNs, actionElement, nsURI); resultNsID = aPs->getStylesheetDocument()->namespaceURIToID(nsURI); } else { String prefix; XMLUtils::getPrefix(name, prefix); txAtom* prefixAtom = TX_GET_ATOM(prefix); if (prefixAtom != txXMLAtoms::_empty) { if (prefixAtom != txXMLAtoms::xmlns) resultNsID = actionElement->lookupNamespaceID(prefixAtom); else // Cut xmlns: (6 characters) name.Cut(0, 6); } TX_IF_RELEASE_ATOM(prefixAtom); } // XXX Should verify that this is correct behaviour. Signal error too? if (resultNsID == kNameSpaceID_Unknown) { TX_RELEASE_ATOM(localName); return; } // Compute value String value; processChildrenAsValue(actionElement, aPs, MB_TRUE, value); NS_ASSERTION(aPs->mResultHandler, "mResultHandler must not be NULL!"); aPs->mResultHandler->attribute(name, resultNsID, value); } // xsl:call-template else if (localName == txXSLTAtoms::callTemplate) { String nameStr; txExpandedName templateName; actionElement->getAttr(txXSLTAtoms::name, kNameSpaceID_None, nameStr); rv = templateName.init(nameStr, actionElement, MB_FALSE); if (NS_SUCCEEDED(rv)) { Element* xslTemplate = aPs->getNamedTemplate(templateName); if (xslTemplate) { #ifdef PR_LOGGING String baseURI = xslTemplate->getBaseURI(); PR_LOG(txLog::xslt, PR_LOG_DEBUG, ("CallTemplate, Name %s, Stylesheet %s\n", NS_LossyConvertUCS2toASCII(nameStr).get(), NS_LossyConvertUCS2toASCII(baseURI).get())); #endif txVariableMap params(0); processParameters(actionElement, ¶ms, aPs); processTemplate(xslTemplate, ¶ms, aPs); } } else { String err(NS_LITERAL_STRING("missing or malformed name in xsl:call-template")); aPs->receiveError(err, NS_ERROR_FAILURE); } } // xsl:choose else if (localName == txXSLTAtoms::choose) { Node* condition = actionElement->getFirstChild(); MBool caseFound = MB_FALSE; while (!caseFound && condition) { if (condition->getNodeType() != Node::ELEMENT_NODE || condition->getNamespaceID() != kNameSpaceID_XSLT) { condition = condition->getNextSibling(); continue; } Element* xslTemplate = (Element*)condition; txAtom* conditionLocalName; condition->getLocalName(&conditionLocalName); if (conditionLocalName == txXSLTAtoms::when) { expr = aPs->getExpr(xslTemplate, ProcessorState::TestAttr); if (!expr) { TX_RELEASE_ATOM(conditionLocalName); condition = condition->getNextSibling(); continue; } ExprResult* result = expr->evaluate(aPs->getEvalContext()); if (result && result->booleanValue()) { processChildren(xslTemplate, aPs); caseFound = MB_TRUE; } delete result; } else if (conditionLocalName == txXSLTAtoms::otherwise) { processChildren(xslTemplate, aPs); caseFound = MB_TRUE; } TX_IF_RELEASE_ATOM(conditionLocalName); condition = condition->getNextSibling(); } // end for-each child of xsl:choose } // xsl:comment else if (localName == txXSLTAtoms::comment) { String value; processChildrenAsValue(actionElement, aPs, MB_TRUE, value); PRInt32 pos = 0; PRUint32 length = value.Length(); while ((pos = value.indexOf('-', pos)) != kNotFound) { ++pos; if (((PRUint32)pos == length) || (value.CharAt(pos) == '-')) { value.insert(pos++, ' '); ++length; } } NS_ASSERTION(aPs->mResultHandler, "mResultHandler must not be NULL!"); aPs->mResultHandler->comment(value); } // xsl:copy else if (localName == txXSLTAtoms::copy) { xslCopy(actionElement, aPs); } // xsl:copy-of else if (localName == txXSLTAtoms::copyOf) { expr = aPs->getExpr(actionElement, ProcessorState::SelectAttr); if (!expr) { TX_RELEASE_ATOM(localName); return; } ExprResult* exprResult = expr->evaluate(aPs->getEvalContext()); xslCopyOf(exprResult, aPs); delete exprResult; } // xsl:element else if (localName == txXSLTAtoms::element) { String nameAttr; if (!actionElement->getAttr(txXSLTAtoms::name, kNameSpaceID_None, nameAttr)) { String err(NS_LITERAL_STRING("missing required name attribute for xsl:element")); aPs->receiveError(err, NS_ERROR_FAILURE); TX_RELEASE_ATOM(localName); return; } // Process name as an AttributeValueTemplate String name; aPs->processAttrValueTemplate(nameAttr, actionElement, name); // Check name validity (must be valid QName and not xmlns) if (!XMLUtils::isValidQName(name)) { String err(NS_LITERAL_STRING("error processing xsl:element, '")); err.Append(name); err.Append(NS_LITERAL_STRING("' is not a valid QName.")); aPs->receiveError(err, NS_ERROR_FAILURE); // XXX We should processChildren without creating attributes or // namespace nodes. TX_RELEASE_ATOM(localName); return; } // Determine namespace URI from the namespace attribute or // from the prefix of the name (using the xslt action element). String resultNs; PRInt32 resultNsID; if (actionElement->getAttr(txXSLTAtoms::_namespace, kNameSpaceID_None, resultNs)) { String nsURI; aPs->processAttrValueTemplate(resultNs, actionElement, nsURI); if (nsURI.IsEmpty()) resultNsID = kNameSpaceID_None; else resultNsID = aPs->getStylesheetDocument()->namespaceURIToID(nsURI); } else { String prefix; XMLUtils::getPrefix(name, prefix); txAtom* prefixAtom = TX_GET_ATOM(prefix); resultNsID = actionElement->lookupNamespaceID(prefixAtom); TX_IF_RELEASE_ATOM(prefixAtom); } if (resultNsID == kNameSpaceID_Unknown) { String err(NS_LITERAL_STRING("error processing xsl:element, can't resolve prefix on'")); err.Append(name); err.Append(NS_LITERAL_STRING("'.")); aPs->receiveError(err, NS_ERROR_FAILURE); // XXX We should processChildren without creating attributes or // namespace nodes. TX_RELEASE_ATOM(localName); return; } NS_ASSERTION(aPs->mResultHandler, "mResultHandler must not be NULL!"); aPs->mResultHandler->startElement(name, resultNsID); processAttributeSets(actionElement, aPs); processChildren(actionElement, aPs); NS_ASSERTION(aPs->mResultHandler, "mResultHandler must not be NULL!"); aPs->mResultHandler->endElement(name, resultNsID); } // xsl:for-each else if (localName == txXSLTAtoms::forEach) { expr = aPs->getExpr(actionElement, ProcessorState::SelectAttr); if (!expr) { TX_RELEASE_ATOM(localName); return; } ExprResult* exprResult = expr->evaluate(aPs->getEvalContext()); if (!exprResult) { TX_RELEASE_ATOM(localName); return; } if (exprResult->getResultType() == ExprResult::NODESET) { NodeSet* nodeSet = (NodeSet*)exprResult; if (nodeSet->isEmpty()) { delete nodeSet; TX_RELEASE_ATOM(localName); return; } txNodeSetContext evalContext(nodeSet, aPs); txIEvalContext* priorEC = aPs->setEvalContext(&evalContext); // Look for xsl:sort elements txNodeSorter sorter(aPs); Node* child = actionElement->getFirstChild(); while (child) { unsigned short nodeType = child->getNodeType(); if (nodeType == Node::ELEMENT_NODE) { txAtom* childLocalName; child->getLocalName(&childLocalName); if (child->getNamespaceID() != kNameSpaceID_XSLT || childLocalName != txXSLTAtoms::sort) { // xsl:sort must occur first TX_IF_RELEASE_ATOM(childLocalName); break; } sorter.addSortElement((Element*)child); TX_RELEASE_ATOM(childLocalName); } else if ((nodeType == Node::TEXT_NODE || nodeType == Node::CDATA_SECTION_NODE) && !XMLUtils::isWhitespace(child->getNodeValue())) { break; } child = child->getNextSibling(); } sorter.sortNodeSet(nodeSet); // Set current template to null ProcessorState::TemplateRule *oldTemplate; oldTemplate = aPs->getCurrentTemplateRule(); aPs->setCurrentTemplateRule(0); while (evalContext.hasNext()) { evalContext.next(); processChildren(actionElement, aPs); } aPs->setCurrentTemplateRule(oldTemplate); aPs->setEvalContext(priorEC); } else { String err(NS_LITERAL_STRING("error processing for-each")); aPs->receiveError(err, NS_ERROR_FAILURE); } //-- clean up exprResult delete exprResult; } // xsl:if else if (localName == txXSLTAtoms::_if) { expr = aPs->getExpr(actionElement, ProcessorState::TestAttr); if (!expr) { TX_RELEASE_ATOM(localName); return; } ExprResult* exprResult = expr->evaluate(aPs->getEvalContext()); if (!exprResult) { TX_RELEASE_ATOM(localName); return; } if (exprResult->booleanValue()) { processChildren(actionElement, aPs); } delete exprResult; } // xsl:message else if (localName == txXSLTAtoms::message) { String message; processChildrenAsValue(actionElement, aPs, MB_FALSE, message); // We should add a MessageObserver class logMessage(message); } // xsl:number else if (localName == txXSLTAtoms::number) { String result; // XXX ErrorReport, propagate result from ::createNumber txXSLTNumber::createNumber(actionElement, aPs, result); NS_ASSERTION(aPs->mResultHandler, "mResultHandler must not be NULL!"); aPs->mResultHandler->characters(result); } // xsl:param else if (localName == txXSLTAtoms::param) { String err(NS_LITERAL_STRING("misplaced xsl:param")); aPs->receiveError(err, NS_ERROR_FAILURE); } // xsl:processing-instruction else if (localName == txXSLTAtoms::processingInstruction) { String nameAttr; if (!actionElement->getAttr(txXSLTAtoms::name, kNameSpaceID_None, nameAttr)) { String err(NS_LITERAL_STRING("missing required name attribute for xsl:processing-instruction")); aPs->receiveError(err, NS_ERROR_FAILURE); TX_RELEASE_ATOM(localName); return; } // Process name as an AttributeValueTemplate String name; aPs->processAttrValueTemplate(nameAttr, actionElement, name); // Check name validity (must be valid NCName and a PITarget) // XXX Need to check for NCName and PITarget if (!XMLUtils::isValidQName(name)) { String err(NS_LITERAL_STRING("error processing xsl:processing-instruction, '")); err.Append(name); err.Append(NS_LITERAL_STRING("' is not a valid QName.")); aPs->receiveError(err, NS_ERROR_FAILURE); } // Compute value String value; processChildrenAsValue(actionElement, aPs, MB_TRUE, value); XMLUtils::normalizePIValue(value); NS_ASSERTION(aPs->mResultHandler, "mResultHandler must not be NULL!"); aPs->mResultHandler->processingInstruction(name, value); } // xsl:sort else if (localName == txXSLTAtoms::sort) { // Ignore in this loop } // xsl:text else if (localName == txXSLTAtoms::text) { String data; XMLDOMUtils::getNodeValue(actionElement, data); NS_ASSERTION(aPs->mResultHandler, "mResultHandler must not be NULL!"); MBool doe = MB_FALSE; if ((aPs->mResultHandler == aPs->mOutputHandler) && aPs->mOutputHandler->hasDisableOutputEscaping()) { String attValue; doe = actionElement->getAttr(txXSLTAtoms::disableOutputEscaping, kNameSpaceID_None, attValue) && attValue.Equals(YES_VALUE); } if (doe) { aPs->mOutputHandler->charactersNoOutputEscaping(data); } else { aPs->mResultHandler->characters(data); } } // xsl:value-of else if (localName == txXSLTAtoms::valueOf) { expr = aPs->getExpr(actionElement, ProcessorState::SelectAttr); if (!expr) { TX_RELEASE_ATOM(localName); return; } ExprResult* exprResult = expr->evaluate(aPs->getEvalContext()); String value; if (!exprResult) { String err(NS_LITERAL_STRING("null ExprResult")); aPs->receiveError(err, NS_ERROR_FAILURE); TX_RELEASE_ATOM(localName); return; } exprResult->stringValue(value); NS_ASSERTION(aPs->mResultHandler, "mResultHandler must not be NULL!"); MBool doe = MB_FALSE; if ((aPs->mResultHandler == aPs->mOutputHandler) && aPs->mOutputHandler->hasDisableOutputEscaping()) { String attValue; doe = actionElement->getAttr(txXSLTAtoms::disableOutputEscaping, kNameSpaceID_None, attValue) && attValue.Equals(YES_VALUE); } if (doe) { aPs->mOutputHandler->charactersNoOutputEscaping(value); } else { aPs->mResultHandler->characters(value); } delete exprResult; } // xsl:variable else if (localName == txXSLTAtoms::variable) { txExpandedName varName; String qName; actionElement->getAttr(txXSLTAtoms::name, kNameSpaceID_None, qName); rv = varName.init(qName, actionElement, MB_FALSE); if (NS_FAILED(rv)) { String err(NS_LITERAL_STRING("bad name for xsl:variable")); aPs->receiveError(err, NS_ERROR_FAILURE); TX_RELEASE_ATOM(localName); return; } ExprResult* exprResult = processVariable(actionElement, aPs); if (!exprResult) { TX_RELEASE_ATOM(localName); return; } txVariableMap* vars = aPs->getLocalVariables(); NS_ASSERTION(vars, "missing localvariable map"); rv = vars->bindVariable(varName, exprResult, MB_TRUE); if (NS_FAILED(rv)) { String err(NS_LITERAL_STRING("bad name for xsl:variable")); aPs->receiveError(err, NS_ERROR_FAILURE); } } TX_IF_RELEASE_ATOM(localName); } void txXSLTProcessor::processAttributeSets(Element* aElement, ProcessorState* aPs, Stack* aRecursionStack) { nsresult rv = NS_OK; String names; PRInt32 namespaceID; if (aElement->getNamespaceID() == kNameSpaceID_XSLT) namespaceID = kNameSpaceID_None; else namespaceID = kNameSpaceID_XSLT; if (!aElement->getAttr(txXSLTAtoms::useAttributeSets, namespaceID, names) || names.IsEmpty()) return; // Split names txTokenizer tokenizer(names); String nameStr; while (tokenizer.hasMoreTokens()) { tokenizer.nextToken(nameStr); txExpandedName name; rv = name.init(nameStr, aElement, MB_FALSE); if (NS_FAILED(rv)) { String err(NS_LITERAL_STRING("missing or malformed name in use-attribute-sets")); aPs->receiveError(err); return; } if (aRecursionStack) { txStackIterator attributeSets(aRecursionStack); while (attributeSets.hasNext()) { if (name == *(txExpandedName*)attributeSets.next()) { String err(NS_LITERAL_STRING("circular inclusion detected in use-attribute-sets")); aPs->receiveError(err); return; } } } NodeSet* attSet = aPs->getAttributeSet(name); if (attSet) { int i; //-- issue: we still need to handle the following fix cleaner, since //-- attribute sets are merged, a different parent could exist //-- for different xsl:attribute nodes. I will probably create //-- an AttributeSet object, which will handle this case better. - Keith V. if (attSet->size() > 0) { Element* parent = (Element*) attSet->get(0)->getXPathParent(); if (aRecursionStack) { aRecursionStack->push(&name); processAttributeSets(parent, aPs, aRecursionStack); aRecursionStack->pop(); } else { Stack recursionStack; recursionStack.push(&name); processAttributeSets(parent, aPs, &recursionStack); recursionStack.pop(); } } for (i = 0; i < attSet->size(); i++) processAction(attSet->get(i), aPs); delete attSet; } } } void txXSLTProcessor::processChildren(Element* aElement, ProcessorState* aPs) { NS_ASSERTION(aElement, "missing aElement"); txVariableMap* oldVars = aPs->getLocalVariables(); txVariableMap localVars(oldVars); aPs->setLocalVariables(&localVars); Node* child = aElement->getFirstChild(); while (child) { processAction(child, aPs); child = child->getNextSibling(); } aPs->setLocalVariables(oldVars); } void txXSLTProcessor::processChildrenAsValue(Element* aElement, ProcessorState* aPs, MBool aOnlyText, String& aValue) { txXMLEventHandler* previousHandler = aPs->mResultHandler; txTextHandler valueHandler(aValue, aOnlyText); aPs->mResultHandler = &valueHandler; processChildren(aElement, aPs); aPs->mResultHandler = previousHandler; } void txXSLTProcessor::processDefaultTemplate(ProcessorState* aPs, const txExpandedName& aMode) { Node* node = aPs->getEvalContext()->getContextNode(); switch (node->getNodeType()) { case Node::ELEMENT_NODE : case Node::DOCUMENT_NODE : { if (!gNodeExpr) break; ExprResult* exprResult = gNodeExpr->evaluate(aPs->getEvalContext()); if (!exprResult || exprResult->getResultType() != ExprResult::NODESET) { String err(NS_LITERAL_STRING("None-nodeset returned while processing default template")); aPs->receiveError(err, NS_ERROR_FAILURE); delete exprResult; return; } NodeSet* nodeSet = (NodeSet*)exprResult; if (nodeSet->isEmpty()) { delete nodeSet; return; } txNodeSetContext evalContext(nodeSet, aPs); txIEvalContext* priorEC = aPs->setEvalContext(&evalContext); while (evalContext.hasNext()) { evalContext.next(); Node* currNode = evalContext.getContextNode(); ProcessorState::ImportFrame *frame; Node* xslTemplate = aPs->findTemplate(currNode, aMode, &frame); processMatchedTemplate(xslTemplate, 0, aMode, frame, aPs); } aPs->setEvalContext(priorEC); delete exprResult; break; } case Node::ATTRIBUTE_NODE : case Node::TEXT_NODE : case Node::CDATA_SECTION_NODE : { NS_ASSERTION(aPs->mResultHandler, "mResultHandler must not be NULL!"); aPs->mResultHandler->characters(node->getNodeValue()); break; } default: // on all other nodetypes (including namespace nodes) // we do nothing break; } } void txXSLTProcessor::processInclude(String& aHref, txListIterator* aImportFrame, ProcessorState* aPs) { // make sure the include isn't included yet txStackIterator iter(aPs->getEnteredStylesheets()); while (iter.hasNext()) { if (((String*)iter.next())->Equals(aHref)) { String err(NS_LITERAL_STRING("Stylesheet includes itself. URI: ")); err.Append(aHref); aPs->receiveError(err, NS_ERROR_FAILURE); return; } } aPs->getEnteredStylesheets()->push(&aHref); // Load XSL document Node* stylesheet = aPs->retrieveDocument(aHref, String()); if (!stylesheet) { String err(NS_LITERAL_STRING("Unable to load included stylesheet ")); err.Append(aHref); aPs->receiveError(err, NS_ERROR_FAILURE); aPs->getEnteredStylesheets()->pop(); return; } switch(stylesheet->getNodeType()) { case Node::DOCUMENT_NODE : processStylesheet((Document*)stylesheet, 0, aImportFrame, aPs); break; case Node::ELEMENT_NODE : processTopLevel((Element*)stylesheet, 0, aImportFrame, aPs); break; default: // This should never happen String err(NS_LITERAL_STRING("Unsupported fragment identifier")); aPs->receiveError(err, NS_ERROR_FAILURE); break; } aPs->getEnteredStylesheets()->pop(); } void txXSLTProcessor::processMatchedTemplate(Node* aTemplate, txVariableMap* aParams, const txExpandedName& aMode, ProcessorState::ImportFrame* aFrame, ProcessorState* aPs) { if (aTemplate) { ProcessorState::TemplateRule *oldTemplate, newTemplate; oldTemplate = aPs->getCurrentTemplateRule(); newTemplate.mFrame = aFrame; newTemplate.mMode = &aMode; newTemplate.mParams = aParams; aPs->setCurrentTemplateRule(&newTemplate); processTemplate(aTemplate, aParams, aPs); aPs->setCurrentTemplateRule(oldTemplate); } else { processDefaultTemplate(aPs, aMode); } } nsresult txXSLTProcessor::processParameters(Element* aAction, txVariableMap* aMap, ProcessorState* aPs) { NS_ASSERTION(aAction && aMap, "missing argument"); nsresult rv = NS_OK; Node* tmpNode = aAction->getFirstChild(); while (tmpNode) { if (tmpNode->getNodeType() == Node::ELEMENT_NODE && tmpNode->getNamespaceID() == kNameSpaceID_XSLT) { txAtom* localName; tmpNode->getLocalName(&localName); if (localName != txXSLTAtoms::withParam) { TX_IF_RELEASE_ATOM(localName); tmpNode = tmpNode->getNextSibling(); continue; } Element* action = (Element*)tmpNode; txExpandedName paramName; String qName; action->getAttr(txXSLTAtoms::name, kNameSpaceID_None, qName); rv = paramName.init(qName, action, MB_FALSE); if (NS_FAILED(rv)) { String err(NS_LITERAL_STRING("bad name for xsl:param")); aPs->receiveError(err, NS_ERROR_FAILURE); break; } ExprResult* exprResult = processVariable(action, aPs); if (!exprResult) { return NS_ERROR_FAILURE; } rv = aMap->bindVariable(paramName, exprResult, MB_TRUE); if (NS_FAILED(rv)) { String err(NS_LITERAL_STRING("Unable to bind parameter '")); err.Append(qName); err.Append(PRUnichar('\'')); aPs->receiveError(err, NS_ERROR_FAILURE); return rv; } TX_RELEASE_ATOM(localName); } tmpNode = tmpNode->getNextSibling(); } return NS_OK; } nsresult txXSLTProcessor::processStylesheet(Document* aStylesheet, txExpandedNameMap* aGlobalParams, ProcessorState* aPs) { txListIterator importFrame(aPs->getImportFrames()); importFrame.addAfter(new ProcessorState::ImportFrame(0)); if (!importFrame.next()) { return NS_ERROR_OUT_OF_MEMORY; } processStylesheet(aStylesheet, aGlobalParams, &importFrame, aPs); return NS_OK; } void txXSLTProcessor::processStylesheet(Document* aStylesheet, txExpandedNameMap* aGlobalParams, txListIterator* aImportFrame, ProcessorState* aPs) { NS_ASSERTION(aStylesheet, "processTopLevel called without stylesheet"); if (!aStylesheet || !aStylesheet->getDocumentElement()) return; Element* elem = aStylesheet->getDocumentElement(); txAtom* localName; PRInt32 namespaceID = elem->getNamespaceID(); elem->getLocalName(&localName); if (((localName == txXSLTAtoms::stylesheet) || (localName == txXSLTAtoms::transform)) && (namespaceID == kNameSpaceID_XSLT)) { processTopLevel(elem, aGlobalParams, aImportFrame, aPs); } else { NS_ASSERTION(aImportFrame->current(), "no current importframe"); if (!aImportFrame->current()) { TX_IF_RELEASE_ATOM(localName); return; } aPs->addLREStylesheet(aStylesheet, (ProcessorState::ImportFrame*)aImportFrame->current()); } TX_IF_RELEASE_ATOM(localName); } void txXSLTProcessor::processTemplate(Node* aTemplate, txVariableMap* aParams, ProcessorState* aPs) { NS_ASSERTION(aTemplate, "aTemplate is NULL"); nsresult rv; txVariableMap* oldVars = aPs->getLocalVariables(); txVariableMap localVars(0); aPs->setLocalVariables(&localVars); // handle params Node* tmpNode = aTemplate->getFirstChild(); while (tmpNode) { int nodeType = tmpNode->getNodeType(); if (nodeType == Node::ELEMENT_NODE) { txAtom* localName; tmpNode->getLocalName(&localName); if (tmpNode->getNamespaceID() != kNameSpaceID_XSLT || localName != txXSLTAtoms::param) { TX_RELEASE_ATOM(localName); break; } TX_RELEASE_ATOM(localName); Element* action = (Element*)tmpNode; txExpandedName paramName; String qName; action->getAttr(txXSLTAtoms::name, kNameSpaceID_None, qName); rv = paramName.init(qName, action, MB_FALSE); if (NS_FAILED(rv)) { String err(NS_LITERAL_STRING("bad name for xsl:param")); aPs->receiveError(err, NS_ERROR_FAILURE); break; } ExprResult* exprResult; if (aParams && (exprResult = aParams->getVariable(paramName))) { rv = localVars.bindVariable(paramName, exprResult, MB_FALSE); } else { exprResult = processVariable(action, aPs); if (!exprResult) break; rv = localVars.bindVariable(paramName, exprResult, MB_TRUE); } if (NS_FAILED(rv)) { String err(NS_LITERAL_STRING("unable to bind xsl:param")); aPs->receiveError(err, NS_ERROR_FAILURE); } } else if (!(nodeType == Node::COMMENT_NODE || ((nodeType == Node::TEXT_NODE || nodeType == Node::CDATA_SECTION_NODE) && XMLUtils::isWhitespace(tmpNode->getNodeValue())))) { break; } tmpNode = tmpNode->getNextSibling(); } // execute contents while (tmpNode) { processAction(tmpNode, aPs); tmpNode = tmpNode->getNextSibling(); } aPs->setLocalVariables(oldVars); } nsresult txXSLTProcessor::processTopLevel(Element* aStylesheet, txExpandedNameMap* aGlobalParams, ProcessorState* aPs) { txListIterator importFrame(aPs->getImportFrames()); importFrame.addAfter(new ProcessorState::ImportFrame(0)); if (!importFrame.next()) { return NS_ERROR_OUT_OF_MEMORY; } processTopLevel(aStylesheet, aGlobalParams, &importFrame, aPs); return NS_OK; } void txXSLTProcessor::processTopLevel(Element* aStylesheet, txExpandedNameMap* aGlobalParams, txListIterator* importFrame, ProcessorState* aPs) { // Index templates and process top level xslt elements NS_ASSERTION(aStylesheet, "processTopLevel called without stylesheet element"); if (!aStylesheet) return; ProcessorState::ImportFrame* currentFrame = (ProcessorState::ImportFrame*)importFrame->current(); NS_ASSERTION(currentFrame, "processTopLevel called with no current importframe"); if (!currentFrame) return; MBool importsDone = MB_FALSE; Node* node = aStylesheet->getFirstChild(); while (node && !importsDone) { if (node->getNodeType() == Node::ELEMENT_NODE) { txAtom* localName; node->getLocalName(&localName); if (node->getNamespaceID() == kNameSpaceID_XSLT && localName == txXSLTAtoms::import) { Element* element = (Element*)node; String hrefAttr, href; element->getAttr(txXSLTAtoms::href, kNameSpaceID_None, hrefAttr); URIUtils::resolveHref(hrefAttr, element->getBaseURI(), href); // Create a new ImportFrame with correct firstNotImported ProcessorState::ImportFrame *nextFrame, *newFrame; nextFrame = (ProcessorState::ImportFrame*)importFrame->next(); newFrame = new ProcessorState::ImportFrame(nextFrame); if (!newFrame) { // XXX ErrorReport: out of memory break; } // Insert frame and process stylesheet importFrame->addBefore(newFrame); importFrame->previous(); processInclude(href, importFrame, aPs); // Restore iterator to initial position importFrame->previous(); } else { importsDone = MB_TRUE; } TX_IF_RELEASE_ATOM(localName); } if (!importsDone) node = node->getNextSibling(); } while (node) { if (node->getNodeType() != Node::ELEMENT_NODE || node->getNamespaceID() != kNameSpaceID_XSLT) { node = node->getNextSibling(); continue; } txAtom* localName; node->getLocalName(&localName); Element* element = (Element*)node; // xsl:attribute-set if (localName == txXSLTAtoms::attributeSet) { aPs->addAttributeSet(element, currentFrame); } // xsl:decimal-format else if (localName == txXSLTAtoms::decimalFormat) { if (!aPs->addDecimalFormat(element)) { // Add error to ErrorObserver String fName; element->getAttr(txXSLTAtoms::name, kNameSpaceID_None, fName); String err(NS_LITERAL_STRING("unable to add ")); if (fName.IsEmpty()) { err.Append(NS_LITERAL_STRING("default")); } else { err.Append(PRUnichar('\"')); err.Append(fName); err.Append(PRUnichar('\"')); } err.Append(NS_LITERAL_STRING(" decimal format for xsl:decimal-format")); aPs->receiveError(err, NS_ERROR_FAILURE); } } // xsl:param else if (localName == txXSLTAtoms::param) { String qName; element->getAttr(txXSLTAtoms::name, kNameSpaceID_None, qName); txExpandedName varName; nsresult rv = varName.init(qName, element, MB_FALSE); if (NS_SUCCEEDED(rv)) { ExprResult* defaultValue = 0; if (aGlobalParams) { txIGlobalParameter* globalParam = (txIGlobalParameter*)aGlobalParams->get(varName); if (globalParam) { globalParam->getValue(&defaultValue); } } rv = aPs->addGlobalVariable(varName, element, currentFrame, defaultValue); if (NS_FAILED(rv)) { String err(NS_LITERAL_STRING("unable to add global xsl:param")); aPs->receiveError(err, NS_ERROR_FAILURE); } } else { String err(NS_LITERAL_STRING("unable to add global xsl:param")); aPs->receiveError(err, NS_ERROR_FAILURE); } } // xsl:import else if (localName == txXSLTAtoms::import) { String err(NS_LITERAL_STRING("xsl:import only allowed at top of stylesheet")); aPs->receiveError(err, NS_ERROR_FAILURE); } // xsl:include else if (localName == txXSLTAtoms::include) { String hrefAttr, href; element->getAttr(txXSLTAtoms::href, kNameSpaceID_None, hrefAttr); URIUtils::resolveHref(hrefAttr, element->getBaseURI(), href); processInclude(href, importFrame, aPs); } // xsl:key else if (localName == txXSLTAtoms::key) { if (!aPs->addKey(element)) { String name; element->getAttr(txXSLTAtoms::name, kNameSpaceID_None, name); String err(NS_LITERAL_STRING("error adding key '")); err.Append(name); err.Append(PRUnichar('\'')); aPs->receiveError(err, NS_ERROR_FAILURE); } } // xsl:output else if (localName == txXSLTAtoms::output) { txOutputFormat& format = currentFrame->mOutputFormat; String attValue; if (element->getAttr(txXSLTAtoms::method, kNameSpaceID_None, attValue)) { if (attValue.getConstNSString().Equals(NS_LITERAL_STRING("html"))) { format.mMethod = eHTMLOutput; } else if (attValue.getConstNSString().Equals(NS_LITERAL_STRING("text"))) { format.mMethod = eTextOutput; } else { format.mMethod = eXMLOutput; } } if (element->getAttr(txXSLTAtoms::version, kNameSpaceID_None, attValue)) { format.mVersion = attValue; } if (element->getAttr(txXSLTAtoms::encoding, kNameSpaceID_None, attValue)) { format.mEncoding = attValue; } if (element->getAttr(txXSLTAtoms::omitXmlDeclaration, kNameSpaceID_None, attValue)) { format.mOmitXMLDeclaration = attValue.Equals(YES_VALUE) ? eTrue : eFalse; } if (element->getAttr(txXSLTAtoms::standalone, kNameSpaceID_None, attValue)) { format.mStandalone = attValue.Equals(YES_VALUE) ? eTrue : eFalse; } if (element->getAttr(txXSLTAtoms::doctypePublic, kNameSpaceID_None, attValue)) { format.mPublicId = attValue; } if (element->getAttr(txXSLTAtoms::doctypeSystem, kNameSpaceID_None, attValue)) { format.mSystemId = attValue; } if (element->getAttr(txXSLTAtoms::cdataSectionElements, kNameSpaceID_None, attValue)) { txTokenizer tokens(attValue); String token; while (tokens.hasMoreTokens()) { tokens.nextToken(token); if (!XMLUtils::isValidQName(token)) { break; } String namePart; XMLUtils::getPrefix(token, namePart); txAtom* nameAtom = TX_GET_ATOM(namePart); PRInt32 nsID = element->lookupNamespaceID(nameAtom); TX_IF_RELEASE_ATOM(nameAtom); if (nsID == kNameSpaceID_Unknown) { // XXX ErrorReport: unknown prefix break; } XMLUtils::getLocalPart(token, namePart); nameAtom = TX_GET_ATOM(namePart); if (!nameAtom) { // XXX ErrorReport: out of memory break; } txExpandedName* qname = new txExpandedName(nsID, nameAtom); TX_RELEASE_ATOM(nameAtom); if (!qname) { // XXX ErrorReport: out of memory break; } format.mCDATASectionElements.add(qname); } } if (element->getAttr(txXSLTAtoms::indent, kNameSpaceID_None, attValue)) { format.mIndent = attValue.Equals(YES_VALUE) ? eTrue : eFalse; } if (element->getAttr(txXSLTAtoms::mediaType, kNameSpaceID_None, attValue)) { format.mMediaType = attValue; } } // xsl:template else if (localName == txXSLTAtoms::_template) { aPs->addTemplate(element, currentFrame); } // xsl:variable else if (localName == txXSLTAtoms::variable) { String qName; element->getAttr(txXSLTAtoms::name, kNameSpaceID_None, qName); txExpandedName varName; nsresult rv = varName.init(qName, element, MB_FALSE); if (NS_SUCCEEDED(rv)) { rv = aPs->addGlobalVariable(varName, element, currentFrame, 0); if (NS_FAILED(rv)) { String err(NS_LITERAL_STRING("unable to add global xsl:variable")); aPs->receiveError(err, NS_ERROR_FAILURE); } } else { String err(NS_LITERAL_STRING("unable to add global xsl:variable")); aPs->receiveError(err, NS_ERROR_FAILURE); } } // xsl:preserve-space else if (localName == txXSLTAtoms::preserveSpace) { String elements; if (!element->getAttr(txXSLTAtoms::elements, kNameSpaceID_None, elements)) { //-- add error to ErrorObserver String err(NS_LITERAL_STRING("missing required 'elements' attribute for ")); err.Append(NS_LITERAL_STRING("xsl:preserve-space")); aPs->receiveError(err, NS_ERROR_FAILURE); } else { aPs->shouldStripSpace(elements, element, MB_FALSE, currentFrame); } } // xsl:strip-space else if (localName == txXSLTAtoms::stripSpace) { String elements; if (!element->getAttr(txXSLTAtoms::elements, kNameSpaceID_None, elements)) { //-- add error to ErrorObserver String err(NS_LITERAL_STRING("missing required 'elements' attribute for ")); err.Append(NS_LITERAL_STRING("xsl:strip-space")); aPs->receiveError(err, NS_ERROR_FAILURE); } else { aPs->shouldStripSpace(elements, element, MB_TRUE, currentFrame); } } TX_IF_RELEASE_ATOM(localName); node = node->getNextSibling(); } } ExprResult* txXSLTProcessor::processVariable(Element* aVariable, ProcessorState* aPs) { NS_ASSERTION(aVariable, "missing xslVariable"); //-- check for select attribute if (aVariable->hasAttr(txXSLTAtoms::select, kNameSpaceID_None)) { Expr* expr = aPs->getExpr(aVariable, ProcessorState::SelectAttr); if (!expr) return new StringResult(NS_LITERAL_STRING("unable to process variable")); return expr->evaluate(aPs->getEvalContext()); } if (aVariable->hasChildNodes()) { txResultTreeFragment* rtf = new txResultTreeFragment(); if (!rtf) // XXX ErrorReport: Out of memory return 0; txXMLEventHandler* previousHandler = aPs->mResultHandler; Document* rtfDocument = aPs->getRTFDocument(); if (!rtfDocument) { rtfDocument = createRTFDocument(eXMLOutput); aPs->setRTFDocument(rtfDocument); } NS_ASSERTION(rtfDocument, "No document to create result tree fragments"); if (!rtfDocument) { return 0; } txRtfHandler rtfHandler(rtfDocument, rtf); aPs->mResultHandler = &rtfHandler; processChildren(aVariable, aPs); NS_ASSERTION(previousHandler, "Setting mResultHandler to NULL!"); aPs->mResultHandler = previousHandler; return rtf; } return new StringResult(); } void txXSLTProcessor::transform(ProcessorState* aPs) { nsresult rv = NS_OK; txListIterator frameIter(aPs->getImportFrames()); ProcessorState::ImportFrame* frame; txOutputFormat* outputFormat = aPs->getOutputFormat(); while ((frame = (ProcessorState::ImportFrame*)frameIter.next())) { outputFormat->merge(frame->mOutputFormat); } txIOutputXMLEventHandler* handler = 0; rv = aPs->mOutputHandlerFactory->createHandlerWith(aPs->getOutputFormat(), &handler); if (NS_FAILED(rv)) { return; } aPs->mOutputHandler = handler; aPs->mResultHandler = handler; aPs->mOutputHandler->startDocument(); frame = 0; txExpandedName nullMode; Node* xslTemplate = aPs->findTemplate(aPs->getEvalContext()->getContextNode(), nullMode, &frame); processMatchedTemplate(xslTemplate, 0, nullMode, frame, aPs); aPs->mOutputHandler->endDocument(); } void txXSLTProcessor::xslCopy(Element* aAction, ProcessorState* aPs) { Node* node = aPs->getEvalContext()->getContextNode(); switch (node->getNodeType()) { case Node::DOCUMENT_NODE: { // Just process children processChildren(aAction, aPs); break; } case Node::ELEMENT_NODE: { Element* element = (Element*)node; String nodeName = element->getNodeName(); PRInt32 nsID = element->getNamespaceID(); NS_ASSERTION(aPs->mResultHandler, "mResultHandler must not be NULL!"); aPs->mResultHandler->startElement(nodeName, nsID); // XXX copy namespace attributes once we have them processAttributeSets(aAction, aPs); processChildren(aAction, aPs); NS_ASSERTION(aPs->mResultHandler, "mResultHandler must not be NULL!"); aPs->mResultHandler->endElement(nodeName, nsID); break; } default: { copyNode(node, aPs); } } } void txXSLTProcessor::xslCopyOf(ExprResult* aExprResult, ProcessorState* aPs) { if (!aExprResult) return; switch (aExprResult->getResultType()) { case ExprResult::NODESET: { NodeSet* nodes = (NodeSet*)aExprResult; int i; for (i = 0; i < nodes->size(); i++) { Node* node = nodes->get(i); copyNode(node, aPs); } break; } default: { String value; aExprResult->stringValue(value); NS_ASSERTION(aPs->mResultHandler, "mResultHandler must not be NULL!"); aPs->mResultHandler->characters(value); } } }