From 2cc84a5fd380b4b48484679ecfda4fa6d2ae1798 Mon Sep 17 00:00:00 2001 From: "cvshook%sicking.cc" Date: Fri, 19 May 2006 10:29:43 +0000 Subject: [PATCH] Bug 194387: Support parameter-passing in PI XSLT transformations. r/sr=peterv --- content/base/src/nsGkAtomList.h | 1 + content/xml/document/src/nsXMLContentSink.cpp | 58 ++++++ content/xslt/public/nsIDocumentTransformer.h | 8 + content/xslt/src/base/txNamespaceMap.cpp | 18 +- content/xslt/src/base/txNamespaceMap.h | 2 +- .../xslt/src/xslt/txMozillaXSLTProcessor.cpp | 168 +++++++++++++++++- .../xslt/src/xslt/txMozillaXSLTProcessor.h | 11 ++ .../xslt/src/xslt/txStylesheetCompiler.cpp | 6 +- 8 files changed, 261 insertions(+), 11 deletions(-) diff --git a/content/base/src/nsGkAtomList.h b/content/base/src/nsGkAtomList.h index 44b5608a85b..06e84903d84 100755 --- a/content/base/src/nsGkAtomList.h +++ b/content/base/src/nsGkAtomList.h @@ -619,6 +619,7 @@ GK_ATOM(pre, "pre") GK_ATOM(preceding, "preceding") GK_ATOM(precedingSibling, "preceding-sibling") GK_ATOM(predicate, "predicate") +GK_ATOM(prefix, "prefix") GK_ATOM(preserve, "preserve") GK_ATOM(preserveSpace, "preserve-space") GK_ATOM(preventdefault, "preventdefault") diff --git a/content/xml/document/src/nsXMLContentSink.cpp b/content/xml/document/src/nsXMLContentSink.cpp index c077f5e4be5..7b0ef1c89ed 100644 --- a/content/xml/document/src/nsXMLContentSink.cpp +++ b/content/xml/document/src/nsXMLContentSink.cpp @@ -96,6 +96,7 @@ #include "nsIContentPolicy.h" #include "nsContentPolicyUtils.h" #include "nsContentErrors.h" +#include "nsIDOMProcessingInstruction.h" #ifdef MOZ_SVG #include "nsSVGAtoms.h" @@ -230,6 +231,48 @@ nsXMLContentSink::MaybePrettyPrint() return printer->PrettyPrint(mDocument); } +static void +CheckXSLTParamPI(nsIDOMProcessingInstruction* aPi, + nsIDocumentTransformer* aProcessor, + nsIDocument* aDocument) +{ + nsAutoString target, data; + aPi->GetTarget(target); + + // Check for namespace declarations + if (target.EqualsLiteral("xslt-param-namespace")) { + aPi->GetData(data); + nsAutoString prefix, namespaceAttr; + nsParserUtils::GetQuotedAttributeValue(data, nsGkAtoms::prefix, + prefix); + if (!prefix.IsEmpty() && + nsParserUtils::GetQuotedAttributeValue(data, nsGkAtoms::_namespace, + namespaceAttr)) { + aProcessor->AddXSLTParamNamespace(prefix, namespaceAttr); + } + } + + // Check for actual parameters + else if (target.EqualsLiteral("xslt-param")) { + aPi->GetData(data); + nsAutoString name, namespaceAttr, select, value; + nsParserUtils::GetQuotedAttributeValue(data, nsGkAtoms::name, + name); + nsParserUtils::GetQuotedAttributeValue(data, nsGkAtoms::_namespace, + namespaceAttr); + if (!nsParserUtils::GetQuotedAttributeValue(data, nsGkAtoms::select, select)) { + select.SetIsVoid(PR_TRUE); + } + if (!nsParserUtils::GetQuotedAttributeValue(data, nsGkAtoms::value, value)) { + value.SetIsVoid(PR_TRUE); + } + if (!name.IsEmpty()) { + nsCOMPtr doc = do_QueryInterface(aDocument); + aProcessor->AddXSLTParam(name, namespaceAttr, select, value, doc); + } + } +} + NS_IMETHODIMP nsXMLContentSink::DidBuildModel() { @@ -239,6 +282,21 @@ nsXMLContentSink::DidBuildModel() } if (mXSLTProcessor) { + + // Check for xslt-param and xslt-param-namespace PIs + PRUint32 i; + nsIContent* child; + for (i = 0; (child = mDocument->GetChildAt(i)); ++i) { + if (child->IsContentOfType(nsIContent::ePROCESSING_INSTRUCTION)) { + nsCOMPtr pi = do_QueryInterface(child); + CheckXSLTParamPI(pi, mXSLTProcessor, mDocument); + } + else if (child->IsContentOfType(nsIContent::eELEMENT)) { + // Only honor PIs in the prolog + break; + } + } + nsCOMPtr currentDOMDoc(do_QueryInterface(mDocument)); mXSLTProcessor->SetSourceContentModel(currentDOMDoc); // Since the processor now holds a reference to us we drop our reference diff --git a/content/xslt/public/nsIDocumentTransformer.h b/content/xslt/public/nsIDocumentTransformer.h index 48c62afdb0a..2e21f60bfee 100644 --- a/content/xslt/public/nsIDocumentTransformer.h +++ b/content/xslt/public/nsIDocumentTransformer.h @@ -80,6 +80,14 @@ public: nsIPrincipal* aCallerPrincipal) = 0; NS_IMETHOD SetSourceContentModel(nsIDOMNode* aSource) = 0; NS_IMETHOD CancelLoads() = 0; + + NS_IMETHOD AddXSLTParamNamespace(const nsString& aPrefix, + const nsString& aNamespace) = 0; + NS_IMETHOD AddXSLTParam(const nsString& aName, + const nsString& aNamespace, + const nsString& aValue, + const nsString& aSelect, + nsIDOMNode* aContextNode) = 0; }; NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocumentTransformer, diff --git a/content/xslt/src/base/txNamespaceMap.cpp b/content/xslt/src/base/txNamespaceMap.cpp index b40871900f3..077cb64d6b9 100644 --- a/content/xslt/src/base/txNamespaceMap.cpp +++ b/content/xslt/src/base/txNamespaceMap.cpp @@ -51,12 +51,24 @@ txNamespaceMap::txNamespaceMap(const txNamespaceMap& aOther) } nsresult -txNamespaceMap::addNamespace(nsIAtom* aPrefix, const nsAString& aNamespaceURI) +txNamespaceMap::mapNamespace(nsIAtom* aPrefix, const nsAString& aNamespaceURI) { - nsIAtom* prefix = aPrefix == txXMLAtoms::_empty ? 0 : aPrefix; + nsIAtom* prefix = aPrefix == txXMLAtoms::_empty ? nsnull : aPrefix; PRInt32 nsId; - if (!prefix && aNamespaceURI.IsEmpty()) { + if (prefix && aNamespaceURI.IsEmpty()) { + // Remove the mapping + PRInt32 index = mPrefixes.IndexOf(prefix); + if (index >= 0) { + mPrefixes.RemoveObjectAt(index); + mNamespaces.RemoveElementAt(index); + } + + return NS_OK; + } + + if (aNamespaceURI.IsEmpty()) { + // Set default to empty namespace nsId = kNameSpaceID_None; } else { diff --git a/content/xslt/src/base/txNamespaceMap.h b/content/xslt/src/base/txNamespaceMap.h index 040ab8a0bfc..766d709daf4 100644 --- a/content/xslt/src/base/txNamespaceMap.h +++ b/content/xslt/src/base/txNamespaceMap.h @@ -62,7 +62,7 @@ public: return mRefCnt; } - nsresult addNamespace(nsIAtom* aPrefix, const nsAString& aNamespaceURI); + nsresult mapNamespace(nsIAtom* aPrefix, const nsAString& aNamespaceURI); PRInt32 lookupNamespace(nsIAtom* aPrefix); PRInt32 lookupNamespace(const nsAString& aPrefix); PRInt32 lookupNamespaceWithDefault(const nsAString& aPrefix); diff --git a/content/xslt/src/xslt/txMozillaXSLTProcessor.cpp b/content/xslt/src/xslt/txMozillaXSLTProcessor.cpp index 2e2aee38985..1f049f775fd 100644 --- a/content/xslt/src/xslt/txMozillaXSLTProcessor.cpp +++ b/content/xslt/src/xslt/txMozillaXSLTProcessor.cpp @@ -65,6 +65,7 @@ #include "nsIPrincipal.h" #include "nsThreadUtils.h" #include "jsapi.h" +#include "txExprParser.h" static NS_DEFINE_CID(kXMLDocumentCID, NS_XMLDOCUMENT_CID); @@ -242,13 +243,17 @@ txToFragmentHandlerFactory::createHandlerWith(txOutputFormat* aFormat, class txVariable : public txIGlobalParameter { public: - txVariable(nsIVariant *aValue) : mValue(aValue), - mTxValue(nsnull) + txVariable(nsIVariant *aValue) : mValue(aValue) { + NS_ASSERTION(aValue, "missing value"); + } + txVariable(txAExprResult* aValue) : mTxValue(aValue) + { + NS_ASSERTION(aValue, "missing value"); } nsresult getValue(txAExprResult** aValue) { - NS_ASSERTION(mValue, "variablevalue is null"); + NS_ASSERTION(mValue || mTxValue, "variablevalue is null"); if (!mTxValue) { nsresult rv = Convert(mValue, getter_AddRefs(mTxValue)); @@ -272,6 +277,12 @@ public: mValue = aValue; mTxValue = nsnull; } + void setValue(txAExprResult* aValue) + { + NS_ASSERTION(aValue, "setting variablevalue to null"); + mValue = nsnull; + mTxValue = aValue; + } private: static nsresult Convert(nsIVariant *aValue, txAExprResult** aResult); @@ -364,6 +375,155 @@ txMozillaXSLTProcessor::SetSourceContentModel(nsIDOMNode* aSourceDOM) return NS_OK; } +NS_IMETHODIMP +txMozillaXSLTProcessor::AddXSLTParamNamespace(const nsString& aPrefix, + const nsString& aNamespace) +{ + nsCOMPtr pre = do_GetAtom(aPrefix); + return mParamNamespaceMap.mapNamespace(pre, aNamespace); +} + + +class txXSLTParamContext : public txIParseContext, + public txIEvalContext +{ +public: + txXSLTParamContext(txNamespaceMap *aResolver, txXPathNode& aContext, + txResultRecycler* aRecycler) + : mResolver(aResolver), + mContext(aContext), + mRecycler(aRecycler) + { + } + + // txIParseContext + nsresult resolveNamespacePrefix(nsIAtom* aPrefix, PRInt32& aID) + { + aID = mResolver->lookupNamespace(aPrefix); + return aID == kNameSpaceID_Unknown ? NS_ERROR_DOM_NAMESPACE_ERR : + NS_OK; + } + nsresult resolveFunctionCall(nsIAtom* aName, PRInt32 aID, + FunctionCall*& aFunction) + { + return NS_ERROR_XPATH_UNKNOWN_FUNCTION; + } + PRBool caseInsensitiveNameTests() + { + return PR_FALSE; + } + void SetErrorOffset(PRUint32 aOffset) + { + } + + // txIEvalContext + nsresult getVariable(PRInt32 aNamespace, nsIAtom* aLName, + txAExprResult*& aResult) + { + aResult = nsnull; + return NS_ERROR_INVALID_ARG; + } + PRBool isStripSpaceAllowed(const txXPathNode& aNode) + { + return PR_FALSE; + } + void* getPrivateContext() + { + return nsnull; + } + txResultRecycler* recycler() + { + return mRecycler; + } + void receiveError(const nsAString& aMsg, nsresult aRes) + { + } + const txXPathNode& getContextNode() + { + return mContext; + } + PRUint32 size() + { + return 1; + } + PRUint32 position() + { + return 1; + } + +private: + txNamespaceMap *mResolver; + txXPathNode& mContext; + txResultRecycler* mRecycler; + +}; + + +NS_IMETHODIMP +txMozillaXSLTProcessor::AddXSLTParam(const nsString& aName, + const nsString& aNamespace, + const nsString& aSelect, + const nsString& aValue, + nsIDOMNode* aContext) +{ + nsresult rv = NS_OK; + + if (aSelect.IsVoid() == aValue.IsVoid()) { + // Ignore if neither or both are specified + return NS_ERROR_FAILURE; + } + + nsRefPtr value; + if (!aSelect.IsVoid()) { + + // Set up context + nsAutoPtr contextNode = + txXPathNativeNode::createXPathNode(aContext); + NS_ENSURE_TRUE(contextNode, NS_ERROR_OUT_OF_MEMORY); + + if (!mRecycler) { + mRecycler = new txResultRecycler; + NS_ENSURE_TRUE(mRecycler, NS_ERROR_OUT_OF_MEMORY); + } + + txXSLTParamContext paramContext(&mParamNamespaceMap, *contextNode, + mRecycler); + + // Parse + nsAutoPtr expr; + rv = txExprParser::createExpr(aSelect, ¶mContext, + getter_Transfers(expr)); + NS_ENSURE_SUCCESS(rv, rv); + + // Evaluate + rv = expr->evaluate(¶mContext, getter_AddRefs(value)); + NS_ENSURE_SUCCESS(rv, rv); + } + else { + value = new StringResult(aValue, nsnull); + NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY); + } + + nsCOMPtr name = do_GetAtom(aName); + PRInt32 nsId = kNameSpaceID_Unknown; + rv = nsContentUtils::NameSpaceManager()-> + RegisterNameSpace(aNamespace, nsId); + NS_ENSURE_SUCCESS(rv, rv); + + txExpandedName varName(nsId, name); + txVariable* var = (txVariable*)mVariables.get(varName); + if (var) { + var->setValue(value); + + return NS_OK; + } + + var = new txVariable(value); + NS_ENSURE_TRUE(var, NS_ERROR_OUT_OF_MEMORY); + + return mVariables.add(varName, var); +} + class nsTransformBlockerEvent : public nsRunnable { public: nsRefPtr mProcessor; @@ -755,7 +915,7 @@ txMozillaXSLTProcessor::SetParameter(const nsAString & aNamespaceURI, nsCOMPtr localName = do_GetAtom(aLocalName); txExpandedName varName(nsId, localName); - txVariable* var = (txVariable*)mVariables.get(varName); + txVariable* var = NS_STATIC_CAST(txVariable*, mVariables.get(varName)); if (var) { var->setValue(value); return NS_OK; diff --git a/content/xslt/src/xslt/txMozillaXSLTProcessor.h b/content/xslt/src/xslt/txMozillaXSLTProcessor.h index fbd4e6461b1..8769d182c9c 100644 --- a/content/xslt/src/xslt/txMozillaXSLTProcessor.h +++ b/content/xslt/src/xslt/txMozillaXSLTProcessor.h @@ -45,12 +45,14 @@ #include "nsIXSLTProcessor.h" #include "nsIXSLTProcessorObsolete.h" #include "txExpandedNameMap.h" +#include "txNamespaceMap.h" class nsIDOMNode; class nsIPrincipal; class nsIURI; class nsIXMLContentSink; class txStylesheet; +class txResultRecycler; /* bacd8ad0-552f-11d3-a9f7-000064657374 */ #define TRANSFORMIIX_XSLT_PROCESSOR_CID \ @@ -95,6 +97,13 @@ public: nsIPrincipal* aCallerPrincipal); NS_IMETHOD SetSourceContentModel(nsIDOMNode* aSource); NS_IMETHOD CancelLoads() {return NS_OK;}; + NS_IMETHOD AddXSLTParamNamespace(const nsString& aPrefix, + const nsString& aNamespace); + NS_IMETHOD AddXSLTParam(const nsString& aName, + const nsString& aNamespace, + const nsString& aSelect, + const nsString& aValue, + nsIDOMNode* aContext); // nsIDocumentObserver interface NS_DECL_NSIDOCUMENTOBSERVER @@ -126,6 +135,8 @@ private: nsString mErrorText, mSourceText; nsCOMPtr mObserver; txExpandedNameMap mVariables; + txNamespaceMap mParamNamespaceMap; + nsRefPtr mRecycler; }; extern nsresult TX_LoadSheet(nsIURI* aUri, txMozillaXSLTProcessor* aProcessor, diff --git a/content/xslt/src/xslt/txStylesheetCompiler.cpp b/content/xslt/src/xslt/txStylesheetCompiler.cpp index c175b9042ec..d2e3463f57e 100644 --- a/content/xslt/src/xslt/txStylesheetCompiler.cpp +++ b/content/xslt/src/xslt/txStylesheetCompiler.cpp @@ -129,11 +129,11 @@ txStylesheetCompiler::startElement(PRInt32 aNamespaceID, nsIAtom* aLocalName, } if (attr->mLocalName == txXMLAtoms::xmlns) { - mElementContext->mMappings->addNamespace(nsnull, attr->mValue); + mElementContext->mMappings->mapNamespace(nsnull, attr->mValue); } else { mElementContext->mMappings-> - addNamespace(attr->mLocalName, attr->mValue); + mapNamespace(attr->mLocalName, attr->mValue); } } } @@ -193,7 +193,7 @@ txStylesheetCompiler::startElement(const PRUnichar *aName, } rv = mElementContext->mMappings-> - addNamespace(prefixToBind, atts[i].mValue); + mapNamespace(prefixToBind, atts[i].mValue); NS_ENSURE_SUCCESS(rv, rv); } }