summaryrefslogtreecommitdiffstats
path: root/dom/xslt/xpath
diff options
context:
space:
mode:
Diffstat (limited to 'dom/xslt/xpath')
-rw-r--r--dom/xslt/xpath/XPathEvaluator.cpp263
-rw-r--r--dom/xslt/xpath/XPathEvaluator.h94
-rw-r--r--dom/xslt/xpath/XPathExpression.cpp254
-rw-r--r--dom/xslt/xpath/XPathExpression.h74
-rw-r--r--dom/xslt/xpath/XPathResult.cpp340
-rw-r--r--dom/xslt/xpath/XPathResult.h209
-rw-r--r--dom/xslt/xpath/moz.build62
-rw-r--r--dom/xslt/xpath/txBooleanExpr.cpp81
-rw-r--r--dom/xslt/xpath/txBooleanResult.cpp56
-rw-r--r--dom/xslt/xpath/txCoreFunctionCall.cpp743
-rw-r--r--dom/xslt/xpath/txErrorExpr.cpp43
-rw-r--r--dom/xslt/xpath/txExpr.cpp30
-rw-r--r--dom/xslt/xpath/txExpr.h1004
-rw-r--r--dom/xslt/xpath/txExprLexer.cpp367
-rw-r--r--dom/xslt/xpath/txExprLexer.h226
-rw-r--r--dom/xslt/xpath/txExprParser.cpp923
-rw-r--r--dom/xslt/xpath/txExprParser.h109
-rw-r--r--dom/xslt/xpath/txExprResult.h131
-rw-r--r--dom/xslt/xpath/txFilterExpr.cpp94
-rw-r--r--dom/xslt/xpath/txForwardContext.cpp61
-rw-r--r--dom/xslt/xpath/txForwardContext.h32
-rw-r--r--dom/xslt/xpath/txFunctionCall.cpp133
-rw-r--r--dom/xslt/xpath/txIXPathContext.h140
-rw-r--r--dom/xslt/xpath/txLiteralExpr.cpp97
-rw-r--r--dom/xslt/xpath/txLocationStep.cpp325
-rw-r--r--dom/xslt/xpath/txMozillaXPathTreeWalker.cpp776
-rw-r--r--dom/xslt/xpath/txNameTest.cpp95
-rw-r--r--dom/xslt/xpath/txNamedAttributeStep.cpp64
-rw-r--r--dom/xslt/xpath/txNodeSet.cpp622
-rw-r--r--dom/xslt/xpath/txNodeSet.h217
-rw-r--r--dom/xslt/xpath/txNodeSetAdaptor.cpp88
-rw-r--r--dom/xslt/xpath/txNodeSetAdaptor.h41
-rw-r--r--dom/xslt/xpath/txNodeSetContext.cpp60
-rw-r--r--dom/xslt/xpath/txNodeSetContext.h46
-rw-r--r--dom/xslt/xpath/txNodeTypeTest.cpp86
-rw-r--r--dom/xslt/xpath/txNumberExpr.cpp116
-rw-r--r--dom/xslt/xpath/txNumberResult.cpp59
-rw-r--r--dom/xslt/xpath/txPathExpr.cpp253
-rw-r--r--dom/xslt/xpath/txPredicateList.cpp85
-rw-r--r--dom/xslt/xpath/txPredicatedNodeTest.cpp57
-rw-r--r--dom/xslt/xpath/txRelationalExpr.cpp202
-rw-r--r--dom/xslt/xpath/txResultRecycler.cpp216
-rw-r--r--dom/xslt/xpath/txResultRecycler.h80
-rw-r--r--dom/xslt/xpath/txRootExpr.cpp43
-rw-r--r--dom/xslt/xpath/txSingleNodeContext.h80
-rw-r--r--dom/xslt/xpath/txStringResult.cpp56
-rw-r--r--dom/xslt/xpath/txUnaryExpr.cpp55
-rw-r--r--dom/xslt/xpath/txUnionExpr.cpp94
-rw-r--r--dom/xslt/xpath/txUnionNodeTest.cpp60
-rw-r--r--dom/xslt/xpath/txVariableRefExpr.cpp69
-rw-r--r--dom/xslt/xpath/txXPCOMExtensionFunction.cpp617
-rw-r--r--dom/xslt/xpath/txXPathNode.h136
-rw-r--r--dom/xslt/xpath/txXPathObjectAdaptor.h45
-rw-r--r--dom/xslt/xpath/txXPathOptimizer.cpp282
-rw-r--r--dom/xslt/xpath/txXPathOptimizer.h31
-rw-r--r--dom/xslt/xpath/txXPathTreeWalker.h287
56 files changed, 10909 insertions, 0 deletions
diff --git a/dom/xslt/xpath/XPathEvaluator.cpp b/dom/xslt/xpath/XPathEvaluator.cpp
new file mode 100644
index 000000000..e7170373d
--- /dev/null
+++ b/dom/xslt/xpath/XPathEvaluator.cpp
@@ -0,0 +1,263 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "mozilla/dom/XPathEvaluator.h"
+#include "mozilla/Move.h"
+#include "nsCOMPtr.h"
+#include "nsIAtom.h"
+#include "mozilla/dom/XPathExpression.h"
+#include "XPathResult.h"
+#include "nsContentCID.h"
+#include "txExpr.h"
+#include "txExprParser.h"
+#include "nsError.h"
+#include "txURIUtils.h"
+#include "nsIDocument.h"
+#include "nsIDOMDocument.h"
+#include "nsDOMString.h"
+#include "nsNameSpaceManager.h"
+#include "nsContentUtils.h"
+#include "txIXPathContext.h"
+#include "mozilla/dom/XPathEvaluatorBinding.h"
+#include "mozilla/dom/BindingUtils.h"
+#include "mozilla/dom/XPathNSResolverBinding.h"
+
+extern nsresult
+TX_ResolveFunctionCallXPCOM(const nsCString &aContractID, int32_t aNamespaceID,
+ nsIAtom *aName, nsISupports *aState,
+ FunctionCall **aFunction);
+
+namespace mozilla {
+namespace dom {
+
+// txIParseContext implementation
+class XPathEvaluatorParseContext : public txIParseContext
+{
+public:
+ XPathEvaluatorParseContext(XPathNSResolver* aResolver,
+ bool aIsCaseSensitive)
+ : mResolver(aResolver),
+ mResolverNode(nullptr),
+ mLastError(NS_OK),
+ mIsCaseSensitive(aIsCaseSensitive)
+ {
+ }
+ XPathEvaluatorParseContext(nsINode* aResolver,
+ bool aIsCaseSensitive)
+ : mResolver(nullptr),
+ mResolverNode(aResolver),
+ mLastError(NS_OK),
+ mIsCaseSensitive(aIsCaseSensitive)
+ {
+ }
+
+ nsresult getError()
+ {
+ return mLastError;
+ }
+
+ nsresult resolveNamespacePrefix(nsIAtom* aPrefix, int32_t& aID);
+ nsresult resolveFunctionCall(nsIAtom* aName, int32_t aID,
+ FunctionCall** aFunction);
+ bool caseInsensitiveNameTests();
+ void SetErrorOffset(uint32_t aOffset);
+
+private:
+ XPathNSResolver* mResolver;
+ nsINode* mResolverNode;
+ nsresult mLastError;
+ bool mIsCaseSensitive;
+};
+
+NS_IMPL_ISUPPORTS(XPathEvaluator, nsIDOMXPathEvaluator)
+
+XPathEvaluator::XPathEvaluator(nsIDocument* aDocument)
+ : mDocument(do_GetWeakReference(aDocument))
+{
+}
+
+XPathEvaluator::~XPathEvaluator()
+{
+}
+
+NS_IMETHODIMP
+XPathEvaluator::Evaluate(const nsAString & aExpression,
+ nsIDOMNode *aContextNode,
+ nsIDOMNode *aResolver,
+ uint16_t aType,
+ nsISupports *aInResult,
+ nsISupports **aResult)
+{
+ nsCOMPtr<nsINode> resolver = do_QueryInterface(aResolver);
+ ErrorResult rv;
+ nsAutoPtr<XPathExpression> expression(CreateExpression(aExpression,
+ resolver, rv));
+ if (rv.Failed()) {
+ return rv.StealNSResult();
+ }
+
+ nsCOMPtr<nsINode> node = do_QueryInterface(aContextNode);
+ if (!node) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIXPathResult> inResult = do_QueryInterface(aInResult);
+ RefPtr<XPathResult> result =
+ expression->Evaluate(*node, aType,
+ static_cast<XPathResult*>(inResult.get()), rv);
+ if (rv.Failed()) {
+ return rv.StealNSResult();
+ }
+
+ *aResult = ToSupports(result.forget().take());
+
+ return NS_OK;
+}
+
+XPathExpression*
+XPathEvaluator::CreateExpression(const nsAString& aExpression,
+ XPathNSResolver* aResolver, ErrorResult& aRv)
+{
+ nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocument);
+ XPathEvaluatorParseContext pContext(aResolver,
+ !(doc && doc->IsHTMLDocument()));
+ return CreateExpression(aExpression, &pContext, doc, aRv);
+}
+
+XPathExpression*
+XPathEvaluator::CreateExpression(const nsAString& aExpression,
+ nsINode* aResolver, ErrorResult& aRv)
+{
+ nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocument);
+ XPathEvaluatorParseContext pContext(aResolver,
+ !(doc && doc->IsHTMLDocument()));
+ return CreateExpression(aExpression, &pContext, doc, aRv);
+}
+
+XPathExpression*
+XPathEvaluator::CreateExpression(const nsAString & aExpression,
+ txIParseContext* aContext,
+ nsIDocument* aDocument,
+ ErrorResult& aRv)
+{
+ if (!mRecycler) {
+ mRecycler = new txResultRecycler;
+ }
+
+ nsAutoPtr<Expr> expression;
+ aRv = txExprParser::createExpr(PromiseFlatString(aExpression), aContext,
+ getter_Transfers(expression));
+ if (aRv.Failed()) {
+ if (!aRv.ErrorCodeIs(NS_ERROR_DOM_NAMESPACE_ERR)) {
+ aRv.SuppressException();
+ aRv.Throw(NS_ERROR_DOM_INVALID_EXPRESSION_ERR);
+ }
+
+ return nullptr;
+ }
+
+ return new XPathExpression(Move(expression), mRecycler, aDocument);
+}
+
+bool
+XPathEvaluator::WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto,
+ JS::MutableHandle<JSObject*> aReflector)
+{
+ return dom::XPathEvaluatorBinding::Wrap(aCx, this, aGivenProto, aReflector);
+}
+
+/* static */
+already_AddRefed<XPathEvaluator>
+XPathEvaluator::Constructor(const GlobalObject& aGlobal,
+ ErrorResult& rv)
+{
+ RefPtr<XPathEvaluator> newObj = new XPathEvaluator(nullptr);
+ return newObj.forget();
+}
+
+already_AddRefed<XPathResult>
+XPathEvaluator::Evaluate(JSContext* aCx, const nsAString& aExpression,
+ nsINode& aContextNode, XPathNSResolver* aResolver,
+ uint16_t aType, JS::Handle<JSObject*> aResult,
+ ErrorResult& rv)
+{
+ nsAutoPtr<XPathExpression> expression(CreateExpression(aExpression,
+ aResolver, rv));
+ if (rv.Failed()) {
+ return nullptr;
+ }
+ return expression->Evaluate(aCx, aContextNode, aType, aResult, rv);
+}
+
+
+/*
+ * Implementation of txIParseContext private to XPathEvaluator, based on a
+ * XPathNSResolver
+ */
+
+nsresult XPathEvaluatorParseContext::resolveNamespacePrefix
+ (nsIAtom* aPrefix, int32_t& aID)
+{
+ aID = kNameSpaceID_Unknown;
+
+ if (!mResolver && !mResolverNode) {
+ return NS_ERROR_DOM_NAMESPACE_ERR;
+ }
+
+ nsAutoString prefix;
+ if (aPrefix) {
+ aPrefix->ToString(prefix);
+ }
+
+ nsVoidableString ns;
+ if (mResolver) {
+ ErrorResult rv;
+ mResolver->LookupNamespaceURI(prefix, ns, rv);
+ if (rv.Failed()) {
+ return rv.StealNSResult();
+ }
+ } else {
+ if (aPrefix == nsGkAtoms::xml) {
+ ns.AssignLiteral("http://www.w3.org/XML/1998/namespace");
+ } else {
+ mResolverNode->LookupNamespaceURI(prefix, ns);
+ }
+ }
+
+ if (DOMStringIsNull(ns)) {
+ return NS_ERROR_DOM_NAMESPACE_ERR;
+ }
+
+ if (ns.IsEmpty()) {
+ aID = kNameSpaceID_None;
+
+ return NS_OK;
+ }
+
+ // get the namespaceID for the URI
+ return nsContentUtils::NameSpaceManager()->RegisterNameSpace(ns, aID);
+}
+
+nsresult
+XPathEvaluatorParseContext::resolveFunctionCall(nsIAtom* aName,
+ int32_t aID,
+ FunctionCall** aFn)
+{
+ return NS_ERROR_XPATH_UNKNOWN_FUNCTION;
+}
+
+bool XPathEvaluatorParseContext::caseInsensitiveNameTests()
+{
+ return !mIsCaseSensitive;
+}
+
+void
+XPathEvaluatorParseContext::SetErrorOffset(uint32_t aOffset)
+{
+}
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/xslt/xpath/XPathEvaluator.h b/dom/xslt/xpath/XPathEvaluator.h
new file mode 100644
index 000000000..e2a0ca46b
--- /dev/null
+++ b/dom/xslt/xpath/XPathEvaluator.h
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+#ifndef mozilla_dom_XPathEvaluator_h
+#define mozilla_dom_XPathEvaluator_h
+
+#include "nsIDOMXPathEvaluator.h"
+#include "nsIWeakReference.h"
+#include "nsAutoPtr.h"
+#include "nsString.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/ErrorResult.h"
+#include "nsIDocument.h"
+
+class nsINode;
+class txIParseContext;
+class txResultRecycler;
+
+namespace mozilla {
+namespace dom {
+
+class GlobalObject;
+class XPathExpression;
+class XPathNSResolver;
+class XPathResult;
+
+/**
+ * A class for evaluating an XPath expression string
+ */
+class XPathEvaluator final : public nsIDOMXPathEvaluator
+{
+ ~XPathEvaluator();
+
+public:
+ explicit XPathEvaluator(nsIDocument* aDocument = nullptr);
+
+ NS_DECL_ISUPPORTS
+
+ // nsIDOMXPathEvaluator interface
+ NS_DECL_NSIDOMXPATHEVALUATOR
+
+ // WebIDL API
+ bool WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto, JS::MutableHandle<JSObject*> aReflector);
+ nsIDocument* GetParentObject()
+ {
+ nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocument);
+ return doc;
+ }
+ static already_AddRefed<XPathEvaluator>
+ Constructor(const GlobalObject& aGlobal, ErrorResult& rv);
+ XPathExpression*
+ CreateExpression(const nsAString& aExpression,
+ XPathNSResolver* aResolver,
+ ErrorResult& rv);
+ XPathExpression*
+ CreateExpression(const nsAString& aExpression,
+ nsINode* aResolver,
+ ErrorResult& aRv);
+ nsINode* CreateNSResolver(nsINode& aNodeResolver)
+ {
+ return &aNodeResolver;
+ }
+ already_AddRefed<XPathResult>
+ Evaluate(JSContext* aCx, const nsAString& aExpression,
+ nsINode& aContextNode, XPathNSResolver* aResolver,
+ uint16_t aType, JS::Handle<JSObject*> aResult,
+ ErrorResult& rv);
+private:
+ XPathExpression*
+ CreateExpression(const nsAString& aExpression,
+ txIParseContext* aContext,
+ nsIDocument* aDocument,
+ ErrorResult& aRv);
+
+ nsWeakPtr mDocument;
+ RefPtr<txResultRecycler> mRecycler;
+};
+
+inline nsISupports*
+ToSupports(XPathEvaluator* e)
+{
+ return static_cast<nsIDOMXPathEvaluator*>(e);
+}
+
+/* d0a75e02-b5e7-11d5-a7f2-df109fb8a1fc */
+#define TRANSFORMIIX_XPATH_EVALUATOR_CID \
+{ 0xd0a75e02, 0xb5e7, 0x11d5, { 0xa7, 0xf2, 0xdf, 0x10, 0x9f, 0xb8, 0xa1, 0xfc } }
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_XPathEvaluator_h */
diff --git a/dom/xslt/xpath/XPathExpression.cpp b/dom/xslt/xpath/XPathExpression.cpp
new file mode 100644
index 000000000..7ef2dba4e
--- /dev/null
+++ b/dom/xslt/xpath/XPathExpression.cpp
@@ -0,0 +1,254 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "mozilla/Move.h"
+#include "XPathExpression.h"
+#include "txExpr.h"
+#include "txExprResult.h"
+#include "txIXPathContext.h"
+#include "nsError.h"
+#include "nsIDOMCharacterData.h"
+#include "nsDOMClassInfoID.h"
+#include "nsIDOMDocument.h"
+#include "XPathResult.h"
+#include "txURIUtils.h"
+#include "txXPathTreeWalker.h"
+#include "mozilla/dom/BindingUtils.h"
+#include "mozilla/dom/XPathResultBinding.h"
+
+using mozilla::Move;
+
+namespace mozilla {
+namespace dom {
+
+class EvalContextImpl : public txIEvalContext
+{
+public:
+ EvalContextImpl(const txXPathNode& aContextNode,
+ uint32_t aContextPosition, uint32_t aContextSize,
+ txResultRecycler* aRecycler)
+ : mContextNode(aContextNode),
+ mContextPosition(aContextPosition),
+ mContextSize(aContextSize),
+ mLastError(NS_OK),
+ mRecycler(aRecycler)
+ {
+ }
+
+ nsresult getError()
+ {
+ return mLastError;
+ }
+
+ TX_DECL_EVAL_CONTEXT;
+
+private:
+ const txXPathNode& mContextNode;
+ uint32_t mContextPosition;
+ uint32_t mContextSize;
+ nsresult mLastError;
+ RefPtr<txResultRecycler> mRecycler;
+};
+
+XPathExpression::XPathExpression(nsAutoPtr<Expr>&& aExpression,
+ txResultRecycler* aRecycler,
+ nsIDocument *aDocument)
+ : mExpression(Move(aExpression)),
+ mRecycler(aRecycler),
+ mDocument(do_GetWeakReference(aDocument)),
+ mCheckDocument(aDocument != nullptr)
+{
+}
+
+XPathExpression::~XPathExpression()
+{
+}
+
+already_AddRefed<XPathResult>
+XPathExpression::EvaluateWithContext(JSContext* aCx,
+ nsINode& aContextNode,
+ uint32_t aContextPosition,
+ uint32_t aContextSize,
+ uint16_t aType,
+ JS::Handle<JSObject*> aInResult,
+ ErrorResult& aRv)
+{
+ RefPtr<XPathResult> inResult;
+ if (aInResult) {
+ nsresult rv = UNWRAP_OBJECT(XPathResult, aInResult, inResult);
+ if (NS_FAILED(rv) && rv != NS_ERROR_XPC_BAD_CONVERT_JS) {
+ aRv.Throw(rv);
+ return nullptr;
+ }
+ }
+
+ return EvaluateWithContext(aContextNode, aContextPosition, aContextSize,
+ aType, inResult, aRv);
+}
+
+already_AddRefed<XPathResult>
+XPathExpression::EvaluateWithContext(nsINode& aContextNode,
+ uint32_t aContextPosition,
+ uint32_t aContextSize,
+ uint16_t aType,
+ XPathResult* aInResult,
+ ErrorResult& aRv)
+{
+ if (aContextPosition > aContextSize) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return nullptr;
+ }
+
+ if (!nsContentUtils::LegacyIsCallerNativeCode() &&
+ !nsContentUtils::CanCallerAccess(&aContextNode))
+ {
+ aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
+ return nullptr;
+ }
+
+ if (mCheckDocument) {
+ nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocument);
+ if (doc != aContextNode.OwnerDoc()) {
+ aRv.Throw(NS_ERROR_DOM_WRONG_DOCUMENT_ERR);
+ return nullptr;
+ }
+ }
+
+ uint16_t nodeType = aContextNode.NodeType();
+
+ if (nodeType == nsIDOMNode::TEXT_NODE ||
+ nodeType == nsIDOMNode::CDATA_SECTION_NODE) {
+ nsCOMPtr<nsIDOMCharacterData> textNode =
+ do_QueryInterface(&aContextNode);
+ if (!textNode) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return nullptr;
+ }
+
+ uint32_t textLength;
+ textNode->GetLength(&textLength);
+ if (textLength == 0) {
+ aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+ return nullptr;
+ }
+
+ // XXX Need to get logical XPath text node for CDATASection
+ // and Text nodes.
+ }
+ else if (nodeType != nsIDOMNode::DOCUMENT_NODE &&
+ nodeType != nsIDOMNode::ELEMENT_NODE &&
+ nodeType != nsIDOMNode::ATTRIBUTE_NODE &&
+ nodeType != nsIDOMNode::COMMENT_NODE &&
+ nodeType != nsIDOMNode::PROCESSING_INSTRUCTION_NODE) {
+ aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+ return nullptr;
+ }
+
+ nsAutoPtr<txXPathNode> contextNode(txXPathNativeNode::createXPathNode(&aContextNode));
+ if (!contextNode) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return nullptr;
+ }
+
+ EvalContextImpl eContext(*contextNode, aContextPosition, aContextSize,
+ mRecycler);
+ RefPtr<txAExprResult> exprResult;
+ aRv = mExpression->evaluate(&eContext, getter_AddRefs(exprResult));
+ if (aRv.Failed()) {
+ return nullptr;
+ }
+
+ uint16_t resultType = aType;
+ if (aType == XPathResult::ANY_TYPE) {
+ short exprResultType = exprResult->getResultType();
+ switch (exprResultType) {
+ case txAExprResult::NUMBER:
+ resultType = XPathResult::NUMBER_TYPE;
+ break;
+ case txAExprResult::STRING:
+ resultType = XPathResult::STRING_TYPE;
+ break;
+ case txAExprResult::BOOLEAN:
+ resultType = XPathResult::BOOLEAN_TYPE;
+ break;
+ case txAExprResult::NODESET:
+ resultType = XPathResult::UNORDERED_NODE_ITERATOR_TYPE;
+ break;
+ case txAExprResult::RESULT_TREE_FRAGMENT:
+ aRv.Throw(NS_ERROR_FAILURE);
+ return nullptr;
+ }
+ }
+
+ RefPtr<XPathResult> xpathResult = aInResult;
+ if (!xpathResult) {
+ xpathResult = new XPathResult(&aContextNode);
+ }
+
+ aRv = xpathResult->SetExprResult(exprResult, resultType, &aContextNode);
+
+ return xpathResult.forget();
+}
+
+/*
+ * Implementation of the txIEvalContext private to XPathExpression
+ * EvalContextImpl bases on only one context node and no variables
+ */
+
+nsresult
+EvalContextImpl::getVariable(int32_t aNamespace,
+ nsIAtom* aLName,
+ txAExprResult*& aResult)
+{
+ aResult = 0;
+ return NS_ERROR_INVALID_ARG;
+}
+
+bool
+EvalContextImpl::isStripSpaceAllowed(const txXPathNode& aNode)
+{
+ return false;
+}
+
+void*
+EvalContextImpl::getPrivateContext()
+{
+ // we don't have a private context here.
+ return nullptr;
+}
+
+txResultRecycler*
+EvalContextImpl::recycler()
+{
+ return mRecycler;
+}
+
+void
+EvalContextImpl::receiveError(const nsAString& aMsg, nsresult aRes)
+{
+ mLastError = aRes;
+ // forward aMsg to console service?
+}
+
+const txXPathNode&
+EvalContextImpl::getContextNode()
+{
+ return mContextNode;
+}
+
+uint32_t
+EvalContextImpl::size()
+{
+ return mContextSize;
+}
+
+uint32_t
+EvalContextImpl::position()
+{
+ return mContextPosition;
+}
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/xslt/xpath/XPathExpression.h b/dom/xslt/xpath/XPathExpression.h
new file mode 100644
index 000000000..bfe478bdb
--- /dev/null
+++ b/dom/xslt/xpath/XPathExpression.h
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+#ifndef mozilla_dom_XPathExpression_h
+#define mozilla_dom_XPathExpression_h
+
+#include "nsAutoPtr.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsIWeakReferenceUtils.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/dom/NonRefcountedDOMObject.h"
+#include "mozilla/dom/XPathExpressionBinding.h"
+
+class Expr;
+class nsIDocument;
+class nsINode;
+class txResultRecycler;
+
+namespace mozilla {
+namespace dom {
+
+class XPathResult;
+
+/**
+ * A class for evaluating an XPath expression string
+ */
+class XPathExpression final : public NonRefcountedDOMObject
+{
+public:
+ XPathExpression(nsAutoPtr<Expr>&& aExpression, txResultRecycler* aRecycler,
+ nsIDocument *aDocument);
+ ~XPathExpression();
+
+ bool WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto, JS::MutableHandle<JSObject*> aReflector)
+ {
+ return XPathExpressionBinding::Wrap(aCx, this, aGivenProto, aReflector);
+ }
+
+ already_AddRefed<XPathResult>
+ Evaluate(JSContext* aCx, nsINode& aContextNode, uint16_t aType,
+ JS::Handle<JSObject*> aInResult, ErrorResult& aRv)
+ {
+ return EvaluateWithContext(aCx, aContextNode, 1, 1, aType, aInResult,
+ aRv);
+ }
+ already_AddRefed<XPathResult>
+ EvaluateWithContext(JSContext* aCx, nsINode& aContextNode,
+ uint32_t aContextPosition, uint32_t aContextSize,
+ uint16_t aType, JS::Handle<JSObject*> aInResult,
+ ErrorResult& aRv);
+ already_AddRefed<XPathResult>
+ Evaluate(nsINode& aContextNode, uint16_t aType, XPathResult* aInResult,
+ ErrorResult& aRv)
+ {
+ return EvaluateWithContext(aContextNode, 1, 1, aType, aInResult, aRv);
+ }
+ already_AddRefed<XPathResult>
+ EvaluateWithContext(nsINode& aContextNode, uint32_t aContextPosition,
+ uint32_t aContextSize, uint16_t aType,
+ XPathResult* aInResult, ErrorResult& aRv);
+
+private:
+ nsAutoPtr<Expr> mExpression;
+ RefPtr<txResultRecycler> mRecycler;
+ nsWeakPtr mDocument;
+ bool mCheckDocument;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_XPathExpression_h */
diff --git a/dom/xslt/xpath/XPathResult.cpp b/dom/xslt/xpath/XPathResult.cpp
new file mode 100644
index 000000000..33315c942
--- /dev/null
+++ b/dom/xslt/xpath/XPathResult.cpp
@@ -0,0 +1,340 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "XPathResult.h"
+#include "txExprResult.h"
+#include "txNodeSet.h"
+#include "nsError.h"
+#include "mozilla/dom/Attr.h"
+#include "mozilla/dom/Element.h"
+#include "nsDOMClassInfoID.h"
+#include "nsIDOMNode.h"
+#include "nsIDOMDocument.h"
+#include "nsDOMString.h"
+#include "txXPathTreeWalker.h"
+#include "nsCycleCollectionParticipant.h"
+#include "mozilla/dom/XPathResultBinding.h"
+
+namespace mozilla {
+namespace dom {
+
+XPathResult::XPathResult(nsINode* aParent)
+ : mParent(aParent),
+ mDocument(nullptr),
+ mCurrentPos(0),
+ mResultType(ANY_TYPE),
+ mInvalidIteratorState(true),
+ mBooleanResult(false),
+ mNumberResult(0)
+{
+}
+
+XPathResult::XPathResult(const XPathResult &aResult)
+ : mParent(aResult.mParent),
+ mResult(aResult.mResult),
+ mResultNodes(aResult.mResultNodes),
+ mDocument(aResult.mDocument),
+ mContextNode(aResult.mContextNode),
+ mCurrentPos(0),
+ mResultType(aResult.mResultType),
+ mInvalidIteratorState(aResult.mInvalidIteratorState)
+{
+ if (mDocument) {
+ mDocument->AddMutationObserver(this);
+ }
+}
+
+XPathResult::~XPathResult()
+{
+ RemoveObserver();
+}
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(XPathResult)
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(XPathResult)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(XPathResult)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
+ {
+ tmp->RemoveObserver();
+ }
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(XPathResult)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResultNodes)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(XPathResult)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(XPathResult)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(XPathResult)
+ NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+ NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
+ NS_INTERFACE_MAP_ENTRY(nsIXPathResult)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPathResult)
+NS_INTERFACE_MAP_END
+
+JSObject*
+XPathResult::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+ return XPathResultBinding::Wrap(aCx, this, aGivenProto);
+}
+
+void
+XPathResult::RemoveObserver()
+{
+ if (mDocument) {
+ mDocument->RemoveMutationObserver(this);
+ }
+}
+
+nsINode*
+XPathResult::IterateNext(ErrorResult& aRv)
+{
+ if (!isIterator()) {
+ aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
+ return nullptr;
+ }
+
+ if (mDocument) {
+ mDocument->FlushPendingNotifications(Flush_Content);
+ }
+
+ if (mInvalidIteratorState) {
+ aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+ return nullptr;
+ }
+
+ return mResultNodes.SafeObjectAt(mCurrentPos++);
+}
+
+void
+XPathResult::NodeWillBeDestroyed(const nsINode* aNode)
+{
+ nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
+ // Set to null to avoid unregistring unnecessarily
+ mDocument = nullptr;
+ Invalidate(aNode->IsNodeOfType(nsINode::eCONTENT) ?
+ static_cast<const nsIContent*>(aNode) : nullptr);
+}
+
+void
+XPathResult::CharacterDataChanged(nsIDocument* aDocument,
+ nsIContent *aContent,
+ CharacterDataChangeInfo* aInfo)
+{
+ Invalidate(aContent);
+}
+
+void
+XPathResult::AttributeChanged(nsIDocument* aDocument,
+ Element* aElement,
+ int32_t aNameSpaceID,
+ nsIAtom* aAttribute,
+ int32_t aModType,
+ const nsAttrValue* aOldValue)
+{
+ Invalidate(aElement);
+}
+
+void
+XPathResult::ContentAppended(nsIDocument* aDocument,
+ nsIContent* aContainer,
+ nsIContent* aFirstNewContent,
+ int32_t aNewIndexInContainer)
+{
+ Invalidate(aContainer);
+}
+
+void
+XPathResult::ContentInserted(nsIDocument* aDocument,
+ nsIContent* aContainer,
+ nsIContent* aChild,
+ int32_t aIndexInContainer)
+{
+ Invalidate(aContainer);
+}
+
+void
+XPathResult::ContentRemoved(nsIDocument* aDocument,
+ nsIContent* aContainer,
+ nsIContent* aChild,
+ int32_t aIndexInContainer,
+ nsIContent* aPreviousSibling)
+{
+ Invalidate(aContainer);
+}
+
+nsresult
+XPathResult::SetExprResult(txAExprResult* aExprResult, uint16_t aResultType,
+ nsINode* aContextNode)
+{
+ MOZ_ASSERT(aExprResult);
+
+ if ((isSnapshot(aResultType) || isIterator(aResultType) ||
+ isNode(aResultType)) &&
+ aExprResult->getResultType() != txAExprResult::NODESET) {
+ // The DOM spec doesn't really say what should happen when reusing an
+ // XPathResult and an error is thrown. Let's not touch the XPathResult
+ // in that case.
+ return NS_ERROR_DOM_TYPE_ERR;
+ }
+
+ mResultType = aResultType;
+ mContextNode = do_GetWeakReference(aContextNode);
+
+ if (mDocument) {
+ mDocument->RemoveMutationObserver(this);
+ mDocument = nullptr;
+ }
+
+ mResultNodes.Clear();
+
+ // XXX This will keep the recycler alive, should we clear it?
+ mResult = aExprResult;
+ switch (mResultType) {
+ case BOOLEAN_TYPE:
+ {
+ mBooleanResult = mResult->booleanValue();
+ break;
+ }
+ case NUMBER_TYPE:
+ {
+ mNumberResult = mResult->numberValue();
+ break;
+ }
+ case STRING_TYPE:
+ {
+ mResult->stringValue(mStringResult);
+ break;
+ }
+ default:
+ {
+ MOZ_ASSERT(isNode() || isIterator() || isSnapshot());
+ }
+ }
+
+ if (aExprResult->getResultType() == txAExprResult::NODESET) {
+ txNodeSet *nodeSet = static_cast<txNodeSet*>(aExprResult);
+ int32_t i, count = nodeSet->size();
+ for (i = 0; i < count; ++i) {
+ nsINode* node = txXPathNativeNode::getNode(nodeSet->get(i));
+ mResultNodes.AppendObject(node);
+ }
+
+ if (count > 0) {
+ mResult = nullptr;
+ }
+ }
+
+ if (!isIterator()) {
+ return NS_OK;
+ }
+
+ mInvalidIteratorState = false;
+
+ if (mResultNodes.Count() > 0) {
+ // If we support the document() function in DOM-XPath we need to
+ // observe all documents that we have resultnodes in.
+ mDocument = mResultNodes[0]->OwnerDoc();
+ NS_ASSERTION(mDocument, "We need a document!");
+ if (mDocument) {
+ mDocument->AddMutationObserver(this);
+ }
+ }
+
+ return NS_OK;
+}
+
+void
+XPathResult::Invalidate(const nsIContent* aChangeRoot)
+{
+ nsCOMPtr<nsINode> contextNode = do_QueryReferent(mContextNode);
+ if (contextNode && aChangeRoot && aChangeRoot->GetBindingParent()) {
+ // If context node is in anonymous content, changes to
+ // non-anonymous content need to invalidate the XPathResult. If
+ // the changes are happening in a different anonymous trees, no
+ // invalidation should happen.
+ nsIContent* ctxBindingParent = nullptr;
+ if (contextNode->IsNodeOfType(nsINode::eCONTENT)) {
+ ctxBindingParent =
+ static_cast<nsIContent*>(contextNode.get())
+ ->GetBindingParent();
+ } else if (contextNode->IsNodeOfType(nsINode::eATTRIBUTE)) {
+ Element* parent =
+ static_cast<Attr*>(contextNode.get())->GetElement();
+ if (parent) {
+ ctxBindingParent = parent->GetBindingParent();
+ }
+ }
+ if (ctxBindingParent != aChangeRoot->GetBindingParent()) {
+ return;
+ }
+ }
+
+ mInvalidIteratorState = true;
+ // Make sure nulling out mDocument is the last thing we do.
+ if (mDocument) {
+ mDocument->RemoveMutationObserver(this);
+ mDocument = nullptr;
+ }
+}
+
+nsresult
+XPathResult::GetExprResult(txAExprResult** aExprResult)
+{
+ if (isIterator() && mInvalidIteratorState) {
+ return NS_ERROR_DOM_INVALID_STATE_ERR;
+ }
+
+ if (mResult) {
+ NS_ADDREF(*aExprResult = mResult);
+
+ return NS_OK;
+ }
+
+ if (mResultNodes.Count() == 0) {
+ return NS_ERROR_DOM_INVALID_STATE_ERR;
+ }
+
+ RefPtr<txNodeSet> nodeSet = new txNodeSet(nullptr);
+ if (!nodeSet) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ uint32_t i, count = mResultNodes.Count();
+ for (i = 0; i < count; ++i) {
+ nsAutoPtr<txXPathNode> node(txXPathNativeNode::createXPathNode(mResultNodes[i]));
+ if (!node) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ nodeSet->append(*node);
+ }
+
+ NS_ADDREF(*aExprResult = nodeSet);
+
+ return NS_OK;
+}
+
+nsresult
+XPathResult::Clone(nsIXPathResult **aResult)
+{
+ *aResult = nullptr;
+
+ if (isIterator() && mInvalidIteratorState) {
+ return NS_ERROR_DOM_INVALID_STATE_ERR;
+ }
+
+ NS_ADDREF(*aResult = new XPathResult(*this));
+
+ return NS_OK;
+}
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/xslt/xpath/XPathResult.h b/dom/xslt/xpath/XPathResult.h
new file mode 100644
index 000000000..9fe8125ba
--- /dev/null
+++ b/dom/xslt/xpath/XPathResult.h
@@ -0,0 +1,209 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+#ifndef mozilla_dom_XPathResult_h
+#define mozilla_dom_XPathResult_h
+
+#include "nsStubMutationObserver.h"
+#include "nsAutoPtr.h"
+#include "nsCOMPtr.h"
+#include "nsCOMArray.h"
+#include "nsWeakPtr.h"
+#include "nsCycleCollectionParticipant.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/ErrorResult.h"
+#include "nsString.h"
+#include "nsWrapperCache.h"
+#include "nsINode.h"
+
+class nsIDocument;
+class txAExprResult;
+
+// {662f2c9a-c7cd-4cab-9349-e733df5a838c}
+#define NS_IXPATHRESULT_IID \
+{ 0x662f2c9a, 0xc7cd, 0x4cab, {0x93, 0x49, 0xe7, 0x33, 0xdf, 0x5a, 0x83, 0x8c }}
+
+class nsIXPathResult : public nsISupports
+{
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_IXPATHRESULT_IID)
+ virtual nsresult SetExprResult(txAExprResult *aExprResult,
+ uint16_t aResultType,
+ nsINode* aContextNode) = 0;
+ virtual nsresult GetExprResult(txAExprResult **aExprResult) = 0;
+ virtual nsresult Clone(nsIXPathResult **aResult) = 0;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIXPathResult, NS_IXPATHRESULT_IID)
+
+namespace mozilla {
+namespace dom {
+
+/**
+ * A class for evaluating an XPath expression string
+ */
+class XPathResult final : public nsIXPathResult,
+ public nsStubMutationObserver,
+ public nsWrapperCache
+{
+ ~XPathResult();
+
+public:
+ explicit XPathResult(nsINode* aParent);
+ XPathResult(const XPathResult &aResult);
+
+ enum {
+ ANY_TYPE = 0U,
+ NUMBER_TYPE = 1U,
+ STRING_TYPE = 2U,
+ BOOLEAN_TYPE = 3U,
+ UNORDERED_NODE_ITERATOR_TYPE = 4U,
+ ORDERED_NODE_ITERATOR_TYPE = 5U,
+ UNORDERED_NODE_SNAPSHOT_TYPE = 6U,
+ ORDERED_NODE_SNAPSHOT_TYPE = 7U,
+ ANY_UNORDERED_NODE_TYPE = 8U,
+ FIRST_ORDERED_NODE_TYPE = 9U
+ };
+
+ // nsISupports interface
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(XPathResult,
+ nsIXPathResult)
+
+ static XPathResult* FromSupports(nsISupports* aSupports)
+ {
+ return static_cast<XPathResult*>(static_cast<nsIXPathResult*>(aSupports));
+ }
+
+ virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+ nsINode* GetParentObject() const
+ {
+ return mParent;
+ }
+ uint16_t ResultType() const
+ {
+ return mResultType;
+ }
+ double GetNumberValue(ErrorResult& aRv) const
+ {
+ if (mResultType != NUMBER_TYPE) {
+ aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
+ return 0;
+ }
+
+ return mNumberResult;
+ }
+ void GetStringValue(nsAString &aStringValue, ErrorResult& aRv) const
+ {
+ if (mResultType != STRING_TYPE) {
+ aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
+ return;
+ }
+
+ aStringValue = mStringResult;
+ }
+ bool GetBooleanValue(ErrorResult& aRv) const
+ {
+ if (mResultType != BOOLEAN_TYPE) {
+ aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
+ return false;
+ }
+
+ return mBooleanResult;
+ }
+ nsINode* GetSingleNodeValue(ErrorResult& aRv) const
+ {
+ if (!isNode()) {
+ aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
+ return nullptr;
+ }
+
+ return mResultNodes.SafeObjectAt(0);
+ }
+ bool InvalidIteratorState() const
+ {
+ return isIterator() && mInvalidIteratorState;
+ }
+ uint32_t GetSnapshotLength(ErrorResult& aRv) const
+ {
+ if (!isSnapshot()) {
+ aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
+ return 0;
+ }
+
+ return (uint32_t)mResultNodes.Count();
+ }
+ nsINode* IterateNext(ErrorResult& aRv);
+ nsINode* SnapshotItem(uint32_t aIndex, ErrorResult& aRv) const
+ {
+ if (!isSnapshot()) {
+ aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
+ return nullptr;
+ }
+
+ return mResultNodes.SafeObjectAt(aIndex);
+ }
+
+ // nsIMutationObserver interface
+ NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED
+ NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
+ NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
+ NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
+ NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
+ NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED
+
+ nsresult SetExprResult(txAExprResult *aExprResult, uint16_t aResultType,
+ nsINode* aContextNode) override;
+ nsresult GetExprResult(txAExprResult **aExprResult) override;
+ nsresult Clone(nsIXPathResult **aResult) override;
+ void RemoveObserver();
+private:
+ static bool isSnapshot(uint16_t aResultType)
+ {
+ return aResultType == UNORDERED_NODE_SNAPSHOT_TYPE ||
+ aResultType == ORDERED_NODE_SNAPSHOT_TYPE;
+ }
+ static bool isIterator(uint16_t aResultType)
+ {
+ return aResultType == UNORDERED_NODE_ITERATOR_TYPE ||
+ aResultType == ORDERED_NODE_ITERATOR_TYPE;
+ }
+ static bool isNode(uint16_t aResultType)
+ {
+ return aResultType == FIRST_ORDERED_NODE_TYPE ||
+ aResultType == ANY_UNORDERED_NODE_TYPE;
+ }
+ bool isSnapshot() const
+ {
+ return isSnapshot(mResultType);
+ }
+ bool isIterator() const
+ {
+ return isIterator(mResultType);
+ }
+ bool isNode() const
+ {
+ return isNode(mResultType);
+ }
+
+ void Invalidate(const nsIContent* aChangeRoot);
+
+ nsCOMPtr<nsINode> mParent;
+ RefPtr<txAExprResult> mResult;
+ nsCOMArray<nsINode> mResultNodes;
+ nsCOMPtr<nsIDocument> mDocument;
+ nsWeakPtr mContextNode;
+ uint32_t mCurrentPos;
+ uint16_t mResultType;
+ bool mInvalidIteratorState;
+ bool mBooleanResult;
+ double mNumberResult;
+ nsString mStringResult;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_XPathResult_h */
diff --git a/dom/xslt/xpath/moz.build b/dom/xslt/xpath/moz.build
new file mode 100644
index 000000000..1a7dbf89f
--- /dev/null
+++ b/dom/xslt/xpath/moz.build
@@ -0,0 +1,62 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+EXPORTS.mozilla.dom += [
+ 'XPathEvaluator.h',
+ 'XPathExpression.h',
+ 'XPathResult.h',
+]
+
+UNIFIED_SOURCES += [
+ 'txBooleanExpr.cpp',
+ 'txBooleanResult.cpp',
+ 'txCoreFunctionCall.cpp',
+ 'txErrorExpr.cpp',
+ 'txExpr.cpp',
+ 'txExprLexer.cpp',
+ 'txExprParser.cpp',
+ 'txFilterExpr.cpp',
+ 'txForwardContext.cpp',
+ 'txFunctionCall.cpp',
+ 'txLiteralExpr.cpp',
+ 'txLocationStep.cpp',
+ 'txMozillaXPathTreeWalker.cpp',
+ 'txNamedAttributeStep.cpp',
+ 'txNameTest.cpp',
+ 'txNodeSet.cpp',
+ 'txNodeSetAdaptor.cpp',
+ 'txNodeSetContext.cpp',
+ 'txNodeTypeTest.cpp',
+ 'txNumberExpr.cpp',
+ 'txNumberResult.cpp',
+ 'txPathExpr.cpp',
+ 'txPredicatedNodeTest.cpp',
+ 'txPredicateList.cpp',
+ 'txRelationalExpr.cpp',
+ 'txResultRecycler.cpp',
+ 'txRootExpr.cpp',
+ 'txStringResult.cpp',
+ 'txUnaryExpr.cpp',
+ 'txUnionExpr.cpp',
+ 'txUnionNodeTest.cpp',
+ 'txVariableRefExpr.cpp',
+ 'txXPathOptimizer.cpp',
+ 'txXPCOMExtensionFunction.cpp',
+ 'XPathEvaluator.cpp',
+ 'XPathExpression.cpp',
+ 'XPathResult.cpp',
+]
+
+LOCAL_INCLUDES += [
+ '../base',
+ '../xml',
+ '../xslt',
+]
+
+FINAL_LIBRARY = 'xul'
+
+if CONFIG['GNU_CXX']:
+ CXXFLAGS += ['-Wno-error=shadow']
diff --git a/dom/xslt/xpath/txBooleanExpr.cpp b/dom/xslt/xpath/txBooleanExpr.cpp
new file mode 100644
index 000000000..450d2ab10
--- /dev/null
+++ b/dom/xslt/xpath/txBooleanExpr.cpp
@@ -0,0 +1,81 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+
+/**
+ * Represents a BooleanExpr, a binary expression that
+ * performs a boolean operation between its lvalue and rvalue.
+**/
+
+#include "txExpr.h"
+#include "txIXPathContext.h"
+
+/**
+ * Evaluates this Expr based on the given context node and processor state
+ * @param context the context node for evaluation of this Expr
+ * @param ps the ContextState containing the stack information needed
+ * for evaluation
+ * @return the result of the evaluation
+**/
+nsresult
+BooleanExpr::evaluate(txIEvalContext* aContext, txAExprResult** aResult)
+{
+ *aResult = nullptr;
+
+ bool lval;
+ nsresult rv = leftExpr->evaluateToBool(aContext, lval);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // check for early decision
+ if (op == OR && lval) {
+ aContext->recycler()->getBoolResult(true, aResult);
+
+ return NS_OK;
+ }
+ if (op == AND && !lval) {
+ aContext->recycler()->getBoolResult(false, aResult);
+
+ return NS_OK;
+ }
+
+ bool rval;
+ rv = rightExpr->evaluateToBool(aContext, rval);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // just use rval, since we already checked lval
+ aContext->recycler()->getBoolResult(rval, aResult);
+
+ return NS_OK;
+} //-- evaluate
+
+TX_IMPL_EXPR_STUBS_2(BooleanExpr, BOOLEAN_RESULT, leftExpr, rightExpr)
+
+bool
+BooleanExpr::isSensitiveTo(ContextSensitivity aContext)
+{
+ return leftExpr->isSensitiveTo(aContext) ||
+ rightExpr->isSensitiveTo(aContext);
+}
+
+#ifdef TX_TO_STRING
+void
+BooleanExpr::toString(nsAString& str)
+{
+ if ( leftExpr ) leftExpr->toString(str);
+ else str.AppendLiteral("null");
+
+ switch ( op ) {
+ case OR:
+ str.AppendLiteral(" or ");
+ break;
+ default:
+ str.AppendLiteral(" and ");
+ break;
+ }
+ if ( rightExpr ) rightExpr->toString(str);
+ else str.AppendLiteral("null");
+
+}
+#endif
diff --git a/dom/xslt/xpath/txBooleanResult.cpp b/dom/xslt/xpath/txBooleanResult.cpp
new file mode 100644
index 000000000..8eb91bb8e
--- /dev/null
+++ b/dom/xslt/xpath/txBooleanResult.cpp
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/*
+ * Boolean Expression result
+*/
+
+#include "txExprResult.h"
+
+/**
+ * Creates a new BooleanResult with the value of the given bool parameter
+ * @param boolean the bool to use for initialization of this BooleanResult's value
+**/
+BooleanResult::BooleanResult(bool boolean)
+ : txAExprResult(nullptr)
+{
+ this->value = boolean;
+} //-- BooleanResult
+
+/*
+ * Virtual Methods from ExprResult
+*/
+
+short BooleanResult::getResultType() {
+ return txAExprResult::BOOLEAN;
+} //-- getResultType
+
+void
+BooleanResult::stringValue(nsString& aResult)
+{
+ if (value) {
+ aResult.AppendLiteral("true");
+ }
+ else {
+ aResult.AppendLiteral("false");
+ }
+}
+
+const nsString*
+BooleanResult::stringValuePointer()
+{
+ // In theory we could set strings containing "true" and "false" somewhere,
+ // but most stylesheets never get the stringvalue of a bool so that won't
+ // really buy us anything.
+ return nullptr;
+}
+
+bool BooleanResult::booleanValue() {
+ return this->value;
+} //-- toBoolean
+
+double BooleanResult::numberValue() {
+ return ( value ) ? 1.0 : 0.0;
+} //-- toNumber
diff --git a/dom/xslt/xpath/txCoreFunctionCall.cpp b/dom/xslt/xpath/txCoreFunctionCall.cpp
new file mode 100644
index 000000000..82df8c8ee
--- /dev/null
+++ b/dom/xslt/xpath/txCoreFunctionCall.cpp
@@ -0,0 +1,743 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "mozilla/ArrayUtils.h"
+#include "mozilla/FloatingPoint.h"
+
+#include "txExpr.h"
+#include "nsAutoPtr.h"
+#include "txNodeSet.h"
+#include "nsGkAtoms.h"
+#include "txIXPathContext.h"
+#include "nsWhitespaceTokenizer.h"
+#include "txXPathTreeWalker.h"
+#include <math.h>
+#include "txStringUtils.h"
+#include "txXMLUtils.h"
+
+using namespace mozilla;
+
+struct txCoreFunctionDescriptor
+{
+ int8_t mMinParams;
+ int8_t mMaxParams;
+ Expr::ResultType mReturnType;
+ nsIAtom** mName;
+};
+
+// This must be ordered in the same order as txCoreFunctionCall::eType.
+// If you change one, change the other.
+static const txCoreFunctionDescriptor descriptTable[] =
+{
+ { 1, 1, Expr::NUMBER_RESULT, &nsGkAtoms::count }, // COUNT
+ { 1, 1, Expr::NODESET_RESULT, &nsGkAtoms::id }, // ID
+ { 0, 0, Expr::NUMBER_RESULT, &nsGkAtoms::last }, // LAST
+ { 0, 1, Expr::STRING_RESULT, &nsGkAtoms::localName }, // LOCAL_NAME
+ { 0, 1, Expr::STRING_RESULT, &nsGkAtoms::namespaceUri }, // NAMESPACE_URI
+ { 0, 1, Expr::STRING_RESULT, &nsGkAtoms::name }, // NAME
+ { 0, 0, Expr::NUMBER_RESULT, &nsGkAtoms::position }, // POSITION
+
+ { 2, -1, Expr::STRING_RESULT, &nsGkAtoms::concat }, // CONCAT
+ { 2, 2, Expr::BOOLEAN_RESULT, &nsGkAtoms::contains }, // CONTAINS
+ { 0, 1, Expr::STRING_RESULT, &nsGkAtoms::normalizeSpace }, // NORMALIZE_SPACE
+ { 2, 2, Expr::BOOLEAN_RESULT, &nsGkAtoms::startsWith }, // STARTS_WITH
+ { 0, 1, Expr::STRING_RESULT, &nsGkAtoms::string }, // STRING
+ { 0, 1, Expr::NUMBER_RESULT, &nsGkAtoms::stringLength }, // STRING_LENGTH
+ { 2, 3, Expr::STRING_RESULT, &nsGkAtoms::substring }, // SUBSTRING
+ { 2, 2, Expr::STRING_RESULT, &nsGkAtoms::substringAfter }, // SUBSTRING_AFTER
+ { 2, 2, Expr::STRING_RESULT, &nsGkAtoms::substringBefore }, // SUBSTRING_BEFORE
+ { 3, 3, Expr::STRING_RESULT, &nsGkAtoms::translate }, // TRANSLATE
+
+ { 0, 1, Expr::NUMBER_RESULT, &nsGkAtoms::number }, // NUMBER
+ { 1, 1, Expr::NUMBER_RESULT, &nsGkAtoms::round }, // ROUND
+ { 1, 1, Expr::NUMBER_RESULT, &nsGkAtoms::floor }, // FLOOR
+ { 1, 1, Expr::NUMBER_RESULT, &nsGkAtoms::ceiling }, // CEILING
+ { 1, 1, Expr::NUMBER_RESULT, &nsGkAtoms::sum }, // SUM
+
+ { 1, 1, Expr::BOOLEAN_RESULT, &nsGkAtoms::boolean }, // BOOLEAN
+ { 0, 0, Expr::BOOLEAN_RESULT, &nsGkAtoms::_false }, // _FALSE
+ { 1, 1, Expr::BOOLEAN_RESULT, &nsGkAtoms::lang }, // LANG
+ { 1, 1, Expr::BOOLEAN_RESULT, &nsGkAtoms::_not }, // _NOT
+ { 0, 0, Expr::BOOLEAN_RESULT, &nsGkAtoms::_true } // _TRUE
+};
+
+
+/*
+ * Evaluates this Expr based on the given context node and processor state
+ * @param context the context node for evaluation of this Expr
+ * @param ps the ContextState containing the stack information needed
+ * for evaluation
+ * @return the result of the evaluation
+ */
+nsresult
+txCoreFunctionCall::evaluate(txIEvalContext* aContext, txAExprResult** aResult)
+{
+ *aResult = nullptr;
+
+ if (!requireParams(descriptTable[mType].mMinParams,
+ descriptTable[mType].mMaxParams,
+ aContext)) {
+ return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT;
+ }
+
+ nsresult rv = NS_OK;
+ switch (mType) {
+ case COUNT:
+ {
+ RefPtr<txNodeSet> nodes;
+ rv = evaluateToNodeSet(mParams[0], aContext,
+ getter_AddRefs(nodes));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return aContext->recycler()->getNumberResult(nodes->size(),
+ aResult);
+ }
+ case ID:
+ {
+ RefPtr<txAExprResult> exprResult;
+ rv = mParams[0]->evaluate(aContext, getter_AddRefs(exprResult));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ RefPtr<txNodeSet> resultSet;
+ rv = aContext->recycler()->getNodeSet(getter_AddRefs(resultSet));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ txXPathTreeWalker walker(aContext->getContextNode());
+
+ if (exprResult->getResultType() == txAExprResult::NODESET) {
+ txNodeSet* nodes = static_cast<txNodeSet*>
+ (static_cast<txAExprResult*>
+ (exprResult));
+ int32_t i;
+ for (i = 0; i < nodes->size(); ++i) {
+ nsAutoString idList;
+ txXPathNodeUtils::appendNodeValue(nodes->get(i), idList);
+ nsWhitespaceTokenizer tokenizer(idList);
+ while (tokenizer.hasMoreTokens()) {
+ if (walker.moveToElementById(tokenizer.nextToken())) {
+ resultSet->add(walker.getCurrentPosition());
+ }
+ }
+ }
+ }
+ else {
+ nsAutoString idList;
+ exprResult->stringValue(idList);
+ nsWhitespaceTokenizer tokenizer(idList);
+ while (tokenizer.hasMoreTokens()) {
+ if (walker.moveToElementById(tokenizer.nextToken())) {
+ resultSet->add(walker.getCurrentPosition());
+ }
+ }
+ }
+
+ *aResult = resultSet;
+ NS_ADDREF(*aResult);
+
+ return NS_OK;
+ }
+ case LAST:
+ {
+ return aContext->recycler()->getNumberResult(aContext->size(),
+ aResult);
+ }
+ case LOCAL_NAME:
+ case NAME:
+ case NAMESPACE_URI:
+ {
+ // Check for optional arg
+ RefPtr<txNodeSet> nodes;
+ if (!mParams.IsEmpty()) {
+ rv = evaluateToNodeSet(mParams[0], aContext,
+ getter_AddRefs(nodes));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (nodes->isEmpty()) {
+ aContext->recycler()->getEmptyStringResult(aResult);
+
+ return NS_OK;
+ }
+ }
+
+ const txXPathNode& node = nodes ? nodes->get(0) :
+ aContext->getContextNode();
+ switch (mType) {
+ case LOCAL_NAME:
+ {
+ StringResult* strRes = nullptr;
+ rv = aContext->recycler()->getStringResult(&strRes);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ *aResult = strRes;
+ txXPathNodeUtils::getLocalName(node, strRes->mValue);
+
+ return NS_OK;
+ }
+ case NAMESPACE_URI:
+ {
+ StringResult* strRes = nullptr;
+ rv = aContext->recycler()->getStringResult(&strRes);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ *aResult = strRes;
+ txXPathNodeUtils::getNamespaceURI(node, strRes->mValue);
+
+ return NS_OK;
+ }
+ case NAME:
+ {
+ // XXX Namespace: namespaces have a name
+ if (txXPathNodeUtils::isAttribute(node) ||
+ txXPathNodeUtils::isElement(node) ||
+ txXPathNodeUtils::isProcessingInstruction(node)) {
+ StringResult* strRes = nullptr;
+ rv = aContext->recycler()->getStringResult(&strRes);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ *aResult = strRes;
+ txXPathNodeUtils::getNodeName(node, strRes->mValue);
+ }
+ else {
+ aContext->recycler()->getEmptyStringResult(aResult);
+ }
+
+ return NS_OK;
+ }
+ default:
+ {
+ MOZ_CRASH("Unexpected mType?!");
+ }
+ }
+ MOZ_CRASH("Inner mType switch should have returned!");
+ }
+ case POSITION:
+ {
+ return aContext->recycler()->getNumberResult(aContext->position(),
+ aResult);
+ }
+
+ // String functions
+
+ case CONCAT:
+ {
+ RefPtr<StringResult> strRes;
+ rv = aContext->recycler()->getStringResult(getter_AddRefs(strRes));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ uint32_t i, len = mParams.Length();
+ for (i = 0; i < len; ++i) {
+ rv = mParams[i]->evaluateToString(aContext, strRes->mValue);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ NS_ADDREF(*aResult = strRes);
+
+ return NS_OK;
+ }
+ case CONTAINS:
+ {
+ nsAutoString arg2;
+ rv = mParams[1]->evaluateToString(aContext, arg2);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (arg2.IsEmpty()) {
+ aContext->recycler()->getBoolResult(true, aResult);
+ }
+ else {
+ nsAutoString arg1;
+ rv = mParams[0]->evaluateToString(aContext, arg1);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aContext->recycler()->getBoolResult(FindInReadable(arg2, arg1),
+ aResult);
+ }
+
+ return NS_OK;
+ }
+ case NORMALIZE_SPACE:
+ {
+ nsAutoString resultStr;
+ if (!mParams.IsEmpty()) {
+ rv = mParams[0]->evaluateToString(aContext, resultStr);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ else {
+ txXPathNodeUtils::appendNodeValue(aContext->getContextNode(),
+ resultStr);
+ }
+
+ RefPtr<StringResult> strRes;
+ rv = aContext->recycler()->getStringResult(getter_AddRefs(strRes));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool addSpace = false;
+ bool first = true;
+ strRes->mValue.SetCapacity(resultStr.Length());
+ char16_t c;
+ uint32_t src;
+ for (src = 0; src < resultStr.Length(); src++) {
+ c = resultStr.CharAt(src);
+ if (XMLUtils::isWhitespace(c)) {
+ addSpace = true;
+ }
+ else {
+ if (addSpace && !first)
+ strRes->mValue.Append(char16_t(' '));
+
+ strRes->mValue.Append(c);
+ addSpace = false;
+ first = false;
+ }
+ }
+ *aResult = strRes;
+ NS_ADDREF(*aResult);
+
+ return NS_OK;
+ }
+ case STARTS_WITH:
+ {
+ nsAutoString arg2;
+ rv = mParams[1]->evaluateToString(aContext, arg2);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool result = false;
+ if (arg2.IsEmpty()) {
+ result = true;
+ }
+ else {
+ nsAutoString arg1;
+ rv = mParams[0]->evaluateToString(aContext, arg1);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ result = StringBeginsWith(arg1, arg2);
+ }
+
+ aContext->recycler()->getBoolResult(result, aResult);
+
+ return NS_OK;
+ }
+ case STRING:
+ {
+ RefPtr<StringResult> strRes;
+ rv = aContext->recycler()->getStringResult(getter_AddRefs(strRes));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!mParams.IsEmpty()) {
+ rv = mParams[0]->evaluateToString(aContext, strRes->mValue);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ else {
+ txXPathNodeUtils::appendNodeValue(aContext->getContextNode(),
+ strRes->mValue);
+ }
+
+ NS_ADDREF(*aResult = strRes);
+
+ return NS_OK;
+ }
+ case STRING_LENGTH:
+ {
+ nsAutoString resultStr;
+ if (!mParams.IsEmpty()) {
+ rv = mParams[0]->evaluateToString(aContext, resultStr);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ else {
+ txXPathNodeUtils::appendNodeValue(aContext->getContextNode(),
+ resultStr);
+ }
+ rv = aContext->recycler()->getNumberResult(resultStr.Length(),
+ aResult);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+ }
+ case SUBSTRING:
+ {
+ nsAutoString src;
+ rv = mParams[0]->evaluateToString(aContext, src);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ double start;
+ rv = evaluateToNumber(mParams[1], aContext, &start);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // check for NaN or +/-Inf
+ if (mozilla::IsNaN(start) ||
+ mozilla::IsInfinite(start) ||
+ start >= src.Length() + 0.5) {
+ aContext->recycler()->getEmptyStringResult(aResult);
+
+ return NS_OK;
+ }
+
+ start = floor(start + 0.5) - 1;
+
+ double end;
+ if (mParams.Length() == 3) {
+ rv = evaluateToNumber(mParams[2], aContext, &end);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ end += start;
+ if (mozilla::IsNaN(end) || end < 0) {
+ aContext->recycler()->getEmptyStringResult(aResult);
+
+ return NS_OK;
+ }
+
+ if (end > src.Length())
+ end = src.Length();
+ else
+ end = floor(end + 0.5);
+ }
+ else {
+ end = src.Length();
+ }
+
+ if (start < 0)
+ start = 0;
+
+ if (start > end) {
+ aContext->recycler()->getEmptyStringResult(aResult);
+
+ return NS_OK;
+ }
+
+ return aContext->recycler()->getStringResult(
+ Substring(src, (uint32_t)start, (uint32_t)(end - start)),
+ aResult);
+ }
+ case SUBSTRING_AFTER:
+ {
+ nsAutoString arg1;
+ rv = mParams[0]->evaluateToString(aContext, arg1);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString arg2;
+ rv = mParams[1]->evaluateToString(aContext, arg2);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (arg2.IsEmpty()) {
+ return aContext->recycler()->getStringResult(arg1, aResult);
+ }
+
+ int32_t idx = arg1.Find(arg2);
+ if (idx == kNotFound) {
+ aContext->recycler()->getEmptyStringResult(aResult);
+
+ return NS_OK;
+ }
+
+ const nsSubstring& result = Substring(arg1, idx + arg2.Length());
+ return aContext->recycler()->getStringResult(result, aResult);
+ }
+ case SUBSTRING_BEFORE:
+ {
+ nsAutoString arg2;
+ rv = mParams[1]->evaluateToString(aContext, arg2);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (arg2.IsEmpty()) {
+ aContext->recycler()->getEmptyStringResult(aResult);
+
+ return NS_OK;
+ }
+
+ nsAutoString arg1;
+ rv = mParams[0]->evaluateToString(aContext, arg1);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ int32_t idx = arg1.Find(arg2);
+ if (idx == kNotFound) {
+ aContext->recycler()->getEmptyStringResult(aResult);
+
+ return NS_OK;
+ }
+
+ return aContext->recycler()->getStringResult(StringHead(arg1, idx),
+ aResult);
+ }
+ case TRANSLATE:
+ {
+ nsAutoString src;
+ rv = mParams[0]->evaluateToString(aContext, src);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (src.IsEmpty()) {
+ aContext->recycler()->getEmptyStringResult(aResult);
+
+ return NS_OK;
+ }
+
+ RefPtr<StringResult> strRes;
+ rv = aContext->recycler()->getStringResult(getter_AddRefs(strRes));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ strRes->mValue.SetCapacity(src.Length());
+
+ nsAutoString oldChars, newChars;
+ rv = mParams[1]->evaluateToString(aContext, oldChars);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mParams[2]->evaluateToString(aContext, newChars);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ uint32_t i;
+ int32_t newCharsLength = (int32_t)newChars.Length();
+ for (i = 0; i < src.Length(); i++) {
+ int32_t idx = oldChars.FindChar(src.CharAt(i));
+ if (idx != kNotFound) {
+ if (idx < newCharsLength)
+ strRes->mValue.Append(newChars.CharAt((uint32_t)idx));
+ }
+ else {
+ strRes->mValue.Append(src.CharAt(i));
+ }
+ }
+
+ NS_ADDREF(*aResult = strRes);
+
+ return NS_OK;
+ }
+
+ // Number functions
+
+ case NUMBER:
+ {
+ double res;
+ if (!mParams.IsEmpty()) {
+ rv = evaluateToNumber(mParams[0], aContext, &res);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ else {
+ nsAutoString resultStr;
+ txXPathNodeUtils::appendNodeValue(aContext->getContextNode(),
+ resultStr);
+ res = txDouble::toDouble(resultStr);
+ }
+ return aContext->recycler()->getNumberResult(res, aResult);
+ }
+ case ROUND:
+ {
+ double dbl;
+ rv = evaluateToNumber(mParams[0], aContext, &dbl);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (mozilla::IsFinite(dbl)) {
+ if (mozilla::IsNegative(dbl) && dbl >= -0.5) {
+ dbl *= 0;
+ }
+ else {
+ dbl = floor(dbl + 0.5);
+ }
+ }
+
+ return aContext->recycler()->getNumberResult(dbl, aResult);
+ }
+ case FLOOR:
+ {
+ double dbl;
+ rv = evaluateToNumber(mParams[0], aContext, &dbl);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (mozilla::IsFinite(dbl) && !mozilla::IsNegativeZero(dbl))
+ dbl = floor(dbl);
+
+ return aContext->recycler()->getNumberResult(dbl, aResult);
+ }
+ case CEILING:
+ {
+ double dbl;
+ rv = evaluateToNumber(mParams[0], aContext, &dbl);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (mozilla::IsFinite(dbl)) {
+ if (mozilla::IsNegative(dbl) && dbl > -1)
+ dbl *= 0;
+ else
+ dbl = ceil(dbl);
+ }
+
+ return aContext->recycler()->getNumberResult(dbl, aResult);
+ }
+ case SUM:
+ {
+ RefPtr<txNodeSet> nodes;
+ nsresult rv = evaluateToNodeSet(mParams[0], aContext,
+ getter_AddRefs(nodes));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ double res = 0;
+ int32_t i;
+ for (i = 0; i < nodes->size(); ++i) {
+ nsAutoString resultStr;
+ txXPathNodeUtils::appendNodeValue(nodes->get(i), resultStr);
+ res += txDouble::toDouble(resultStr);
+ }
+ return aContext->recycler()->getNumberResult(res, aResult);
+ }
+
+ // Boolean functions
+
+ case BOOLEAN:
+ {
+ bool result;
+ nsresult rv = mParams[0]->evaluateToBool(aContext, result);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aContext->recycler()->getBoolResult(result, aResult);
+
+ return NS_OK;
+ }
+ case _FALSE:
+ {
+ aContext->recycler()->getBoolResult(false, aResult);
+
+ return NS_OK;
+ }
+ case LANG:
+ {
+ txXPathTreeWalker walker(aContext->getContextNode());
+
+ nsAutoString lang;
+ bool found;
+ do {
+ found = walker.getAttr(nsGkAtoms::lang, kNameSpaceID_XML,
+ lang);
+ } while (!found && walker.moveToParent());
+
+ if (!found) {
+ aContext->recycler()->getBoolResult(false, aResult);
+
+ return NS_OK;
+ }
+
+ nsAutoString arg;
+ rv = mParams[0]->evaluateToString(aContext, arg);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool result =
+ StringBeginsWith(lang, arg,
+ txCaseInsensitiveStringComparator()) &&
+ (lang.Length() == arg.Length() ||
+ lang.CharAt(arg.Length()) == '-');
+
+ aContext->recycler()->getBoolResult(result, aResult);
+
+ return NS_OK;
+ }
+ case _NOT:
+ {
+ bool result;
+ rv = mParams[0]->evaluateToBool(aContext, result);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aContext->recycler()->getBoolResult(!result, aResult);
+
+ return NS_OK;
+ }
+ case _TRUE:
+ {
+ aContext->recycler()->getBoolResult(true, aResult);
+
+ return NS_OK;
+ }
+ }
+
+ aContext->receiveError(NS_LITERAL_STRING("Internal error"),
+ NS_ERROR_UNEXPECTED);
+ return NS_ERROR_UNEXPECTED;
+}
+
+Expr::ResultType
+txCoreFunctionCall::getReturnType()
+{
+ return descriptTable[mType].mReturnType;
+}
+
+bool
+txCoreFunctionCall::isSensitiveTo(ContextSensitivity aContext)
+{
+ switch (mType) {
+ case COUNT:
+ case CONCAT:
+ case CONTAINS:
+ case STARTS_WITH:
+ case SUBSTRING:
+ case SUBSTRING_AFTER:
+ case SUBSTRING_BEFORE:
+ case TRANSLATE:
+ case ROUND:
+ case FLOOR:
+ case CEILING:
+ case SUM:
+ case BOOLEAN:
+ case _NOT:
+ case _FALSE:
+ case _TRUE:
+ {
+ return argsSensitiveTo(aContext);
+ }
+ case ID:
+ {
+ return (aContext & NODE_CONTEXT) ||
+ argsSensitiveTo(aContext);
+ }
+ case LAST:
+ {
+ return !!(aContext & SIZE_CONTEXT);
+ }
+ case LOCAL_NAME:
+ case NAME:
+ case NAMESPACE_URI:
+ case NORMALIZE_SPACE:
+ case STRING:
+ case STRING_LENGTH:
+ case NUMBER:
+ {
+ if (mParams.IsEmpty()) {
+ return !!(aContext & NODE_CONTEXT);
+ }
+ return argsSensitiveTo(aContext);
+ }
+ case POSITION:
+ {
+ return !!(aContext & POSITION_CONTEXT);
+ }
+ case LANG:
+ {
+ return (aContext & NODE_CONTEXT) ||
+ argsSensitiveTo(aContext);
+ }
+ }
+
+ NS_NOTREACHED("how'd we get here?");
+ return true;
+}
+
+// static
+bool
+txCoreFunctionCall::getTypeFromAtom(nsIAtom* aName, eType& aType)
+{
+ uint32_t i;
+ for (i = 0; i < ArrayLength(descriptTable); ++i) {
+ if (aName == *descriptTable[i].mName) {
+ aType = static_cast<eType>(i);
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+#ifdef TX_TO_STRING
+nsresult
+txCoreFunctionCall::getNameAtom(nsIAtom** aAtom)
+{
+ NS_ADDREF(*aAtom = *descriptTable[mType].mName);
+ return NS_OK;
+}
+#endif
diff --git a/dom/xslt/xpath/txErrorExpr.cpp b/dom/xslt/xpath/txErrorExpr.cpp
new file mode 100644
index 000000000..93c45c6a7
--- /dev/null
+++ b/dom/xslt/xpath/txErrorExpr.cpp
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "nsError.h"
+#include "txExpr.h"
+#include "nsString.h"
+#include "txIXPathContext.h"
+
+nsresult
+txErrorExpr::evaluate(txIEvalContext* aContext, txAExprResult** aResult)
+{
+ *aResult = nullptr;
+
+ nsAutoString err(NS_LITERAL_STRING("Invalid expression evaluated"));
+#ifdef TX_TO_STRING
+ err.AppendLiteral(": ");
+ toString(err);
+#endif
+ aContext->receiveError(err,
+ NS_ERROR_XPATH_INVALID_EXPRESSION_EVALUATED);
+
+ return NS_ERROR_XPATH_INVALID_EXPRESSION_EVALUATED;
+}
+
+TX_IMPL_EXPR_STUBS_0(txErrorExpr, ANY_RESULT)
+
+bool
+txErrorExpr::isSensitiveTo(ContextSensitivity aContext)
+{
+ // It doesn't really matter what we return here, but it might
+ // be a good idea to try to keep this as unoptimizable as possible
+ return true;
+}
+
+#ifdef TX_TO_STRING
+void
+txErrorExpr::toString(nsAString& aStr)
+{
+ aStr.Append(mStr);
+}
+#endif
diff --git a/dom/xslt/xpath/txExpr.cpp b/dom/xslt/xpath/txExpr.cpp
new file mode 100644
index 000000000..01c1ff6d3
--- /dev/null
+++ b/dom/xslt/xpath/txExpr.cpp
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "txExpr.h"
+
+nsresult
+Expr::evaluateToBool(txIEvalContext* aContext, bool& aResult)
+{
+ RefPtr<txAExprResult> exprRes;
+ nsresult rv = evaluate(aContext, getter_AddRefs(exprRes));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aResult = exprRes->booleanValue();
+
+ return NS_OK;
+}
+
+nsresult
+Expr::evaluateToString(txIEvalContext* aContext, nsString& aResult)
+{
+ RefPtr<txAExprResult> exprRes;
+ nsresult rv = evaluate(aContext, getter_AddRefs(exprRes));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ exprRes->stringValue(aResult);
+
+ return NS_OK;
+}
diff --git a/dom/xslt/xpath/txExpr.h b/dom/xslt/xpath/txExpr.h
new file mode 100644
index 000000000..562fca7a3
--- /dev/null
+++ b/dom/xslt/xpath/txExpr.h
@@ -0,0 +1,1004 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+#ifndef TRANSFRMX_EXPR_H
+#define TRANSFRMX_EXPR_H
+
+#include "mozilla/Attributes.h"
+#include "nsAutoPtr.h"
+#include "txExprResult.h"
+#include "txCore.h"
+#include "nsString.h"
+#include "txOwningArray.h"
+#include "nsIAtom.h"
+
+#ifdef DEBUG
+#define TX_TO_STRING
+#endif
+
+/*
+ XPath class definitions.
+ Much of this code was ported from XSL:P.
+*/
+
+class nsIAtom;
+class txIMatchContext;
+class txIEvalContext;
+class txNodeSet;
+class txXPathNode;
+
+/**
+ * A Base Class for all XSL Expressions
+**/
+class Expr
+{
+public:
+ Expr()
+ {
+ MOZ_COUNT_CTOR(Expr);
+ }
+ virtual ~Expr()
+ {
+ MOZ_COUNT_DTOR(Expr);
+ }
+
+ /**
+ * Evaluates this Expr based on the given context node and processor state
+ * @param context the context node for evaluation of this Expr
+ * @param ps the ContextState containing the stack information needed
+ * for evaluation
+ * @return the result of the evaluation
+ **/
+ virtual nsresult evaluate(txIEvalContext* aContext,
+ txAExprResult** aResult) = 0;
+
+
+ /**
+ * Returns the type of this expression.
+ */
+ enum ExprType {
+ LOCATIONSTEP_EXPR,
+ PATH_EXPR,
+ UNION_EXPR,
+ LITERAL_EXPR,
+ OTHER_EXPR
+ };
+ virtual ExprType getType()
+ {
+ return OTHER_EXPR;
+ }
+
+ /**
+ * Returns the type or types of results this Expr return.
+ */
+ typedef uint16_t ResultType;
+ enum {
+ NODESET_RESULT = 0x01,
+ BOOLEAN_RESULT = 0x02,
+ NUMBER_RESULT = 0x04,
+ STRING_RESULT = 0x08,
+ RTF_RESULT = 0x10,
+ ANY_RESULT = 0xFFFF
+ };
+ virtual ResultType getReturnType() = 0;
+ bool canReturnType(ResultType aType)
+ {
+ return (getReturnType() & aType) != 0;
+ }
+
+ typedef uint16_t ContextSensitivity;
+ enum {
+ NO_CONTEXT = 0x00,
+ NODE_CONTEXT = 0x01,
+ POSITION_CONTEXT = 0x02,
+ SIZE_CONTEXT = 0x04,
+ NODESET_CONTEXT = POSITION_CONTEXT | SIZE_CONTEXT,
+ VARIABLES_CONTEXT = 0x08,
+ PRIVATE_CONTEXT = 0x10,
+ ANY_CONTEXT = 0xFFFF
+ };
+
+ /**
+ * Returns true if this expression is sensitive to *any* of
+ * the requested contexts in aContexts.
+ */
+ virtual bool isSensitiveTo(ContextSensitivity aContexts) = 0;
+
+ /**
+ * Returns sub-expression at given position
+ */
+ virtual Expr* getSubExprAt(uint32_t aPos) = 0;
+
+ /**
+ * Replace sub-expression at given position. Does not delete the old
+ * expression, that is the responsibility of the caller.
+ */
+ virtual void setSubExprAt(uint32_t aPos, Expr* aExpr) = 0;
+
+ virtual nsresult evaluateToBool(txIEvalContext* aContext,
+ bool& aResult);
+
+ virtual nsresult evaluateToString(txIEvalContext* aContext,
+ nsString& aResult);
+
+#ifdef TX_TO_STRING
+ /**
+ * Returns the String representation of this Expr.
+ * @param dest the String to use when creating the String
+ * representation. The String representation will be appended to
+ * any data in the destination String, to allow cascading calls to
+ * other #toString() methods for Expressions.
+ * @return the String representation of this Expr.
+ **/
+ virtual void toString(nsAString& str) = 0;
+#endif
+}; //-- Expr
+
+#ifdef TX_TO_STRING
+#define TX_DECL_TOSTRING \
+ void toString(nsAString& aDest) override;
+#define TX_DECL_GETNAMEATOM \
+ nsresult getNameAtom(nsIAtom** aAtom) override;
+#else
+#define TX_DECL_TOSTRING
+#define TX_DECL_GETNAMEATOM
+#endif
+
+#define TX_DECL_EXPR_BASE \
+ nsresult evaluate(txIEvalContext* aContext, txAExprResult** aResult) override; \
+ ResultType getReturnType() override; \
+ bool isSensitiveTo(ContextSensitivity aContexts) override;
+
+#define TX_DECL_EXPR \
+ TX_DECL_EXPR_BASE \
+ TX_DECL_TOSTRING \
+ Expr* getSubExprAt(uint32_t aPos) override; \
+ void setSubExprAt(uint32_t aPos, Expr* aExpr) override;
+
+#define TX_DECL_OPTIMIZABLE_EXPR \
+ TX_DECL_EXPR \
+ ExprType getType() override;
+
+#define TX_DECL_FUNCTION \
+ TX_DECL_GETNAMEATOM \
+ TX_DECL_EXPR_BASE
+
+#define TX_IMPL_EXPR_STUBS_BASE(_class, _ReturnType) \
+Expr::ResultType \
+_class::getReturnType() \
+{ \
+ return _ReturnType; \
+}
+
+#define TX_IMPL_EXPR_STUBS_0(_class, _ReturnType) \
+TX_IMPL_EXPR_STUBS_BASE(_class, _ReturnType) \
+Expr* \
+_class::getSubExprAt(uint32_t aPos) \
+{ \
+ return nullptr; \
+} \
+void \
+_class::setSubExprAt(uint32_t aPos, Expr* aExpr) \
+{ \
+ NS_NOTREACHED("setting bad subexpression index"); \
+}
+
+#define TX_IMPL_EXPR_STUBS_1(_class, _ReturnType, _Expr1) \
+TX_IMPL_EXPR_STUBS_BASE(_class, _ReturnType) \
+Expr* \
+_class::getSubExprAt(uint32_t aPos) \
+{ \
+ if (aPos == 0) { \
+ return _Expr1; \
+ } \
+ return nullptr; \
+} \
+void \
+_class::setSubExprAt(uint32_t aPos, Expr* aExpr) \
+{ \
+ NS_ASSERTION(aPos < 1, "setting bad subexpression index");\
+ _Expr1.forget(); \
+ _Expr1 = aExpr; \
+}
+
+#define TX_IMPL_EXPR_STUBS_2(_class, _ReturnType, _Expr1, _Expr2) \
+TX_IMPL_EXPR_STUBS_BASE(_class, _ReturnType) \
+Expr* \
+_class::getSubExprAt(uint32_t aPos) \
+{ \
+ switch(aPos) { \
+ case 0: \
+ return _Expr1; \
+ case 1: \
+ return _Expr2; \
+ default: \
+ break; \
+ } \
+ return nullptr; \
+} \
+void \
+_class::setSubExprAt(uint32_t aPos, Expr* aExpr) \
+{ \
+ NS_ASSERTION(aPos < 2, "setting bad subexpression index");\
+ if (aPos == 0) { \
+ _Expr1.forget(); \
+ _Expr1 = aExpr; \
+ } \
+ else { \
+ _Expr2.forget(); \
+ _Expr2 = aExpr; \
+ } \
+}
+
+#define TX_IMPL_EXPR_STUBS_LIST(_class, _ReturnType, _ExprList) \
+TX_IMPL_EXPR_STUBS_BASE(_class, _ReturnType) \
+Expr* \
+_class::getSubExprAt(uint32_t aPos) \
+{ \
+ return _ExprList.SafeElementAt(aPos); \
+} \
+void \
+_class::setSubExprAt(uint32_t aPos, Expr* aExpr) \
+{ \
+ NS_ASSERTION(aPos < _ExprList.Length(), \
+ "setting bad subexpression index"); \
+ _ExprList[aPos] = aExpr; \
+}
+
+
+/**
+ * This class represents a FunctionCall as defined by the XPath 1.0
+ * Recommendation.
+**/
+class FunctionCall : public Expr
+{
+public:
+ /**
+ * Adds the given parameter to this FunctionCall's parameter list.
+ * The ownership of the given Expr is passed over to the FunctionCall,
+ * even on failure.
+ * @param aExpr the Expr to add to this FunctionCall's parameter list
+ * @return nsresult indicating out of memory
+ */
+ nsresult addParam(Expr* aExpr)
+ {
+ return mParams.AppendElement(aExpr) ?
+ NS_OK : NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ /**
+ * Check if the number of parameters falls within a range.
+ *
+ * @param aParamCountMin minimum number of required parameters.
+ * @param aParamCountMax maximum number of parameters. If aParamCountMax
+ * is negative the maximum number is not checked.
+ * @return boolean representing whether the number of parameters falls
+ * within the expected range or not.
+ *
+ * XXX txIEvalContext should be txIParseContest, bug 143291
+ */
+ virtual bool requireParams(int32_t aParamCountMin,
+ int32_t aParamCountMax,
+ txIEvalContext* aContext);
+
+ TX_DECL_TOSTRING
+ Expr* getSubExprAt(uint32_t aPos) override;
+ void setSubExprAt(uint32_t aPos, Expr* aExpr) override;
+
+protected:
+
+ txOwningArray<Expr> mParams;
+
+ /*
+ * Evaluates the given Expression and converts its result to a number.
+ */
+ static nsresult evaluateToNumber(Expr* aExpr, txIEvalContext* aContext,
+ double* aResult);
+
+ /*
+ * Evaluates the given Expression and converts its result to a NodeSet.
+ * If the result is not a NodeSet an error is returned.
+ */
+ static nsresult evaluateToNodeSet(Expr* aExpr, txIEvalContext* aContext,
+ txNodeSet** aResult);
+
+ /**
+ * Returns true if any argument is sensitive to the given context.
+ */
+ bool argsSensitiveTo(ContextSensitivity aContexts);
+
+
+#ifdef TX_TO_STRING
+ /*
+ * Returns the name of the function as an atom.
+ */
+ virtual nsresult getNameAtom(nsIAtom** aAtom) = 0;
+#endif
+};
+
+class txCoreFunctionCall : public FunctionCall
+{
+public:
+
+ // This must be ordered in the same order as descriptTable in
+ // txCoreFunctionCall.cpp. If you change one, change the other.
+ enum eType {
+ COUNT = 0, // count()
+ ID, // id()
+ LAST, // last()
+ LOCAL_NAME, // local-name()
+ NAMESPACE_URI, // namespace-uri()
+ NAME, // name()
+ POSITION, // position()
+
+ CONCAT, // concat()
+ CONTAINS, // contains()
+ NORMALIZE_SPACE, // normalize-space()
+ STARTS_WITH, // starts-with()
+ STRING, // string()
+ STRING_LENGTH, // string-length()
+ SUBSTRING, // substring()
+ SUBSTRING_AFTER, // substring-after()
+ SUBSTRING_BEFORE, // substring-before()
+ TRANSLATE, // translate()
+
+ NUMBER, // number()
+ ROUND, // round()
+ FLOOR, // floor()
+ CEILING, // ceiling()
+ SUM, // sum()
+
+ BOOLEAN, // boolean()
+ _FALSE, // false()
+ LANG, // lang()
+ _NOT, // not()
+ _TRUE // true()
+ };
+
+ /*
+ * Creates a txCoreFunctionCall of the given type
+ */
+ explicit txCoreFunctionCall(eType aType) : mType(aType)
+ {
+ }
+
+ TX_DECL_FUNCTION
+
+ static bool getTypeFromAtom(nsIAtom* aName, eType& aType);
+
+private:
+ eType mType;
+};
+
+
+/*
+ * This class represents a NodeTest as defined by the XPath spec
+ */
+class txNodeTest
+{
+public:
+ txNodeTest()
+ {
+ MOZ_COUNT_CTOR(txNodeTest);
+ }
+ virtual ~txNodeTest()
+ {
+ MOZ_COUNT_DTOR(txNodeTest);
+ }
+
+ /*
+ * Virtual methods
+ * pretty much a txPattern, but not supposed to be used
+ * standalone. The NodeTest node() is different to the
+ * Pattern "node()" (document node isn't matched)
+ */
+ virtual bool matches(const txXPathNode& aNode,
+ txIMatchContext* aContext) = 0;
+ virtual double getDefaultPriority() = 0;
+
+ /**
+ * Returns the type of this nodetest.
+ */
+ enum NodeTestType {
+ NAME_TEST,
+ NODETYPE_TEST,
+ OTHER_TEST
+ };
+ virtual NodeTestType getType()
+ {
+ return OTHER_TEST;
+ }
+
+ /**
+ * Returns true if this expression is sensitive to *any* of
+ * the requested flags.
+ */
+ virtual bool isSensitiveTo(Expr::ContextSensitivity aContext) = 0;
+
+#ifdef TX_TO_STRING
+ virtual void toString(nsAString& aDest) = 0;
+#endif
+};
+
+#define TX_DECL_NODE_TEST \
+ TX_DECL_TOSTRING \
+ bool matches(const txXPathNode& aNode, txIMatchContext* aContext) override; \
+ double getDefaultPriority() override; \
+ bool isSensitiveTo(Expr::ContextSensitivity aContext) override;
+
+/*
+ * This class represents a NameTest as defined by the XPath spec
+ */
+class txNameTest : public txNodeTest
+{
+public:
+ /*
+ * Creates a new txNameTest with the given type and the given
+ * principal node type
+ */
+ txNameTest(nsIAtom* aPrefix, nsIAtom* aLocalName, int32_t aNSID,
+ uint16_t aNodeType);
+
+ NodeTestType getType() override;
+
+ TX_DECL_NODE_TEST
+
+ nsCOMPtr<nsIAtom> mPrefix;
+ nsCOMPtr<nsIAtom> mLocalName;
+ int32_t mNamespace;
+private:
+ uint16_t mNodeType;
+};
+
+/*
+ * This class represents a NodeType as defined by the XPath spec
+ */
+class txNodeTypeTest : public txNodeTest
+{
+public:
+ enum NodeType {
+ COMMENT_TYPE,
+ TEXT_TYPE,
+ PI_TYPE,
+ NODE_TYPE
+ };
+
+ /*
+ * Creates a new txNodeTypeTest of the given type
+ */
+ explicit txNodeTypeTest(NodeType aNodeType)
+ : mNodeType(aNodeType)
+ {
+ }
+
+ /*
+ * Sets the name of the node to match. Only availible for pi nodes
+ */
+ void setNodeName(const nsAString& aName)
+ {
+ mNodeName = NS_Atomize(aName);
+ }
+
+ NodeType getNodeTestType()
+ {
+ return mNodeType;
+ }
+
+ NodeTestType getType() override;
+
+ TX_DECL_NODE_TEST
+
+private:
+ NodeType mNodeType;
+ nsCOMPtr<nsIAtom> mNodeName;
+};
+
+/**
+ * Class representing a nodetest combined with a predicate. May only be used
+ * if the predicate is not sensitive to the context-nodelist.
+ */
+class txPredicatedNodeTest : public txNodeTest
+{
+public:
+ txPredicatedNodeTest(txNodeTest* aNodeTest, Expr* aPredicate);
+ TX_DECL_NODE_TEST
+
+private:
+ nsAutoPtr<txNodeTest> mNodeTest;
+ nsAutoPtr<Expr> mPredicate;
+};
+
+/**
+ * Represents an ordered list of Predicates,
+ * for use with Step and Filter Expressions
+**/
+class PredicateList {
+public:
+ /**
+ * Adds the given Expr to the list.
+ * The ownership of the given Expr is passed over the PredicateList,
+ * even on failure.
+ * @param aExpr the Expr to add to the list
+ * @return nsresult indicating out of memory
+ */
+ nsresult add(Expr* aExpr)
+ {
+ NS_ASSERTION(aExpr, "missing expression");
+ return mPredicates.AppendElement(aExpr) ?
+ NS_OK : NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ nsresult evaluatePredicates(txNodeSet* aNodes, txIMatchContext* aContext);
+
+ /**
+ * Drops the first predicate without deleting it.
+ */
+ void dropFirst()
+ {
+ mPredicates.RemoveElementAt(0);
+ }
+
+ /**
+ * returns true if this predicate list is empty
+ **/
+ bool isEmpty()
+ {
+ return mPredicates.IsEmpty();
+ }
+
+#ifdef TX_TO_STRING
+ /**
+ * Returns the String representation of this PredicateList.
+ * @param dest the String to use when creating the String
+ * representation. The String representation will be appended to
+ * any data in the destination String, to allow cascading calls to
+ * other #toString() methods for Expressions.
+ * @return the String representation of this PredicateList.
+ **/
+ void toString(nsAString& dest);
+#endif
+
+protected:
+ bool isSensitiveTo(Expr::ContextSensitivity aContext);
+ Expr* getSubExprAt(uint32_t aPos)
+ {
+ return mPredicates.SafeElementAt(aPos);
+ }
+ void setSubExprAt(uint32_t aPos, Expr* aExpr)
+ {
+ NS_ASSERTION(aPos < mPredicates.Length(),
+ "setting bad subexpression index");
+ mPredicates[aPos] = aExpr;
+ }
+
+ //-- list of predicates
+ txOwningArray<Expr> mPredicates;
+}; //-- PredicateList
+
+class LocationStep : public Expr,
+ public PredicateList
+{
+public:
+ enum LocationStepType {
+ ANCESTOR_AXIS = 0,
+ ANCESTOR_OR_SELF_AXIS,
+ ATTRIBUTE_AXIS,
+ CHILD_AXIS,
+ DESCENDANT_AXIS,
+ DESCENDANT_OR_SELF_AXIS,
+ FOLLOWING_AXIS,
+ FOLLOWING_SIBLING_AXIS,
+ NAMESPACE_AXIS,
+ PARENT_AXIS,
+ PRECEDING_AXIS,
+ PRECEDING_SIBLING_AXIS,
+ SELF_AXIS
+ };
+
+ /**
+ * Creates a new LocationStep using the given NodeExpr and Axis Identifier
+ * @param nodeExpr the NodeExpr to use when matching Nodes
+ * @param axisIdentifier the Axis Identifier in which to search for nodes
+ **/
+ LocationStep(txNodeTest* aNodeTest,
+ LocationStepType aAxisIdentifier)
+ : mNodeTest(aNodeTest),
+ mAxisIdentifier(aAxisIdentifier)
+ {
+ }
+
+ TX_DECL_OPTIMIZABLE_EXPR
+
+ txNodeTest* getNodeTest()
+ {
+ return mNodeTest;
+ }
+ void setNodeTest(txNodeTest* aNodeTest)
+ {
+ mNodeTest.forget();
+ mNodeTest = aNodeTest;
+ }
+ LocationStepType getAxisIdentifier()
+ {
+ return mAxisIdentifier;
+ }
+ void setAxisIdentifier(LocationStepType aAxisIdentifier)
+ {
+ mAxisIdentifier = aAxisIdentifier;
+ }
+
+private:
+ void fromDescendants(const txXPathNode& aNode, txIMatchContext* aCs,
+ txNodeSet* aNodes);
+ void fromDescendantsRev(const txXPathNode& aNode, txIMatchContext* aCs,
+ txNodeSet* aNodes);
+
+ nsAutoPtr<txNodeTest> mNodeTest;
+ LocationStepType mAxisIdentifier;
+};
+
+class FilterExpr : public Expr,
+ public PredicateList
+{
+public:
+
+ /**
+ * Creates a new FilterExpr using the given Expr
+ * @param expr the Expr to use for evaluation
+ */
+ explicit FilterExpr(Expr* aExpr)
+ : expr(aExpr)
+ {
+ }
+
+ TX_DECL_EXPR
+
+private:
+ nsAutoPtr<Expr> expr;
+
+}; //-- FilterExpr
+
+
+class txLiteralExpr : public Expr {
+public:
+ explicit txLiteralExpr(double aDbl)
+ : mValue(new NumberResult(aDbl, nullptr))
+ {
+ }
+ explicit txLiteralExpr(const nsAString& aStr)
+ : mValue(new StringResult(aStr, nullptr))
+ {
+ }
+ explicit txLiteralExpr(txAExprResult* aValue)
+ : mValue(aValue)
+ {
+ }
+
+ TX_DECL_EXPR
+
+private:
+ RefPtr<txAExprResult> mValue;
+};
+
+/**
+ * Represents an UnaryExpr. Returns the negative value of its expr.
+**/
+class UnaryExpr : public Expr {
+
+public:
+
+ explicit UnaryExpr(Expr* aExpr)
+ : expr(aExpr)
+ {
+ }
+
+ TX_DECL_EXPR
+
+private:
+ nsAutoPtr<Expr> expr;
+}; //-- UnaryExpr
+
+/**
+ * Represents a BooleanExpr, a binary expression that
+ * performs a boolean operation between its lvalue and rvalue.
+**/
+class BooleanExpr : public Expr
+{
+public:
+
+ //-- BooleanExpr Types
+ enum _BooleanExprType { AND = 1, OR };
+
+ BooleanExpr(Expr* aLeftExpr, Expr* aRightExpr, short aOp)
+ : leftExpr(aLeftExpr),
+ rightExpr(aRightExpr),
+ op(aOp)
+ {
+ }
+
+ TX_DECL_EXPR
+
+private:
+ nsAutoPtr<Expr> leftExpr, rightExpr;
+ short op;
+}; //-- BooleanExpr
+
+/**
+ * Represents a MultiplicativeExpr, a binary expression that
+ * performs a multiplicative operation between its lvalue and rvalue:
+ * * : multiply
+ * mod : modulus
+ * div : divide
+ *
+**/
+class txNumberExpr : public Expr
+{
+public:
+
+ enum eOp { ADD, SUBTRACT, DIVIDE, MULTIPLY, MODULUS };
+
+ txNumberExpr(Expr* aLeftExpr, Expr* aRightExpr, eOp aOp)
+ : mLeftExpr(aLeftExpr),
+ mRightExpr(aRightExpr),
+ mOp(aOp)
+ {
+ }
+
+ TX_DECL_EXPR
+
+private:
+ nsAutoPtr<Expr> mLeftExpr, mRightExpr;
+ eOp mOp;
+}; //-- MultiplicativeExpr
+
+/**
+ * Represents a RelationalExpr, an expression that compares its lvalue
+ * to its rvalue using:
+ * = : equal to
+ * < : less than
+ * > : greater than
+ * <= : less than or equal to
+ * >= : greater than or equal to
+ *
+**/
+class RelationalExpr : public Expr
+{
+public:
+ enum RelationalExprType {
+ EQUAL,
+ NOT_EQUAL,
+ LESS_THAN,
+ GREATER_THAN,
+ LESS_OR_EQUAL,
+ GREATER_OR_EQUAL
+ };
+
+ RelationalExpr(Expr* aLeftExpr, Expr* aRightExpr, RelationalExprType aOp)
+ : mLeftExpr(aLeftExpr),
+ mRightExpr(aRightExpr),
+ mOp(aOp)
+ {
+ }
+
+
+ TX_DECL_EXPR
+
+private:
+ bool compareResults(txIEvalContext* aContext, txAExprResult* aLeft,
+ txAExprResult* aRight);
+
+ nsAutoPtr<Expr> mLeftExpr;
+ nsAutoPtr<Expr> mRightExpr;
+ RelationalExprType mOp;
+};
+
+/**
+ * VariableRefExpr
+ * Represents a variable reference ($refname)
+**/
+class VariableRefExpr : public Expr {
+
+public:
+
+ VariableRefExpr(nsIAtom* aPrefix, nsIAtom* aLocalName, int32_t aNSID);
+
+ TX_DECL_EXPR
+
+private:
+ nsCOMPtr<nsIAtom> mPrefix;
+ nsCOMPtr<nsIAtom> mLocalName;
+ int32_t mNamespace;
+};
+
+/**
+ * Represents a PathExpr
+**/
+class PathExpr : public Expr {
+
+public:
+
+ //-- Path Operators
+ //-- RELATIVE_OP is the default
+ //-- LF, changed from static const short to enum
+ enum PathOperator { RELATIVE_OP, DESCENDANT_OP };
+
+ /**
+ * Adds the Expr to this PathExpr
+ * The ownership of the given Expr is passed over the PathExpr,
+ * even on failure.
+ * @param aExpr the Expr to add to this PathExpr
+ * @return nsresult indicating out of memory
+ */
+ nsresult addExpr(Expr* aExpr, PathOperator pathOp);
+
+ /**
+ * Removes and deletes the expression at the given index.
+ */
+ void deleteExprAt(uint32_t aPos)
+ {
+ NS_ASSERTION(aPos < mItems.Length(),
+ "killing bad expression index");
+ mItems.RemoveElementAt(aPos);
+ }
+
+ TX_DECL_OPTIMIZABLE_EXPR
+
+ PathOperator getPathOpAt(uint32_t aPos)
+ {
+ NS_ASSERTION(aPos < mItems.Length(), "getting bad pathop index");
+ return mItems[aPos].pathOp;
+ }
+ void setPathOpAt(uint32_t aPos, PathOperator aPathOp)
+ {
+ NS_ASSERTION(aPos < mItems.Length(), "setting bad pathop index");
+ mItems[aPos].pathOp = aPathOp;
+ }
+
+private:
+ class PathExprItem {
+ public:
+ nsAutoPtr<Expr> expr;
+ PathOperator pathOp;
+ };
+
+ nsTArray<PathExprItem> mItems;
+
+ /*
+ * Selects from the descendants of the context node
+ * all nodes that match the Expr
+ */
+ nsresult evalDescendants(Expr* aStep, const txXPathNode& aNode,
+ txIMatchContext* aContext,
+ txNodeSet* resNodes);
+};
+
+/**
+ * This class represents a RootExpr, which only matches the Document node
+**/
+class RootExpr : public Expr {
+public:
+ /**
+ * Creates a new RootExpr
+ */
+ RootExpr()
+#ifdef TX_TO_STRING
+ : mSerialize(true)
+#endif
+ {
+ }
+
+ TX_DECL_EXPR
+
+#ifdef TX_TO_STRING
+public:
+ void setSerialize(bool aSerialize)
+ {
+ mSerialize = aSerialize;
+ }
+
+private:
+ // When a RootExpr is used in a PathExpr it shouldn't be serialized
+ bool mSerialize;
+#endif
+}; //-- RootExpr
+
+/**
+ * Represents a UnionExpr
+**/
+class UnionExpr : public Expr {
+public:
+ /**
+ * Adds the PathExpr to this UnionExpr
+ * The ownership of the given Expr is passed over the UnionExpr,
+ * even on failure.
+ * @param aExpr the Expr to add to this UnionExpr
+ * @return nsresult indicating out of memory
+ */
+ nsresult addExpr(Expr* aExpr)
+ {
+ return mExpressions.AppendElement(aExpr) ?
+ NS_OK : NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ /**
+ * Removes and deletes the expression at the given index.
+ */
+ void deleteExprAt(uint32_t aPos)
+ {
+ NS_ASSERTION(aPos < mExpressions.Length(),
+ "killing bad expression index");
+
+ delete mExpressions[aPos];
+ mExpressions.RemoveElementAt(aPos);
+ }
+
+ TX_DECL_OPTIMIZABLE_EXPR
+
+private:
+
+ txOwningArray<Expr> mExpressions;
+
+}; //-- UnionExpr
+
+/**
+ * Class specializing in executing expressions like "@foo" where we are
+ * interested in different result-types, and expressions like "@foo = 'hi'"
+ */
+class txNamedAttributeStep : public Expr
+{
+public:
+ txNamedAttributeStep(int32_t aNsID, nsIAtom* aPrefix,
+ nsIAtom* aLocalName);
+
+ TX_DECL_EXPR
+
+private:
+ int32_t mNamespace;
+ nsCOMPtr<nsIAtom> mPrefix;
+ nsCOMPtr<nsIAtom> mLocalName;
+};
+
+/**
+ *
+ */
+class txUnionNodeTest : public txNodeTest
+{
+public:
+ nsresult addNodeTest(txNodeTest* aNodeTest)
+ {
+ return mNodeTests.AppendElement(aNodeTest) ?
+ NS_OK : NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ TX_DECL_NODE_TEST
+
+private:
+ txOwningArray<txNodeTest> mNodeTests;
+};
+
+/**
+ * Expression that failed to parse
+ */
+class txErrorExpr : public Expr
+{
+public:
+#ifdef TX_TO_STRING
+ explicit txErrorExpr(const nsAString& aStr)
+ : mStr(aStr)
+ {
+ }
+#endif
+
+ TX_DECL_EXPR
+
+#ifdef TX_TO_STRING
+private:
+ nsString mStr;
+#endif
+};
+
+#endif
+
+
diff --git a/dom/xslt/xpath/txExprLexer.cpp b/dom/xslt/xpath/txExprLexer.cpp
new file mode 100644
index 000000000..dc1616af0
--- /dev/null
+++ b/dom/xslt/xpath/txExprLexer.cpp
@@ -0,0 +1,367 @@
+/* -*- Mode: C++; tab-width: 2; 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/. */
+
+/**
+ * Lexical analyzer for XPath expressions
+ */
+
+#include "txExprLexer.h"
+#include "nsGkAtoms.h"
+#include "nsString.h"
+#include "nsError.h"
+#include "txXMLUtils.h"
+
+/**
+ * Creates a new ExprLexer
+ */
+txExprLexer::txExprLexer()
+ : mCurrentItem(nullptr),
+ mFirstItem(nullptr),
+ mLastItem(nullptr),
+ mTokenCount(0)
+{
+}
+
+/**
+ * Destroys this instance of an txExprLexer
+ */
+txExprLexer::~txExprLexer()
+{
+ //-- delete tokens
+ Token* tok = mFirstItem;
+ while (tok) {
+ Token* temp = tok->mNext;
+ delete tok;
+ tok = temp;
+ }
+ mCurrentItem = nullptr;
+}
+
+Token*
+txExprLexer::nextToken()
+{
+ if (!mCurrentItem) {
+ NS_NOTREACHED("nextToken called on uninitialized lexer");
+ return nullptr;
+ }
+
+ if (mCurrentItem->mType == Token::END) {
+ // Do not progress beyond the end token
+ return mCurrentItem;
+ }
+
+ Token* token = mCurrentItem;
+ mCurrentItem = mCurrentItem->mNext;
+ return token;
+}
+
+void
+txExprLexer::addToken(Token* aToken)
+{
+ if (mLastItem) {
+ mLastItem->mNext = aToken;
+ }
+ if (!mFirstItem) {
+ mFirstItem = aToken;
+ mCurrentItem = aToken;
+ }
+ mLastItem = aToken;
+ ++mTokenCount;
+}
+
+/**
+ * Returns true if the following Token should be an operator.
+ * This is a helper for the first bullet of [XPath 3.7]
+ * Lexical Structure
+ */
+bool
+txExprLexer::nextIsOperatorToken(Token* aToken)
+{
+ if (!aToken || aToken->mType == Token::NULL_TOKEN) {
+ return false;
+ }
+ /* This relies on the tokens having the right order in txExprLexer.h */
+ return aToken->mType < Token::COMMA ||
+ aToken->mType > Token::UNION_OP;
+
+}
+
+/**
+ * Parses the given string into a sequence of Tokens
+ */
+nsresult
+txExprLexer::parse(const nsASingleFragmentString& aPattern)
+{
+ iterator start, end;
+ start = aPattern.BeginReading(mPosition);
+ aPattern.EndReading(end);
+
+ //-- initialize previous token, this will automatically get
+ //-- deleted when it goes out of scope
+ Token nullToken(nullptr, nullptr, Token::NULL_TOKEN);
+
+ Token::Type defType;
+ Token* newToken = nullptr;
+ Token* prevToken = &nullToken;
+ bool isToken;
+
+ while (mPosition < end) {
+
+ defType = Token::CNAME;
+ isToken = true;
+
+ if (*mPosition == DOLLAR_SIGN) {
+ if (++mPosition == end || !XMLUtils::isLetter(*mPosition)) {
+ return NS_ERROR_XPATH_INVALID_VAR_NAME;
+ }
+ defType = Token::VAR_REFERENCE;
+ }
+ // just reuse the QName parsing, which will use defType
+ // the token to construct
+
+ if (XMLUtils::isLetter(*mPosition)) {
+ // NCName, can get QName or OperatorName;
+ // FunctionName, NodeName, and AxisSpecifier may want whitespace,
+ // and are dealt with below
+ start = mPosition;
+ while (++mPosition < end && XMLUtils::isNCNameChar(*mPosition)) {
+ /* just go */
+ }
+ if (mPosition < end && *mPosition == COLON) {
+ // try QName or wildcard, might need to step back for axis
+ if (++mPosition == end) {
+ return NS_ERROR_XPATH_UNEXPECTED_END;
+ }
+ if (XMLUtils::isLetter(*mPosition)) {
+ while (++mPosition < end && XMLUtils::isNCNameChar(*mPosition)) {
+ /* just go */
+ }
+ }
+ else if (*mPosition == '*' && defType != Token::VAR_REFERENCE) {
+ // eat wildcard for NameTest, bail for var ref at COLON
+ ++mPosition;
+ }
+ else {
+ --mPosition; // step back
+ }
+ }
+ if (nextIsOperatorToken(prevToken)) {
+ nsDependentSubstring op(Substring(start, mPosition));
+ if (nsGkAtoms::_and->Equals(op)) {
+ defType = Token::AND_OP;
+ }
+ else if (nsGkAtoms::_or->Equals(op)) {
+ defType = Token::OR_OP;
+ }
+ else if (nsGkAtoms::mod->Equals(op)) {
+ defType = Token::MODULUS_OP;
+ }
+ else if (nsGkAtoms::div->Equals(op)) {
+ defType = Token::DIVIDE_OP;
+ }
+ else {
+ // XXX QUESTION: spec is not too precise
+ // badops is sure an error, but is bad:ops, too? We say yes!
+ return NS_ERROR_XPATH_OPERATOR_EXPECTED;
+ }
+ }
+ newToken = new Token(start, mPosition, defType);
+ }
+ else if (isXPathDigit(*mPosition)) {
+ start = mPosition;
+ while (++mPosition < end && isXPathDigit(*mPosition)) {
+ /* just go */
+ }
+ if (mPosition < end && *mPosition == '.') {
+ while (++mPosition < end && isXPathDigit(*mPosition)) {
+ /* just go */
+ }
+ }
+ newToken = new Token(start, mPosition, Token::NUMBER);
+ }
+ else {
+ switch (*mPosition) {
+ //-- ignore whitespace
+ case SPACE:
+ case TX_TAB:
+ case TX_CR:
+ case TX_LF:
+ ++mPosition;
+ isToken = false;
+ break;
+ case S_QUOTE :
+ case D_QUOTE :
+ start = mPosition;
+ while (++mPosition < end && *mPosition != *start) {
+ // eat literal
+ }
+ if (mPosition == end) {
+ mPosition = start;
+ return NS_ERROR_XPATH_UNCLOSED_LITERAL;
+ }
+ newToken = new Token(start + 1, mPosition, Token::LITERAL);
+ ++mPosition;
+ break;
+ case PERIOD:
+ // period can be .., .(DIGITS)+ or ., check next
+ if (++mPosition == end) {
+ newToken = new Token(mPosition - 1, Token::SELF_NODE);
+ }
+ else if (isXPathDigit(*mPosition)) {
+ start = mPosition - 1;
+ while (++mPosition < end && isXPathDigit(*mPosition)) {
+ /* just go */
+ }
+ newToken = new Token(start, mPosition, Token::NUMBER);
+ }
+ else if (*mPosition == PERIOD) {
+ ++mPosition;
+ newToken = new Token(mPosition - 2, mPosition, Token::PARENT_NODE);
+ }
+ else {
+ newToken = new Token(mPosition - 1, Token::SELF_NODE);
+ }
+ break;
+ case COLON: // QNames are dealt above, must be axis ident
+ if (++mPosition >= end || *mPosition != COLON ||
+ prevToken->mType != Token::CNAME) {
+ return NS_ERROR_XPATH_BAD_COLON;
+ }
+ prevToken->mType = Token::AXIS_IDENTIFIER;
+ ++mPosition;
+ isToken = false;
+ break;
+ case FORWARD_SLASH :
+ if (++mPosition < end && *mPosition == FORWARD_SLASH) {
+ ++mPosition;
+ newToken = new Token(mPosition - 2, mPosition, Token::ANCESTOR_OP);
+ }
+ else {
+ newToken = new Token(mPosition - 1, Token::PARENT_OP);
+ }
+ break;
+ case BANG : // can only be !=
+ if (++mPosition < end && *mPosition == EQUAL) {
+ ++mPosition;
+ newToken = new Token(mPosition - 2, mPosition, Token::NOT_EQUAL_OP);
+ break;
+ }
+ // Error ! is not not()
+ return NS_ERROR_XPATH_BAD_BANG;
+ case EQUAL:
+ newToken = new Token(mPosition, Token::EQUAL_OP);
+ ++mPosition;
+ break;
+ case L_ANGLE:
+ if (++mPosition == end) {
+ return NS_ERROR_XPATH_UNEXPECTED_END;
+ }
+ if (*mPosition == EQUAL) {
+ ++mPosition;
+ newToken = new Token(mPosition - 2, mPosition,
+ Token::LESS_OR_EQUAL_OP);
+ }
+ else {
+ newToken = new Token(mPosition - 1, Token::LESS_THAN_OP);
+ }
+ break;
+ case R_ANGLE:
+ if (++mPosition == end) {
+ return NS_ERROR_XPATH_UNEXPECTED_END;
+ }
+ if (*mPosition == EQUAL) {
+ ++mPosition;
+ newToken = new Token(mPosition - 2, mPosition,
+ Token::GREATER_OR_EQUAL_OP);
+ }
+ else {
+ newToken = new Token(mPosition - 1, Token::GREATER_THAN_OP);
+ }
+ break;
+ case HYPHEN :
+ newToken = new Token(mPosition, Token::SUBTRACTION_OP);
+ ++mPosition;
+ break;
+ case ASTERISK:
+ if (nextIsOperatorToken(prevToken)) {
+ newToken = new Token(mPosition, Token::MULTIPLY_OP);
+ }
+ else {
+ newToken = new Token(mPosition, Token::CNAME);
+ }
+ ++mPosition;
+ break;
+ case L_PAREN:
+ if (prevToken->mType == Token::CNAME) {
+ const nsDependentSubstring& val = prevToken->Value();
+ if (val.EqualsLiteral("comment")) {
+ prevToken->mType = Token::COMMENT_AND_PAREN;
+ }
+ else if (val.EqualsLiteral("node")) {
+ prevToken->mType = Token::NODE_AND_PAREN;
+ }
+ else if (val.EqualsLiteral("processing-instruction")) {
+ prevToken->mType = Token::PROC_INST_AND_PAREN;
+ }
+ else if (val.EqualsLiteral("text")) {
+ prevToken->mType = Token::TEXT_AND_PAREN;
+ }
+ else {
+ prevToken->mType = Token::FUNCTION_NAME_AND_PAREN;
+ }
+ isToken = false;
+ }
+ else {
+ newToken = new Token(mPosition, Token::L_PAREN);
+ }
+ ++mPosition;
+ break;
+ case R_PAREN:
+ newToken = new Token(mPosition, Token::R_PAREN);
+ ++mPosition;
+ break;
+ case L_BRACKET:
+ newToken = new Token(mPosition, Token::L_BRACKET);
+ ++mPosition;
+ break;
+ case R_BRACKET:
+ newToken = new Token(mPosition, Token::R_BRACKET);
+ ++mPosition;
+ break;
+ case COMMA:
+ newToken = new Token(mPosition, Token::COMMA);
+ ++mPosition;
+ break;
+ case AT_SIGN :
+ newToken = new Token(mPosition, Token::AT_SIGN);
+ ++mPosition;
+ break;
+ case PLUS:
+ newToken = new Token(mPosition, Token::ADDITION_OP);
+ ++mPosition;
+ break;
+ case VERT_BAR:
+ newToken = new Token(mPosition, Token::UNION_OP);
+ ++mPosition;
+ break;
+ default:
+ // Error, don't grok character :-(
+ return NS_ERROR_XPATH_ILLEGAL_CHAR;
+ }
+ }
+ if (isToken) {
+ NS_ENSURE_TRUE(newToken, NS_ERROR_OUT_OF_MEMORY);
+ NS_ENSURE_TRUE(newToken != mLastItem, NS_ERROR_FAILURE);
+ prevToken = newToken;
+ addToken(newToken);
+ }
+ }
+
+ // add a endToken to the list
+ newToken = new Token(end, end, Token::END);
+ addToken(newToken);
+
+ return NS_OK;
+}
diff --git a/dom/xslt/xpath/txExprLexer.h b/dom/xslt/xpath/txExprLexer.h
new file mode 100644
index 000000000..7497ca505
--- /dev/null
+++ b/dom/xslt/xpath/txExprLexer.h
@@ -0,0 +1,226 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+
+#ifndef MITREXSL_EXPRLEXER_H
+#define MITREXSL_EXPRLEXER_H
+
+#include "txCore.h"
+#include "nsString.h"
+
+/**
+ * A Token class for the ExprLexer.
+ *
+ * This class was ported from XSL:P, an open source Java based
+ * XSLT processor, written by yours truly.
+ */
+class Token
+{
+public:
+
+ /**
+ * Token types
+ */
+ enum Type {
+ //-- Trivial Tokens
+ NULL_TOKEN = 1,
+ LITERAL,
+ NUMBER,
+ CNAME,
+ VAR_REFERENCE,
+ PARENT_NODE,
+ SELF_NODE,
+ R_PAREN,
+ R_BRACKET, // 9
+ /**
+ * start of tokens for 3.7, bullet 1
+ * ExprLexer::nextIsOperatorToken bails if the tokens aren't
+ * consecutive.
+ */
+ COMMA,
+ AT_SIGN,
+ L_PAREN,
+ L_BRACKET,
+ AXIS_IDENTIFIER,
+
+ // These tokens include their following left parenthesis
+ FUNCTION_NAME_AND_PAREN, // 15
+ COMMENT_AND_PAREN,
+ NODE_AND_PAREN,
+ PROC_INST_AND_PAREN,
+ TEXT_AND_PAREN,
+
+ /**
+ * operators
+ */
+ //-- boolean ops
+ AND_OP, // 20
+ OR_OP,
+
+ //-- relational
+ EQUAL_OP, // 22
+ NOT_EQUAL_OP,
+ LESS_THAN_OP,
+ GREATER_THAN_OP,
+ LESS_OR_EQUAL_OP,
+ GREATER_OR_EQUAL_OP,
+ //-- additive operators
+ ADDITION_OP, // 28
+ SUBTRACTION_OP,
+ //-- multiplicative
+ DIVIDE_OP, // 30
+ MULTIPLY_OP,
+ MODULUS_OP,
+ //-- path operators
+ PARENT_OP, // 33
+ ANCESTOR_OP,
+ UNION_OP,
+ /**
+ * end of tokens for 3.7, bullet 1 -/
+ */
+ //-- Special endtoken
+ END // 36
+ };
+
+
+ /**
+ * Constructors
+ */
+ typedef nsASingleFragmentString::const_char_iterator iterator;
+
+ Token(iterator aStart, iterator aEnd, Type aType)
+ : mStart(aStart),
+ mEnd(aEnd),
+ mType(aType),
+ mNext(nullptr)
+ {
+ }
+ Token(iterator aChar, Type aType)
+ : mStart(aChar),
+ mEnd(aChar + 1),
+ mType(aType),
+ mNext(nullptr)
+ {
+ }
+
+ const nsDependentSubstring Value()
+ {
+ return Substring(mStart, mEnd);
+ }
+
+ iterator mStart, mEnd;
+ Type mType;
+ Token* mNext;
+};
+
+/**
+ * A class for splitting an "Expr" String into tokens and
+ * performing basic Lexical Analysis.
+ *
+ * This class was ported from XSL:P, an open source Java based XSL processor
+ */
+
+class txExprLexer
+{
+public:
+
+ txExprLexer();
+ ~txExprLexer();
+
+ /**
+ * Parse the given string.
+ * returns an error result if lexing failed.
+ * The given string must outlive the use of the lexer, as the
+ * generated Tokens point to Substrings of it.
+ * mPosition points to the offending location in case of an error.
+ */
+ nsresult parse(const nsASingleFragmentString& aPattern);
+
+ typedef nsASingleFragmentString::const_char_iterator iterator;
+ iterator mPosition;
+
+ /**
+ * Functions for iterating over the TokenList
+ */
+
+ Token* nextToken();
+ Token* peek()
+ {
+ NS_ASSERTION(mCurrentItem, "peek called uninitialized lexer");
+ return mCurrentItem;
+ }
+ Token* peekAhead()
+ {
+ NS_ASSERTION(mCurrentItem, "peekAhead called on uninitialized lexer");
+ // Don't peek past the end node
+ return (mCurrentItem && mCurrentItem->mNext) ? mCurrentItem->mNext : mCurrentItem;
+ }
+ bool hasMoreTokens()
+ {
+ NS_ASSERTION(mCurrentItem, "HasMoreTokens called on uninitialized lexer");
+ return (mCurrentItem && mCurrentItem->mType != Token::END);
+ }
+
+ /**
+ * Trivial Tokens
+ */
+ //-- LF, changed to enum
+ enum _TrivialTokens {
+ D_QUOTE = '\"',
+ S_QUOTE = '\'',
+ L_PAREN = '(',
+ R_PAREN = ')',
+ L_BRACKET = '[',
+ R_BRACKET = ']',
+ L_ANGLE = '<',
+ R_ANGLE = '>',
+ COMMA = ',',
+ PERIOD = '.',
+ ASTERISK = '*',
+ FORWARD_SLASH = '/',
+ EQUAL = '=',
+ BANG = '!',
+ VERT_BAR = '|',
+ AT_SIGN = '@',
+ DOLLAR_SIGN = '$',
+ PLUS = '+',
+ HYPHEN = '-',
+ COLON = ':',
+ //-- whitespace tokens
+ SPACE = ' ',
+ TX_TAB = '\t',
+ TX_CR = '\n',
+ TX_LF = '\r'
+ };
+
+private:
+
+ Token* mCurrentItem;
+ Token* mFirstItem;
+ Token* mLastItem;
+
+ int mTokenCount;
+
+ void addToken(Token* aToken);
+
+ /**
+ * Returns true if the following Token should be an operator.
+ * This is a helper for the first bullet of [XPath 3.7]
+ * Lexical Structure
+ */
+ bool nextIsOperatorToken(Token* aToken);
+
+ /**
+ * Returns true if the given character represents a numeric letter (digit)
+ * Implemented in ExprLexerChars.cpp
+ */
+ static bool isXPathDigit(char16_t ch)
+ {
+ return (ch >= '0' && ch <= '9');
+ }
+};
+
+#endif
+
diff --git a/dom/xslt/xpath/txExprParser.cpp b/dom/xslt/xpath/txExprParser.cpp
new file mode 100644
index 000000000..f8474004b
--- /dev/null
+++ b/dom/xslt/xpath/txExprParser.cpp
@@ -0,0 +1,923 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/**
+ * ExprParser
+ * This class is used to parse XSL Expressions
+ * @see ExprLexer
+**/
+
+#include "mozilla/Move.h"
+#include "txExprParser.h"
+#include "txExprLexer.h"
+#include "txExpr.h"
+#include "txStack.h"
+#include "nsGkAtoms.h"
+#include "nsError.h"
+#include "txIXPathContext.h"
+#include "txStringUtils.h"
+#include "txXPathNode.h"
+#include "txXPathOptimizer.h"
+
+using mozilla::Move;
+
+/**
+ * Creates an Attribute Value Template using the given value
+ * This should move to XSLProcessor class
+ */
+nsresult
+txExprParser::createAVT(const nsSubstring& aAttrValue,
+ txIParseContext* aContext,
+ Expr** aResult)
+{
+ *aResult = nullptr;
+ nsresult rv = NS_OK;
+ nsAutoPtr<Expr> expr;
+ FunctionCall* concat = nullptr;
+
+ nsAutoString literalString;
+ bool inExpr = false;
+ nsSubstring::const_char_iterator iter, start, end, avtStart;
+ aAttrValue.BeginReading(iter);
+ aAttrValue.EndReading(end);
+ avtStart = iter;
+
+ while (iter != end) {
+ // Every iteration through this loop parses either a literal section
+ // or an expression
+ start = iter;
+ nsAutoPtr<Expr> newExpr;
+ if (!inExpr) {
+ // Parse literal section
+ literalString.Truncate();
+ while (iter != end) {
+ char16_t q = *iter;
+ if (q == '{' || q == '}') {
+ // Store what we've found so far and set a new |start| to
+ // skip the (first) brace
+ literalString.Append(Substring(start, iter));
+ start = ++iter;
+ // Unless another brace follows we've found the start of
+ // an expression (in case of '{') or an unbalanced brace
+ // (in case of '}')
+ if (iter == end || *iter != q) {
+ if (q == '}') {
+ aContext->SetErrorOffset(iter - avtStart);
+ return NS_ERROR_XPATH_UNBALANCED_CURLY_BRACE;
+ }
+
+ inExpr = true;
+ break;
+ }
+ // We found a second brace, let that be part of the next
+ // literal section being parsed and continue looping
+ }
+ ++iter;
+ }
+
+ if (start == iter && literalString.IsEmpty()) {
+ // Restart the loop since we didn't create an expression
+ continue;
+ }
+ newExpr = new txLiteralExpr(literalString +
+ Substring(start, iter));
+ }
+ else {
+ // Parse expressions, iter is already past the initial '{' when
+ // we get here.
+ while (iter != end) {
+ if (*iter == '}') {
+ rv = createExprInternal(Substring(start, iter),
+ start - avtStart, aContext,
+ getter_Transfers(newExpr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ inExpr = false;
+ ++iter; // skip closing '}'
+ break;
+ }
+ else if (*iter == '\'' || *iter == '"') {
+ char16_t q = *iter;
+ while (++iter != end && *iter != q) {} /* do nothing */
+ if (iter == end) {
+ break;
+ }
+ }
+ ++iter;
+ }
+
+ if (inExpr) {
+ aContext->SetErrorOffset(start - avtStart);
+ return NS_ERROR_XPATH_UNBALANCED_CURLY_BRACE;
+ }
+ }
+
+ // Add expression, create a concat() call if necessary
+ if (!expr) {
+ expr = Move(newExpr);
+ }
+ else {
+ if (!concat) {
+ concat = new txCoreFunctionCall(txCoreFunctionCall::CONCAT);
+ rv = concat->addParam(expr.forget());
+ expr = concat;
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ rv = concat->addParam(newExpr.forget());
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ }
+
+ if (inExpr) {
+ aContext->SetErrorOffset(iter - avtStart);
+ return NS_ERROR_XPATH_UNBALANCED_CURLY_BRACE;
+ }
+
+ if (!expr) {
+ expr = new txLiteralExpr(EmptyString());
+ }
+
+ *aResult = expr.forget();
+
+ return NS_OK;
+}
+
+nsresult
+txExprParser::createExprInternal(const nsSubstring& aExpression,
+ uint32_t aSubStringPos,
+ txIParseContext* aContext, Expr** aExpr)
+{
+ NS_ENSURE_ARG_POINTER(aExpr);
+ *aExpr = nullptr;
+ txExprLexer lexer;
+ nsresult rv = lexer.parse(aExpression);
+ if (NS_FAILED(rv)) {
+ nsASingleFragmentString::const_char_iterator start;
+ aExpression.BeginReading(start);
+ aContext->SetErrorOffset(lexer.mPosition - start + aSubStringPos);
+ return rv;
+ }
+ nsAutoPtr<Expr> expr;
+ rv = createExpr(lexer, aContext, getter_Transfers(expr));
+ if (NS_SUCCEEDED(rv) && lexer.peek()->mType != Token::END) {
+ rv = NS_ERROR_XPATH_BINARY_EXPECTED;
+ }
+ if (NS_FAILED(rv)) {
+ nsASingleFragmentString::const_char_iterator start;
+ aExpression.BeginReading(start);
+ aContext->SetErrorOffset(lexer.peek()->mStart - start + aSubStringPos);
+
+ return rv;
+ }
+
+ txXPathOptimizer optimizer;
+ Expr* newExpr = nullptr;
+ rv = optimizer.optimize(expr, &newExpr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ *aExpr = newExpr ? newExpr : expr.forget();
+
+ return NS_OK;
+}
+
+/**
+ * Private Methods
+ */
+
+/**
+ * Creates a binary Expr for the given operator
+ */
+nsresult
+txExprParser::createBinaryExpr(nsAutoPtr<Expr>& left, nsAutoPtr<Expr>& right,
+ Token* op, Expr** aResult)
+{
+ NS_ASSERTION(op, "internal error");
+ *aResult = nullptr;
+
+ Expr* expr = nullptr;
+ switch (op->mType) {
+ //-- math ops
+ case Token::ADDITION_OP :
+ expr = new txNumberExpr(left, right, txNumberExpr::ADD);
+ break;
+ case Token::SUBTRACTION_OP:
+ expr = new txNumberExpr(left, right, txNumberExpr::SUBTRACT);
+ break;
+ case Token::DIVIDE_OP :
+ expr = new txNumberExpr(left, right, txNumberExpr::DIVIDE);
+ break;
+ case Token::MODULUS_OP :
+ expr = new txNumberExpr(left, right, txNumberExpr::MODULUS);
+ break;
+ case Token::MULTIPLY_OP :
+ expr = new txNumberExpr(left, right, txNumberExpr::MULTIPLY);
+ break;
+
+ //-- case boolean ops
+ case Token::AND_OP:
+ expr = new BooleanExpr(left, right, BooleanExpr::AND);
+ break;
+ case Token::OR_OP:
+ expr = new BooleanExpr(left, right, BooleanExpr::OR);
+ break;
+
+ //-- equality ops
+ case Token::EQUAL_OP :
+ expr = new RelationalExpr(left, right, RelationalExpr::EQUAL);
+ break;
+ case Token::NOT_EQUAL_OP :
+ expr = new RelationalExpr(left, right, RelationalExpr::NOT_EQUAL);
+ break;
+
+ //-- relational ops
+ case Token::LESS_THAN_OP:
+ expr = new RelationalExpr(left, right, RelationalExpr::LESS_THAN);
+ break;
+ case Token::GREATER_THAN_OP:
+ expr = new RelationalExpr(left, right,
+ RelationalExpr::GREATER_THAN);
+ break;
+ case Token::LESS_OR_EQUAL_OP:
+ expr = new RelationalExpr(left, right,
+ RelationalExpr::LESS_OR_EQUAL);
+ break;
+ case Token::GREATER_OR_EQUAL_OP:
+ expr = new RelationalExpr(left, right,
+ RelationalExpr::GREATER_OR_EQUAL);
+ break;
+
+ default:
+ NS_NOTREACHED("operator tokens should be already checked");
+ return NS_ERROR_UNEXPECTED;
+ }
+ NS_ENSURE_TRUE(expr, NS_ERROR_OUT_OF_MEMORY);
+
+ left.forget();
+ right.forget();
+
+ *aResult = expr;
+ return NS_OK;
+}
+
+
+nsresult
+txExprParser::createExpr(txExprLexer& lexer, txIParseContext* aContext,
+ Expr** aResult)
+{
+ *aResult = nullptr;
+
+ nsresult rv = NS_OK;
+ bool done = false;
+
+ nsAutoPtr<Expr> expr;
+
+ txStack exprs;
+ txStack ops;
+
+ while (!done) {
+
+ uint16_t negations = 0;
+ while (lexer.peek()->mType == Token::SUBTRACTION_OP) {
+ negations++;
+ lexer.nextToken();
+ }
+
+ rv = createUnionExpr(lexer, aContext, getter_Transfers(expr));
+ if (NS_FAILED(rv)) {
+ break;
+ }
+
+ if (negations > 0) {
+ if (negations % 2 == 0) {
+ FunctionCall* fcExpr = new txCoreFunctionCall(txCoreFunctionCall::NUMBER);
+
+ rv = fcExpr->addParam(expr);
+ if (NS_FAILED(rv))
+ return rv;
+ expr.forget();
+ expr = fcExpr;
+ }
+ else {
+ expr = new UnaryExpr(expr.forget());
+ }
+ }
+
+ short tokPrecedence = precedence(lexer.peek());
+ if (tokPrecedence != 0) {
+ Token* tok = lexer.nextToken();
+ while (!exprs.isEmpty() && tokPrecedence
+ <= precedence(static_cast<Token*>(ops.peek()))) {
+ // can't use expr as argument due to order of evaluation
+ nsAutoPtr<Expr> left(static_cast<Expr*>(exprs.pop()));
+ nsAutoPtr<Expr> right(Move(expr));
+ rv = createBinaryExpr(left, right,
+ static_cast<Token*>(ops.pop()),
+ getter_Transfers(expr));
+ if (NS_FAILED(rv)) {
+ done = true;
+ break;
+ }
+ }
+ exprs.push(expr.forget());
+ ops.push(tok);
+ }
+ else {
+ done = true;
+ }
+ }
+
+ while (NS_SUCCEEDED(rv) && !exprs.isEmpty()) {
+ nsAutoPtr<Expr> left(static_cast<Expr*>(exprs.pop()));
+ nsAutoPtr<Expr> right(Move(expr));
+ rv = createBinaryExpr(left, right, static_cast<Token*>(ops.pop()),
+ getter_Transfers(expr));
+ }
+ // clean up on error
+ while (!exprs.isEmpty()) {
+ delete static_cast<Expr*>(exprs.pop());
+ }
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ *aResult = expr.forget();
+ return NS_OK;
+}
+
+nsresult
+txExprParser::createFilterOrStep(txExprLexer& lexer, txIParseContext* aContext,
+ Expr** aResult)
+{
+ *aResult = nullptr;
+
+ nsresult rv = NS_OK;
+ Token* tok = lexer.peek();
+
+ nsAutoPtr<Expr> expr;
+ switch (tok->mType) {
+ case Token::FUNCTION_NAME_AND_PAREN:
+ rv = createFunctionCall(lexer, aContext, getter_Transfers(expr));
+ NS_ENSURE_SUCCESS(rv, rv);
+ break;
+ case Token::VAR_REFERENCE :
+ lexer.nextToken();
+ {
+ nsCOMPtr<nsIAtom> prefix, lName;
+ int32_t nspace;
+ nsresult rv = resolveQName(tok->Value(), getter_AddRefs(prefix),
+ aContext, getter_AddRefs(lName),
+ nspace);
+ NS_ENSURE_SUCCESS(rv, rv);
+ expr = new VariableRefExpr(prefix, lName, nspace);
+ }
+ break;
+ case Token::L_PAREN:
+ lexer.nextToken();
+ rv = createExpr(lexer, aContext, getter_Transfers(expr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (lexer.peek()->mType != Token::R_PAREN) {
+ return NS_ERROR_XPATH_PAREN_EXPECTED;
+ }
+ lexer.nextToken();
+ break;
+ case Token::LITERAL :
+ lexer.nextToken();
+ expr = new txLiteralExpr(tok->Value());
+ break;
+ case Token::NUMBER:
+ {
+ lexer.nextToken();
+ expr = new txLiteralExpr(txDouble::toDouble(tok->Value()));
+ break;
+ }
+ default:
+ return createLocationStep(lexer, aContext, aResult);
+ }
+
+ if (lexer.peek()->mType == Token::L_BRACKET) {
+ nsAutoPtr<FilterExpr> filterExpr(new FilterExpr(expr));
+
+ expr.forget();
+
+ //-- handle predicates
+ rv = parsePredicates(filterExpr, lexer, aContext);
+ NS_ENSURE_SUCCESS(rv, rv);
+ expr = filterExpr.forget();
+ }
+
+ *aResult = expr.forget();
+ return NS_OK;
+}
+
+nsresult
+txExprParser::createFunctionCall(txExprLexer& lexer, txIParseContext* aContext,
+ Expr** aResult)
+{
+ *aResult = nullptr;
+
+ nsAutoPtr<FunctionCall> fnCall;
+
+ Token* tok = lexer.nextToken();
+ NS_ASSERTION(tok->mType == Token::FUNCTION_NAME_AND_PAREN,
+ "FunctionCall expected");
+
+ //-- compare function names
+ nsCOMPtr<nsIAtom> prefix, lName;
+ int32_t namespaceID;
+ nsresult rv = resolveQName(tok->Value(), getter_AddRefs(prefix), aContext,
+ getter_AddRefs(lName), namespaceID);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ txCoreFunctionCall::eType type;
+ if (namespaceID == kNameSpaceID_None &&
+ txCoreFunctionCall::getTypeFromAtom(lName, type)) {
+ // It is a known built-in function.
+ fnCall = new txCoreFunctionCall(type);
+ }
+
+ // check extension functions and xslt
+ if (!fnCall) {
+ rv = aContext->resolveFunctionCall(lName, namespaceID,
+ getter_Transfers(fnCall));
+
+ if (rv == NS_ERROR_NOT_IMPLEMENTED) {
+ // this should just happen for unparsed-entity-uri()
+ NS_ASSERTION(!fnCall, "Now is it implemented or not?");
+ rv = parseParameters(0, lexer, aContext);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ *aResult = new txLiteralExpr(tok->Value() +
+ NS_LITERAL_STRING(" not implemented."));
+
+ return NS_OK;
+ }
+
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ //-- handle parametes
+ rv = parseParameters(fnCall, lexer, aContext);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ *aResult = fnCall.forget();
+ return NS_OK;
+}
+
+nsresult
+txExprParser::createLocationStep(txExprLexer& lexer, txIParseContext* aContext,
+ Expr** aExpr)
+{
+ *aExpr = nullptr;
+
+ //-- child axis is default
+ LocationStep::LocationStepType axisIdentifier = LocationStep::CHILD_AXIS;
+ nsAutoPtr<txNodeTest> nodeTest;
+
+ //-- get Axis Identifier or AbbreviatedStep, if present
+ Token* tok = lexer.peek();
+ switch (tok->mType) {
+ case Token::AXIS_IDENTIFIER:
+ {
+ //-- eat token
+ lexer.nextToken();
+ nsCOMPtr<nsIAtom> axis = NS_Atomize(tok->Value());
+ if (axis == nsGkAtoms::ancestor) {
+ axisIdentifier = LocationStep::ANCESTOR_AXIS;
+ }
+ else if (axis == nsGkAtoms::ancestorOrSelf) {
+ axisIdentifier = LocationStep::ANCESTOR_OR_SELF_AXIS;
+ }
+ else if (axis == nsGkAtoms::attribute) {
+ axisIdentifier = LocationStep::ATTRIBUTE_AXIS;
+ }
+ else if (axis == nsGkAtoms::child) {
+ axisIdentifier = LocationStep::CHILD_AXIS;
+ }
+ else if (axis == nsGkAtoms::descendant) {
+ axisIdentifier = LocationStep::DESCENDANT_AXIS;
+ }
+ else if (axis == nsGkAtoms::descendantOrSelf) {
+ axisIdentifier = LocationStep::DESCENDANT_OR_SELF_AXIS;
+ }
+ else if (axis == nsGkAtoms::following) {
+ axisIdentifier = LocationStep::FOLLOWING_AXIS;
+ }
+ else if (axis == nsGkAtoms::followingSibling) {
+ axisIdentifier = LocationStep::FOLLOWING_SIBLING_AXIS;
+ }
+ else if (axis == nsGkAtoms::_namespace) {
+ axisIdentifier = LocationStep::NAMESPACE_AXIS;
+ }
+ else if (axis == nsGkAtoms::parent) {
+ axisIdentifier = LocationStep::PARENT_AXIS;
+ }
+ else if (axis == nsGkAtoms::preceding) {
+ axisIdentifier = LocationStep::PRECEDING_AXIS;
+ }
+ else if (axis == nsGkAtoms::precedingSibling) {
+ axisIdentifier = LocationStep::PRECEDING_SIBLING_AXIS;
+ }
+ else if (axis == nsGkAtoms::self) {
+ axisIdentifier = LocationStep::SELF_AXIS;
+ }
+ else {
+ return NS_ERROR_XPATH_INVALID_AXIS;
+ }
+ break;
+ }
+ case Token::AT_SIGN:
+ //-- eat token
+ lexer.nextToken();
+ axisIdentifier = LocationStep::ATTRIBUTE_AXIS;
+ break;
+ case Token::PARENT_NODE :
+ //-- eat token
+ lexer.nextToken();
+ axisIdentifier = LocationStep::PARENT_AXIS;
+ nodeTest = new txNodeTypeTest(txNodeTypeTest::NODE_TYPE);
+ break;
+ case Token::SELF_NODE :
+ //-- eat token
+ lexer.nextToken();
+ axisIdentifier = LocationStep::SELF_AXIS;
+ nodeTest = new txNodeTypeTest(txNodeTypeTest::NODE_TYPE);
+ break;
+ default:
+ break;
+ }
+
+ //-- get NodeTest unless an AbbreviatedStep was found
+ nsresult rv = NS_OK;
+ if (!nodeTest) {
+ tok = lexer.peek();
+
+ if (tok->mType == Token::CNAME) {
+ lexer.nextToken();
+ // resolve QName
+ nsCOMPtr<nsIAtom> prefix, lName;
+ int32_t nspace;
+ rv = resolveQName(tok->Value(), getter_AddRefs(prefix),
+ aContext, getter_AddRefs(lName),
+ nspace, true);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nodeTest =
+ new txNameTest(prefix, lName, nspace,
+ axisIdentifier == LocationStep::ATTRIBUTE_AXIS ?
+ static_cast<uint16_t>(txXPathNodeType::ATTRIBUTE_NODE) :
+ static_cast<uint16_t>(txXPathNodeType::ELEMENT_NODE));
+ }
+ else {
+ rv = createNodeTypeTest(lexer, getter_Transfers(nodeTest));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ }
+
+ nsAutoPtr<LocationStep> lstep(new LocationStep(nodeTest, axisIdentifier));
+
+ nodeTest.forget();
+
+ //-- handle predicates
+ rv = parsePredicates(lstep, lexer, aContext);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ *aExpr = lstep.forget();
+ return NS_OK;
+}
+
+/**
+ * This method only handles comment(), text(), processing-instructing()
+ * and node()
+ */
+nsresult
+txExprParser::createNodeTypeTest(txExprLexer& lexer, txNodeTest** aTest)
+{
+ *aTest = 0;
+ nsAutoPtr<txNodeTypeTest> nodeTest;
+
+ Token* nodeTok = lexer.peek();
+
+ switch (nodeTok->mType) {
+ case Token::COMMENT_AND_PAREN:
+ lexer.nextToken();
+ nodeTest = new txNodeTypeTest(txNodeTypeTest::COMMENT_TYPE);
+ break;
+ case Token::NODE_AND_PAREN:
+ lexer.nextToken();
+ nodeTest = new txNodeTypeTest(txNodeTypeTest::NODE_TYPE);
+ break;
+ case Token::PROC_INST_AND_PAREN:
+ lexer.nextToken();
+ nodeTest = new txNodeTypeTest(txNodeTypeTest::PI_TYPE);
+ break;
+ case Token::TEXT_AND_PAREN:
+ lexer.nextToken();
+ nodeTest = new txNodeTypeTest(txNodeTypeTest::TEXT_TYPE);
+ break;
+ default:
+ return NS_ERROR_XPATH_NO_NODE_TYPE_TEST;
+ }
+
+ NS_ENSURE_TRUE(nodeTest, NS_ERROR_OUT_OF_MEMORY);
+
+ if (nodeTok->mType == Token::PROC_INST_AND_PAREN &&
+ lexer.peek()->mType == Token::LITERAL) {
+ Token* tok = lexer.nextToken();
+ nodeTest->setNodeName(tok->Value());
+ }
+ if (lexer.peek()->mType != Token::R_PAREN) {
+ return NS_ERROR_XPATH_PAREN_EXPECTED;
+ }
+ lexer.nextToken();
+
+ *aTest = nodeTest.forget();
+ return NS_OK;
+}
+
+/**
+ * Creates a PathExpr using the given txExprLexer
+ * @param lexer the txExprLexer for retrieving Tokens
+ */
+nsresult
+txExprParser::createPathExpr(txExprLexer& lexer, txIParseContext* aContext,
+ Expr** aResult)
+{
+ *aResult = nullptr;
+
+ nsAutoPtr<Expr> expr;
+
+ Token* tok = lexer.peek();
+
+ // is this a root expression?
+ if (tok->mType == Token::PARENT_OP) {
+ if (!isLocationStepToken(lexer.peekAhead())) {
+ lexer.nextToken();
+ *aResult = new RootExpr();
+ return NS_OK;
+ }
+ }
+
+ // parse first step (possibly a FilterExpr)
+ nsresult rv = NS_OK;
+ if (tok->mType != Token::PARENT_OP &&
+ tok->mType != Token::ANCESTOR_OP) {
+ rv = createFilterOrStep(lexer, aContext, getter_Transfers(expr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // is this a singlestep path expression?
+ tok = lexer.peek();
+ if (tok->mType != Token::PARENT_OP &&
+ tok->mType != Token::ANCESTOR_OP) {
+ *aResult = expr.forget();
+ return NS_OK;
+ }
+ }
+ else {
+ expr = new RootExpr();
+
+#ifdef TX_TO_STRING
+ static_cast<RootExpr*>(expr.get())->setSerialize(false);
+#endif
+ }
+
+ // We have a PathExpr containing several steps
+ nsAutoPtr<PathExpr> pathExpr(new PathExpr());
+
+ rv = pathExpr->addExpr(expr, PathExpr::RELATIVE_OP);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ expr.forget();
+
+ // this is ugly
+ while (1) {
+ PathExpr::PathOperator pathOp;
+ switch (lexer.peek()->mType) {
+ case Token::ANCESTOR_OP :
+ pathOp = PathExpr::DESCENDANT_OP;
+ break;
+ case Token::PARENT_OP :
+ pathOp = PathExpr::RELATIVE_OP;
+ break;
+ default:
+ *aResult = pathExpr.forget();
+ return NS_OK;
+ }
+ lexer.nextToken();
+
+ rv = createLocationStep(lexer, aContext, getter_Transfers(expr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = pathExpr->addExpr(expr, pathOp);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ expr.forget();
+ }
+ NS_NOTREACHED("internal xpath parser error");
+ return NS_ERROR_UNEXPECTED;
+}
+
+/**
+ * Creates a PathExpr using the given txExprLexer
+ * @param lexer the txExprLexer for retrieving Tokens
+ */
+nsresult
+txExprParser::createUnionExpr(txExprLexer& lexer, txIParseContext* aContext,
+ Expr** aResult)
+{
+ *aResult = nullptr;
+
+ nsAutoPtr<Expr> expr;
+ nsresult rv = createPathExpr(lexer, aContext, getter_Transfers(expr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (lexer.peek()->mType != Token::UNION_OP) {
+ *aResult = expr.forget();
+ return NS_OK;
+ }
+
+ nsAutoPtr<UnionExpr> unionExpr(new UnionExpr());
+
+ rv = unionExpr->addExpr(expr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ expr.forget();
+
+ while (lexer.peek()->mType == Token::UNION_OP) {
+ lexer.nextToken(); //-- eat token
+
+ rv = createPathExpr(lexer, aContext, getter_Transfers(expr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = unionExpr->addExpr(expr.forget());
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ *aResult = unionExpr.forget();
+ return NS_OK;
+}
+
+bool
+txExprParser::isLocationStepToken(Token* aToken)
+{
+ // We could put these in consecutive order in ExprLexer.h for speed
+ return aToken->mType == Token::AXIS_IDENTIFIER ||
+ aToken->mType == Token::AT_SIGN ||
+ aToken->mType == Token::PARENT_NODE ||
+ aToken->mType == Token::SELF_NODE ||
+ aToken->mType == Token::CNAME ||
+ aToken->mType == Token::COMMENT_AND_PAREN ||
+ aToken->mType == Token::NODE_AND_PAREN ||
+ aToken->mType == Token::PROC_INST_AND_PAREN ||
+ aToken->mType == Token::TEXT_AND_PAREN;
+}
+
+/**
+ * Using the given lexer, parses the tokens if they represent a predicate list
+ * If an error occurs a non-zero String pointer will be returned containing the
+ * error message.
+ * @param predicateList, the PredicateList to add predicate expressions to
+ * @param lexer the txExprLexer to use for parsing tokens
+ * @return 0 if successful, or a String pointer to the error message
+ */
+nsresult
+txExprParser::parsePredicates(PredicateList* aPredicateList,
+ txExprLexer& lexer, txIParseContext* aContext)
+{
+ nsAutoPtr<Expr> expr;
+ nsresult rv = NS_OK;
+ while (lexer.peek()->mType == Token::L_BRACKET) {
+ //-- eat Token
+ lexer.nextToken();
+
+ rv = createExpr(lexer, aContext, getter_Transfers(expr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = aPredicateList->add(expr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ expr.forget();
+
+ if (lexer.peek()->mType != Token::R_BRACKET) {
+ return NS_ERROR_XPATH_BRACKET_EXPECTED;
+ }
+ lexer.nextToken();
+ }
+ return NS_OK;
+}
+
+
+/**
+ * Using the given lexer, parses the tokens if they represent a parameter list
+ * If an error occurs a non-zero String pointer will be returned containing the
+ * error message.
+ * @param list, the List to add parameter expressions to
+ * @param lexer the txExprLexer to use for parsing tokens
+ * @return NS_OK if successful, or another rv otherwise
+ */
+nsresult
+txExprParser::parseParameters(FunctionCall* aFnCall, txExprLexer& lexer,
+ txIParseContext* aContext)
+{
+ if (lexer.peek()->mType == Token::R_PAREN) {
+ lexer.nextToken();
+ return NS_OK;
+ }
+
+ nsAutoPtr<Expr> expr;
+ nsresult rv = NS_OK;
+ while (1) {
+ rv = createExpr(lexer, aContext, getter_Transfers(expr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (aFnCall) {
+ rv = aFnCall->addParam(expr.forget());
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ switch (lexer.peek()->mType) {
+ case Token::R_PAREN :
+ lexer.nextToken();
+ return NS_OK;
+ case Token::COMMA: //-- param separator
+ lexer.nextToken();
+ break;
+ default:
+ return NS_ERROR_XPATH_PAREN_EXPECTED;
+ }
+ }
+
+ NS_NOTREACHED("internal xpath parser error");
+ return NS_ERROR_UNEXPECTED;
+}
+
+short
+txExprParser::precedence(Token* aToken)
+{
+ switch (aToken->mType) {
+ case Token::OR_OP:
+ return 1;
+ case Token::AND_OP:
+ return 2;
+ //-- equality
+ case Token::EQUAL_OP:
+ case Token::NOT_EQUAL_OP:
+ return 3;
+ //-- relational
+ case Token::LESS_THAN_OP:
+ case Token::GREATER_THAN_OP:
+ case Token::LESS_OR_EQUAL_OP:
+ case Token::GREATER_OR_EQUAL_OP:
+ return 4;
+ //-- additive operators
+ case Token::ADDITION_OP:
+ case Token::SUBTRACTION_OP:
+ return 5;
+ //-- multiplicative
+ case Token::DIVIDE_OP:
+ case Token::MULTIPLY_OP:
+ case Token::MODULUS_OP:
+ return 6;
+ default:
+ break;
+ }
+ return 0;
+}
+
+nsresult
+txExprParser::resolveQName(const nsAString& aQName,
+ nsIAtom** aPrefix, txIParseContext* aContext,
+ nsIAtom** aLocalName, int32_t& aNamespace,
+ bool aIsNameTest)
+{
+ aNamespace = kNameSpaceID_None;
+ int32_t idx = aQName.FindChar(':');
+ if (idx > 0) {
+ *aPrefix = NS_Atomize(StringHead(aQName, (uint32_t)idx)).take();
+ if (!*aPrefix) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ *aLocalName = NS_Atomize(Substring(aQName, (uint32_t)idx + 1,
+ aQName.Length() - (idx + 1))).take();
+ if (!*aLocalName) {
+ NS_RELEASE(*aPrefix);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ return aContext->resolveNamespacePrefix(*aPrefix, aNamespace);
+ }
+ // the lexer dealt with idx == 0
+ *aPrefix = 0;
+ if (aIsNameTest && aContext->caseInsensitiveNameTests()) {
+ nsAutoString lcname;
+ nsContentUtils::ASCIIToLower(aQName, lcname);
+ *aLocalName = NS_Atomize(lcname).take();
+ }
+ else {
+ *aLocalName = NS_Atomize(aQName).take();
+ }
+ if (!*aLocalName) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ return NS_OK;
+}
diff --git a/dom/xslt/xpath/txExprParser.h b/dom/xslt/xpath/txExprParser.h
new file mode 100644
index 000000000..238e03399
--- /dev/null
+++ b/dom/xslt/xpath/txExprParser.h
@@ -0,0 +1,109 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/**
+ * ExprParser
+ * This class is used to parse XSL Expressions
+ * @see ExprLexer
+**/
+
+#ifndef MITREXSL_EXPRPARSER_H
+#define MITREXSL_EXPRPARSER_H
+
+#include "txCore.h"
+#include "nsAutoPtr.h"
+#include "nsString.h"
+
+class Expr;
+class txExprLexer;
+class FunctionCall;
+class LocationStep;
+class nsIAtom;
+class PredicateList;
+class Token;
+class txIParseContext;
+class txNodeTest;
+
+class txExprParser
+{
+public:
+
+ static nsresult createExpr(const nsSubstring& aExpression,
+ txIParseContext* aContext, Expr** aExpr)
+ {
+ return createExprInternal(aExpression, 0, aContext, aExpr);
+ }
+
+ /**
+ * Creates an Attribute Value Template using the given value
+ */
+ static nsresult createAVT(const nsSubstring& aAttrValue,
+ txIParseContext* aContext,
+ Expr** aResult);
+
+
+protected:
+ static nsresult createExprInternal(const nsSubstring& aExpression,
+ uint32_t aSubStringPos,
+ txIParseContext* aContext,
+ Expr** aExpr);
+ /**
+ * Using nsAutoPtr& to optimize passing the ownership to the
+ * created binary expression objects.
+ */
+ static nsresult createBinaryExpr(nsAutoPtr<Expr>& left,
+ nsAutoPtr<Expr>& right, Token* op,
+ Expr** aResult);
+ static nsresult createExpr(txExprLexer& lexer, txIParseContext* aContext,
+ Expr** aResult);
+ static nsresult createFilterOrStep(txExprLexer& lexer,
+ txIParseContext* aContext,
+ Expr** aResult);
+ static nsresult createFunctionCall(txExprLexer& lexer,
+ txIParseContext* aContext,
+ Expr** aResult);
+ static nsresult createLocationStep(txExprLexer& lexer,
+ txIParseContext* aContext,
+ Expr** aResult);
+ static nsresult createNodeTypeTest(txExprLexer& lexer,
+ txNodeTest** aResult);
+ static nsresult createPathExpr(txExprLexer& lexer,
+ txIParseContext* aContext,
+ Expr** aResult);
+ static nsresult createUnionExpr(txExprLexer& lexer,
+ txIParseContext* aContext,
+ Expr** aResult);
+
+ static bool isLocationStepToken(Token* aToken);
+
+ static short precedence(Token* aToken);
+
+ /**
+ * Resolve a QName, given the mContext parse context.
+ * Returns prefix and localName as well as namespace ID
+ */
+ static nsresult resolveQName(const nsAString& aQName, nsIAtom** aPrefix,
+ txIParseContext* aContext,
+ nsIAtom** aLocalName, int32_t& aNamespace,
+ bool aIsNameTest = false);
+
+ /**
+ * Using the given lexer, parses the tokens if they represent a
+ * predicate list
+ * If an error occurs a non-zero String pointer will be returned
+ * containing the error message.
+ * @param predicateList, the PredicateList to add predicate expressions to
+ * @param lexer the ExprLexer to use for parsing tokens
+ * @return 0 if successful, or a String pointer to the error message
+ */
+ static nsresult parsePredicates(PredicateList* aPredicateList,
+ txExprLexer& lexer,
+ txIParseContext* aContext);
+ static nsresult parseParameters(FunctionCall* aFnCall, txExprLexer& lexer,
+ txIParseContext* aContext);
+
+};
+
+#endif
diff --git a/dom/xslt/xpath/txExprResult.h b/dom/xslt/xpath/txExprResult.h
new file mode 100644
index 000000000..73e2f0772
--- /dev/null
+++ b/dom/xslt/xpath/txExprResult.h
@@ -0,0 +1,131 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+#ifndef TRANSFRMX_EXPRRESULT_H
+#define TRANSFRMX_EXPRRESULT_H
+
+#include "nsString.h"
+#include "nsAutoPtr.h"
+#include "txCore.h"
+#include "txResultRecycler.h"
+
+/*
+ * ExprResult
+ *
+ * Classes Represented:
+ * BooleanResult, ExprResult, NumberResult, StringResult
+ *
+ * Note: for NodeSet, see NodeSet.h
+*/
+
+class txAExprResult
+{
+public:
+ friend class txResultRecycler;
+
+ // Update txLiteralExpr::getReturnType and sTypes in txEXSLTFunctions.cpp if
+ // this enum is changed.
+ enum ResultType {
+ NODESET = 0,
+ BOOLEAN,
+ NUMBER,
+ STRING,
+ RESULT_TREE_FRAGMENT
+ };
+
+ explicit txAExprResult(txResultRecycler* aRecycler) : mRecycler(aRecycler)
+ {
+ }
+ virtual ~txAExprResult()
+ {
+ }
+
+ void AddRef()
+ {
+ ++mRefCnt;
+ NS_LOG_ADDREF(this, mRefCnt, "txAExprResult", sizeof(*this));
+ }
+
+ void Release(); // Implemented in txResultRecycler.cpp
+
+ /**
+ * Returns the type of ExprResult represented
+ * @return the type of ExprResult represented
+ **/
+ virtual short getResultType() = 0;
+
+ /**
+ * Creates a String representation of this ExprResult
+ * @param aResult the destination string to append the String
+ * representation to.
+ **/
+ virtual void stringValue(nsString& aResult) = 0;
+
+ /**
+ * Returns a pointer to the stringvalue if possible. Otherwise null is
+ * returned.
+ */
+ virtual const nsString* stringValuePointer() = 0;
+
+ /**
+ * Converts this ExprResult to a Boolean (bool) value
+ * @return the Boolean value
+ **/
+ virtual bool booleanValue() = 0;
+
+ /**
+ * Converts this ExprResult to a Number (double) value
+ * @return the Number value
+ **/
+ virtual double numberValue() = 0;
+
+private:
+ nsAutoRefCnt mRefCnt;
+ RefPtr<txResultRecycler> mRecycler;
+};
+
+#define TX_DECL_EXPRRESULT \
+ virtual short getResultType(); \
+ virtual void stringValue(nsString& aString); \
+ virtual const nsString* stringValuePointer(); \
+ virtual bool booleanValue(); \
+ virtual double numberValue(); \
+
+
+class BooleanResult : public txAExprResult {
+
+public:
+ explicit BooleanResult(bool aValue);
+
+ TX_DECL_EXPRRESULT
+
+private:
+ bool value;
+};
+
+class NumberResult : public txAExprResult {
+
+public:
+ NumberResult(double aValue, txResultRecycler* aRecycler);
+
+ TX_DECL_EXPRRESULT
+
+ double value;
+
+};
+
+
+class StringResult : public txAExprResult {
+public:
+ explicit StringResult(txResultRecycler* aRecycler);
+ StringResult(const nsAString& aValue, txResultRecycler* aRecycler);
+
+ TX_DECL_EXPRRESULT
+
+ nsString mValue;
+};
+
+#endif
+
diff --git a/dom/xslt/xpath/txFilterExpr.cpp b/dom/xslt/xpath/txFilterExpr.cpp
new file mode 100644
index 000000000..155210557
--- /dev/null
+++ b/dom/xslt/xpath/txFilterExpr.cpp
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "txExpr.h"
+#include "txNodeSet.h"
+#include "txIXPathContext.h"
+
+//-- Implementation of FilterExpr --/
+
+ //-----------------------------/
+ //- Virtual methods from Expr -/
+//-----------------------------/
+
+/**
+ * Evaluates this Expr based on the given context node and processor state
+ * @param context the context node for evaluation of this Expr
+ * @param ps the ProcessorState containing the stack information needed
+ * for evaluation
+ * @return the result of the evaluation
+ * @see Expr
+**/
+nsresult
+FilterExpr::evaluate(txIEvalContext* aContext, txAExprResult** aResult)
+{
+ *aResult = nullptr;
+
+ RefPtr<txAExprResult> exprRes;
+ nsresult rv = expr->evaluate(aContext, getter_AddRefs(exprRes));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_ENSURE_TRUE(exprRes->getResultType() == txAExprResult::NODESET,
+ NS_ERROR_XSLT_NODESET_EXPECTED);
+
+ RefPtr<txNodeSet> nodes =
+ static_cast<txNodeSet*>(static_cast<txAExprResult*>(exprRes));
+ // null out exprRes so that we can test for shared-ness
+ exprRes = nullptr;
+
+ RefPtr<txNodeSet> nonShared;
+ rv = aContext->recycler()->getNonSharedNodeSet(nodes,
+ getter_AddRefs(nonShared));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = evaluatePredicates(nonShared, aContext);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ *aResult = nonShared;
+ NS_ADDREF(*aResult);
+
+ return NS_OK;
+} //-- evaluate
+
+TX_IMPL_EXPR_STUBS_BASE(FilterExpr, NODESET_RESULT)
+
+Expr*
+FilterExpr::getSubExprAt(uint32_t aPos)
+{
+ if (aPos == 0) {
+ return expr;
+ }
+ return PredicateList::getSubExprAt(aPos - 1);
+}
+
+void
+FilterExpr::setSubExprAt(uint32_t aPos, Expr* aExpr)
+{
+ if (aPos == 0) {
+ expr.forget();
+ expr = aExpr;
+ }
+ else {
+ PredicateList::setSubExprAt(aPos - 1, aExpr);
+ }
+}
+
+bool
+FilterExpr::isSensitiveTo(ContextSensitivity aContext)
+{
+ return expr->isSensitiveTo(aContext) ||
+ PredicateList::isSensitiveTo(aContext);
+}
+
+#ifdef TX_TO_STRING
+void
+FilterExpr::toString(nsAString& str)
+{
+ if ( expr ) expr->toString(str);
+ else str.AppendLiteral("null");
+ PredicateList::toString(str);
+}
+#endif
+
diff --git a/dom/xslt/xpath/txForwardContext.cpp b/dom/xslt/xpath/txForwardContext.cpp
new file mode 100644
index 000000000..e2367a3ae
--- /dev/null
+++ b/dom/xslt/xpath/txForwardContext.cpp
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "txForwardContext.h"
+#include "txNodeSet.h"
+
+const txXPathNode& txForwardContext::getContextNode()
+{
+ return mContextNode;
+}
+
+uint32_t txForwardContext::size()
+{
+ return (uint32_t)mContextSet->size();
+}
+
+uint32_t txForwardContext::position()
+{
+ int32_t pos = mContextSet->indexOf(mContextNode);
+ NS_ASSERTION(pos >= 0, "Context is not member of context node list.");
+ return (uint32_t)(pos + 1);
+}
+
+nsresult txForwardContext::getVariable(int32_t aNamespace, nsIAtom* aLName,
+ txAExprResult*& aResult)
+{
+ NS_ASSERTION(mInner, "mInner is null!!!");
+ return mInner->getVariable(aNamespace, aLName, aResult);
+}
+
+bool txForwardContext::isStripSpaceAllowed(const txXPathNode& aNode)
+{
+ NS_ASSERTION(mInner, "mInner is null!!!");
+ return mInner->isStripSpaceAllowed(aNode);
+}
+
+void* txForwardContext::getPrivateContext()
+{
+ NS_ASSERTION(mInner, "mInner is null!!!");
+ return mInner->getPrivateContext();
+}
+
+txResultRecycler* txForwardContext::recycler()
+{
+ NS_ASSERTION(mInner, "mInner is null!!!");
+ return mInner->recycler();
+}
+
+void txForwardContext::receiveError(const nsAString& aMsg, nsresult aRes)
+{
+ NS_ASSERTION(mInner, "mInner is null!!!");
+#ifdef DEBUG
+ nsAutoString error(NS_LITERAL_STRING("forwarded error: "));
+ error.Append(aMsg);
+ mInner->receiveError(error, aRes);
+#else
+ mInner->receiveError(aMsg, aRes);
+#endif
+}
diff --git a/dom/xslt/xpath/txForwardContext.h b/dom/xslt/xpath/txForwardContext.h
new file mode 100644
index 000000000..19e06f8d1
--- /dev/null
+++ b/dom/xslt/xpath/txForwardContext.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+#ifndef __TX_XPATH_CONTEXT
+#define __TX_XPATH_CONTEXT
+
+#include "txIXPathContext.h"
+#include "nsAutoPtr.h"
+#include "txNodeSet.h"
+
+class txForwardContext : public txIEvalContext
+{
+public:
+ txForwardContext(txIMatchContext* aContext,
+ const txXPathNode& aContextNode,
+ txNodeSet* aContextNodeSet)
+ : mInner(aContext),
+ mContextNode(aContextNode),
+ mContextSet(aContextNodeSet)
+ {}
+
+ TX_DECL_EVAL_CONTEXT;
+
+private:
+ txIMatchContext* mInner;
+ const txXPathNode& mContextNode;
+ RefPtr<txNodeSet> mContextSet;
+};
+
+#endif // __TX_XPATH_CONTEXT
diff --git a/dom/xslt/xpath/txFunctionCall.cpp b/dom/xslt/xpath/txFunctionCall.cpp
new file mode 100644
index 000000000..aa8f51fc7
--- /dev/null
+++ b/dom/xslt/xpath/txFunctionCall.cpp
@@ -0,0 +1,133 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "txExpr.h"
+#include "nsIAtom.h"
+#include "txIXPathContext.h"
+#include "txNodeSet.h"
+
+/**
+ * This class represents a FunctionCall as defined by the XSL Working Draft
+**/
+
+ //------------------/
+ //- Public Methods -/
+//------------------/
+
+/*
+ * Evaluates the given Expression and converts its result to a number.
+ */
+// static
+nsresult
+FunctionCall::evaluateToNumber(Expr* aExpr, txIEvalContext* aContext,
+ double* aResult)
+{
+ NS_ASSERTION(aExpr, "missing expression");
+ RefPtr<txAExprResult> exprResult;
+ nsresult rv = aExpr->evaluate(aContext, getter_AddRefs(exprResult));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ *aResult = exprResult->numberValue();
+
+ return NS_OK;
+}
+
+/*
+ * Evaluates the given Expression and converts its result to a NodeSet.
+ * If the result is not a NodeSet nullptr is returned.
+ */
+nsresult
+FunctionCall::evaluateToNodeSet(Expr* aExpr, txIEvalContext* aContext,
+ txNodeSet** aResult)
+{
+ NS_ASSERTION(aExpr, "Missing expression to evaluate");
+ *aResult = nullptr;
+
+ RefPtr<txAExprResult> exprRes;
+ nsresult rv = aExpr->evaluate(aContext, getter_AddRefs(exprRes));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (exprRes->getResultType() != txAExprResult::NODESET) {
+ aContext->receiveError(NS_LITERAL_STRING("NodeSet expected as argument"), NS_ERROR_XSLT_NODESET_EXPECTED);
+ return NS_ERROR_XSLT_NODESET_EXPECTED;
+ }
+
+ *aResult =
+ static_cast<txNodeSet*>(static_cast<txAExprResult*>(exprRes));
+ NS_ADDREF(*aResult);
+
+ return NS_OK;
+}
+
+bool FunctionCall::requireParams(int32_t aParamCountMin,
+ int32_t aParamCountMax,
+ txIEvalContext* aContext)
+{
+ int32_t argc = mParams.Length();
+ if (argc < aParamCountMin ||
+ (aParamCountMax > -1 && argc > aParamCountMax)) {
+ nsAutoString err(NS_LITERAL_STRING("invalid number of parameters for function"));
+#ifdef TX_TO_STRING
+ err.AppendLiteral(": ");
+ toString(err);
+#endif
+ aContext->receiveError(err, NS_ERROR_XPATH_INVALID_ARG);
+
+ return false;
+ }
+
+ return true;
+}
+
+Expr*
+FunctionCall::getSubExprAt(uint32_t aPos)
+{
+ return mParams.SafeElementAt(aPos);
+}
+
+void
+FunctionCall::setSubExprAt(uint32_t aPos, Expr* aExpr)
+{
+ NS_ASSERTION(aPos < mParams.Length(),
+ "setting bad subexpression index");
+ mParams[aPos] = aExpr;
+}
+
+bool
+FunctionCall::argsSensitiveTo(ContextSensitivity aContext)
+{
+ uint32_t i, len = mParams.Length();
+ for (i = 0; i < len; ++i) {
+ if (mParams[i]->isSensitiveTo(aContext)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+#ifdef TX_TO_STRING
+void
+FunctionCall::toString(nsAString& aDest)
+{
+ nsCOMPtr<nsIAtom> functionNameAtom;
+ if (NS_FAILED(getNameAtom(getter_AddRefs(functionNameAtom)))) {
+ NS_ERROR("Can't get function name.");
+ return;
+ }
+
+
+
+ aDest.Append(nsDependentAtomString(functionNameAtom) +
+ NS_LITERAL_STRING("("));
+ for (uint32_t i = 0; i < mParams.Length(); ++i) {
+ if (i != 0) {
+ aDest.Append(char16_t(','));
+ }
+ mParams[i]->toString(aDest);
+ }
+ aDest.Append(char16_t(')'));
+}
+#endif
diff --git a/dom/xslt/xpath/txIXPathContext.h b/dom/xslt/xpath/txIXPathContext.h
new file mode 100644
index 000000000..ed5f81a53
--- /dev/null
+++ b/dom/xslt/xpath/txIXPathContext.h
@@ -0,0 +1,140 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+#ifndef __TX_I_XPATH_CONTEXT
+#define __TX_I_XPATH_CONTEXT
+
+#include "txCore.h"
+
+class FunctionCall;
+class nsIAtom;
+class txAExprResult;
+class txResultRecycler;
+class txXPathNode;
+
+/*
+ * txIParseContext
+ *
+ * This interface describes the context needed to create
+ * XPath Expressions and XSLT Patters.
+ * (not completely though. key() requires the ProcessorState, which is
+ * not part of this interface.)
+ */
+
+class txIParseContext
+{
+public:
+ virtual ~txIParseContext()
+ {
+ }
+
+ /*
+ * Return a namespaceID for a given prefix.
+ */
+ virtual nsresult resolveNamespacePrefix(nsIAtom* aPrefix, int32_t& aID) = 0;
+
+ /*
+ * Create a FunctionCall, needed for extension function calls and
+ * XSLT. XPath function calls are resolved by the Parser.
+ */
+ virtual nsresult resolveFunctionCall(nsIAtom* aName, int32_t aID,
+ FunctionCall** aFunction) = 0;
+
+ /**
+ * Should nametests parsed in this context be case-sensitive
+ */
+ virtual bool caseInsensitiveNameTests() = 0;
+
+ /*
+ * Callback to be used by the Parser if errors are detected.
+ */
+ virtual void SetErrorOffset(uint32_t aOffset) = 0;
+
+ enum Allowed {
+ KEY_FUNCTION = 1 << 0
+ };
+ virtual bool allowed(Allowed aAllowed)
+ {
+ return true;
+ }
+};
+
+/*
+ * txIMatchContext
+ *
+ * Interface used for matching XSLT Patters.
+ * This is the part of txIEvalContext (see below), that is independent
+ * of the context node when evaluating a XPath expression, too.
+ * When evaluating a XPath expression, |txIMatchContext|s are used
+ * to transport the information from Step to Step.
+ */
+class txIMatchContext
+{
+public:
+ virtual ~txIMatchContext()
+ {
+ }
+
+ /*
+ * Return the ExprResult associated with the variable with the
+ * given namespace and local name.
+ */
+ virtual nsresult getVariable(int32_t aNamespace, nsIAtom* aLName,
+ txAExprResult*& aResult) = 0;
+
+ /*
+ * Is whitespace stripping allowed for the given node?
+ * See http://www.w3.org/TR/xslt#strip
+ */
+ virtual bool isStripSpaceAllowed(const txXPathNode& aNode) = 0;
+
+ /**
+ * Returns a pointer to the private context
+ */
+ virtual void* getPrivateContext() = 0;
+
+ virtual txResultRecycler* recycler() = 0;
+
+ /*
+ * Callback to be used by the expression/pattern if errors are detected.
+ */
+ virtual void receiveError(const nsAString& aMsg, nsresult aRes) = 0;
+};
+
+#define TX_DECL_MATCH_CONTEXT \
+ nsresult getVariable(int32_t aNamespace, nsIAtom* aLName, \
+ txAExprResult*& aResult); \
+ bool isStripSpaceAllowed(const txXPathNode& aNode); \
+ void* getPrivateContext(); \
+ txResultRecycler* recycler(); \
+ void receiveError(const nsAString& aMsg, nsresult aRes)
+
+class txIEvalContext : public txIMatchContext
+{
+public:
+ /*
+ * Get the context node.
+ */
+ virtual const txXPathNode& getContextNode() = 0;
+
+ /*
+ * Get the size of the context node set.
+ */
+ virtual uint32_t size() = 0;
+
+ /*
+ * Get the position of the context node in the context node set,
+ * starting with 1.
+ */
+ virtual uint32_t position() = 0;
+};
+
+#define TX_DECL_EVAL_CONTEXT \
+ TX_DECL_MATCH_CONTEXT; \
+ const txXPathNode& getContextNode(); \
+ uint32_t size(); \
+ uint32_t position()
+
+#endif // __TX_I_XPATH_CONTEXT
diff --git a/dom/xslt/xpath/txLiteralExpr.cpp b/dom/xslt/xpath/txLiteralExpr.cpp
new file mode 100644
index 000000000..8c984dcd1
--- /dev/null
+++ b/dom/xslt/xpath/txLiteralExpr.cpp
@@ -0,0 +1,97 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "txExpr.h"
+
+nsresult
+txLiteralExpr::evaluate(txIEvalContext* aContext, txAExprResult** aResult)
+{
+ NS_ENSURE_TRUE(mValue, NS_ERROR_OUT_OF_MEMORY);
+
+ *aResult = mValue;
+ NS_ADDREF(*aResult);
+
+ return NS_OK;
+}
+
+static Expr::ResultType resultTypes[] =
+{
+ Expr::NODESET_RESULT, // NODESET
+ Expr::BOOLEAN_RESULT, // BOOLEAN
+ Expr::NUMBER_RESULT, // NUMBER
+ Expr::STRING_RESULT, // STRING
+ Expr::RTF_RESULT // RESULT_TREE_FRAGMENT
+};
+
+Expr::ResultType
+txLiteralExpr::getReturnType()
+{
+ return resultTypes[mValue->getResultType()];
+}
+
+Expr*
+txLiteralExpr::getSubExprAt(uint32_t aPos)
+{
+ return nullptr;
+}
+void
+txLiteralExpr::setSubExprAt(uint32_t aPos, Expr* aExpr)
+{
+ NS_NOTREACHED("setting bad subexpression index");
+}
+
+bool
+txLiteralExpr::isSensitiveTo(ContextSensitivity aContext)
+{
+ return false;
+}
+
+#ifdef TX_TO_STRING
+void
+txLiteralExpr::toString(nsAString& aStr)
+{
+ switch (mValue->getResultType()) {
+ case txAExprResult::NODESET:
+ {
+ aStr.AppendLiteral(" { Nodeset literal } ");
+ return;
+ }
+ case txAExprResult::BOOLEAN:
+ {
+ if (mValue->booleanValue()) {
+ aStr.AppendLiteral("true()");
+ }
+ else {
+ aStr.AppendLiteral("false()");
+ }
+ return;
+ }
+ case txAExprResult::NUMBER:
+ {
+ txDouble::toString(mValue->numberValue(), aStr);
+ return;
+ }
+ case txAExprResult::STRING:
+ {
+ StringResult* strRes =
+ static_cast<StringResult*>(static_cast<txAExprResult*>
+ (mValue));
+ char16_t ch = '\'';
+ if (strRes->mValue.Contains(ch)) {
+ ch = '\"';
+ }
+ aStr.Append(ch);
+ aStr.Append(strRes->mValue);
+ aStr.Append(ch);
+ return;
+ }
+ case txAExprResult::RESULT_TREE_FRAGMENT:
+ {
+ aStr.AppendLiteral(" { RTF literal } ");
+ return;
+ }
+ }
+}
+#endif
diff --git a/dom/xslt/xpath/txLocationStep.cpp b/dom/xslt/xpath/txLocationStep.cpp
new file mode 100644
index 000000000..f945b750f
--- /dev/null
+++ b/dom/xslt/xpath/txLocationStep.cpp
@@ -0,0 +1,325 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/*
+ Implementation of an XPath LocationStep
+*/
+
+#include "txExpr.h"
+#include "txIXPathContext.h"
+#include "txNodeSet.h"
+#include "txXPathTreeWalker.h"
+
+ //-----------------------------/
+ //- Virtual methods from Expr -/
+//-----------------------------/
+
+/**
+ * Evaluates this Expr based on the given context node and processor state
+ * @param context the context node for evaluation of this Expr
+ * @param ps the ProcessorState containing the stack information needed
+ * for evaluation
+ * @return the result of the evaluation
+ * @see Expr
+**/
+nsresult
+LocationStep::evaluate(txIEvalContext* aContext, txAExprResult** aResult)
+{
+ NS_ASSERTION(aContext, "internal error");
+ *aResult = nullptr;
+
+ RefPtr<txNodeSet> nodes;
+ nsresult rv = aContext->recycler()->getNodeSet(getter_AddRefs(nodes));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ txXPathTreeWalker walker(aContext->getContextNode());
+
+ switch (mAxisIdentifier) {
+ case ANCESTOR_AXIS:
+ {
+ if (!walker.moveToParent()) {
+ break;
+ }
+ MOZ_FALLTHROUGH;
+ }
+ case ANCESTOR_OR_SELF_AXIS:
+ {
+ nodes->setReverse();
+
+ do {
+ if (mNodeTest->matches(walker.getCurrentPosition(), aContext)) {
+ nodes->append(walker.getCurrentPosition());
+ }
+ } while (walker.moveToParent());
+
+ break;
+ }
+ case ATTRIBUTE_AXIS:
+ {
+ if (!walker.moveToFirstAttribute()) {
+ break;
+ }
+
+ do {
+ if (mNodeTest->matches(walker.getCurrentPosition(), aContext)) {
+ nodes->append(walker.getCurrentPosition());
+ }
+ } while (walker.moveToNextAttribute());
+ break;
+ }
+ case DESCENDANT_OR_SELF_AXIS:
+ {
+ if (mNodeTest->matches(walker.getCurrentPosition(), aContext)) {
+ nodes->append(walker.getCurrentPosition());
+ }
+ MOZ_FALLTHROUGH;
+ }
+ case DESCENDANT_AXIS:
+ {
+ fromDescendants(walker.getCurrentPosition(), aContext, nodes);
+ break;
+ }
+ case FOLLOWING_AXIS:
+ {
+ if (txXPathNodeUtils::isAttribute(walker.getCurrentPosition())) {
+ walker.moveToParent();
+ fromDescendants(walker.getCurrentPosition(), aContext, nodes);
+ }
+ bool cont = true;
+ while (!walker.moveToNextSibling()) {
+ if (!walker.moveToParent()) {
+ cont = false;
+ break;
+ }
+ }
+ while (cont) {
+ if (mNodeTest->matches(walker.getCurrentPosition(), aContext)) {
+ nodes->append(walker.getCurrentPosition());
+ }
+
+ fromDescendants(walker.getCurrentPosition(), aContext, nodes);
+
+ while (!walker.moveToNextSibling()) {
+ if (!walker.moveToParent()) {
+ cont = false;
+ break;
+ }
+ }
+ }
+ break;
+ }
+ case FOLLOWING_SIBLING_AXIS:
+ {
+ while (walker.moveToNextSibling()) {
+ if (mNodeTest->matches(walker.getCurrentPosition(), aContext)) {
+ nodes->append(walker.getCurrentPosition());
+ }
+ }
+ break;
+ }
+ case NAMESPACE_AXIS: //-- not yet implemented
+#if 0
+ // XXX DEBUG OUTPUT
+ cout << "namespace axis not yet implemented"<<endl;
+#endif
+ break;
+ case PARENT_AXIS :
+ {
+ if (walker.moveToParent() &&
+ mNodeTest->matches(walker.getCurrentPosition(), aContext)) {
+ nodes->append(walker.getCurrentPosition());
+ }
+ break;
+ }
+ case PRECEDING_AXIS:
+ {
+ nodes->setReverse();
+
+ bool cont = true;
+ while (!walker.moveToPreviousSibling()) {
+ if (!walker.moveToParent()) {
+ cont = false;
+ break;
+ }
+ }
+ while (cont) {
+ fromDescendantsRev(walker.getCurrentPosition(), aContext, nodes);
+
+ if (mNodeTest->matches(walker.getCurrentPosition(), aContext)) {
+ nodes->append(walker.getCurrentPosition());
+ }
+
+ while (!walker.moveToPreviousSibling()) {
+ if (!walker.moveToParent()) {
+ cont = false;
+ break;
+ }
+ }
+ }
+ break;
+ }
+ case PRECEDING_SIBLING_AXIS:
+ {
+ nodes->setReverse();
+
+ while (walker.moveToPreviousSibling()) {
+ if (mNodeTest->matches(walker.getCurrentPosition(), aContext)) {
+ nodes->append(walker.getCurrentPosition());
+ }
+ }
+ break;
+ }
+ case SELF_AXIS:
+ {
+ if (mNodeTest->matches(walker.getCurrentPosition(), aContext)) {
+ nodes->append(walker.getCurrentPosition());
+ }
+ break;
+ }
+ default: // Children Axis
+ {
+ if (!walker.moveToFirstChild()) {
+ break;
+ }
+
+ do {
+ if (mNodeTest->matches(walker.getCurrentPosition(), aContext)) {
+ nodes->append(walker.getCurrentPosition());
+ }
+ } while (walker.moveToNextSibling());
+ break;
+ }
+ }
+
+ // Apply predicates
+ if (!isEmpty()) {
+ rv = evaluatePredicates(nodes, aContext);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ nodes->unsetReverse();
+
+ NS_ADDREF(*aResult = nodes);
+
+ return NS_OK;
+}
+
+void LocationStep::fromDescendants(const txXPathNode& aNode,
+ txIMatchContext* aCs,
+ txNodeSet* aNodes)
+{
+ txXPathTreeWalker walker(aNode);
+ if (!walker.moveToFirstChild()) {
+ return;
+ }
+
+ do {
+ const txXPathNode& child = walker.getCurrentPosition();
+ if (mNodeTest->matches(child, aCs)) {
+ aNodes->append(child);
+ }
+ fromDescendants(child, aCs, aNodes);
+ } while (walker.moveToNextSibling());
+}
+
+void LocationStep::fromDescendantsRev(const txXPathNode& aNode,
+ txIMatchContext* aCs,
+ txNodeSet* aNodes)
+{
+ txXPathTreeWalker walker(aNode);
+ if (!walker.moveToLastChild()) {
+ return;
+ }
+
+ do {
+ const txXPathNode& child = walker.getCurrentPosition();
+ fromDescendantsRev(child, aCs, aNodes);
+
+ if (mNodeTest->matches(child, aCs)) {
+ aNodes->append(child);
+ }
+
+ } while (walker.moveToPreviousSibling());
+}
+
+Expr::ExprType
+LocationStep::getType()
+{
+ return LOCATIONSTEP_EXPR;
+}
+
+
+TX_IMPL_EXPR_STUBS_BASE(LocationStep, NODESET_RESULT)
+
+Expr*
+LocationStep::getSubExprAt(uint32_t aPos)
+{
+ return PredicateList::getSubExprAt(aPos);
+}
+
+void
+LocationStep::setSubExprAt(uint32_t aPos, Expr* aExpr)
+{
+ PredicateList::setSubExprAt(aPos, aExpr);
+}
+
+bool
+LocationStep::isSensitiveTo(ContextSensitivity aContext)
+{
+ return (aContext & NODE_CONTEXT) ||
+ mNodeTest->isSensitiveTo(aContext) ||
+ PredicateList::isSensitiveTo(aContext);
+}
+
+#ifdef TX_TO_STRING
+void
+LocationStep::toString(nsAString& str)
+{
+ switch (mAxisIdentifier) {
+ case ANCESTOR_AXIS :
+ str.AppendLiteral("ancestor::");
+ break;
+ case ANCESTOR_OR_SELF_AXIS :
+ str.AppendLiteral("ancestor-or-self::");
+ break;
+ case ATTRIBUTE_AXIS:
+ str.Append(char16_t('@'));
+ break;
+ case DESCENDANT_AXIS:
+ str.AppendLiteral("descendant::");
+ break;
+ case DESCENDANT_OR_SELF_AXIS:
+ str.AppendLiteral("descendant-or-self::");
+ break;
+ case FOLLOWING_AXIS :
+ str.AppendLiteral("following::");
+ break;
+ case FOLLOWING_SIBLING_AXIS:
+ str.AppendLiteral("following-sibling::");
+ break;
+ case NAMESPACE_AXIS:
+ str.AppendLiteral("namespace::");
+ break;
+ case PARENT_AXIS :
+ str.AppendLiteral("parent::");
+ break;
+ case PRECEDING_AXIS :
+ str.AppendLiteral("preceding::");
+ break;
+ case PRECEDING_SIBLING_AXIS :
+ str.AppendLiteral("preceding-sibling::");
+ break;
+ case SELF_AXIS :
+ str.AppendLiteral("self::");
+ break;
+ default:
+ break;
+ }
+ NS_ASSERTION(mNodeTest, "mNodeTest is null, that's verboten");
+ mNodeTest->toString(str);
+
+ PredicateList::toString(str);
+}
+#endif
diff --git a/dom/xslt/xpath/txMozillaXPathTreeWalker.cpp b/dom/xslt/xpath/txMozillaXPathTreeWalker.cpp
new file mode 100644
index 000000000..1387097e1
--- /dev/null
+++ b/dom/xslt/xpath/txMozillaXPathTreeWalker.cpp
@@ -0,0 +1,776 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "txXPathTreeWalker.h"
+#include "nsIAtom.h"
+#include "nsIAttribute.h"
+#include "nsIDOMAttr.h"
+#include "nsIDOMDocument.h"
+#include "nsIDOMNode.h"
+#include "nsIDOMElement.h"
+#include "nsIDOMProcessingInstruction.h"
+#include "nsPrintfCString.h"
+#include "nsReadableUtils.h"
+#include "nsString.h"
+#include "nsTextFragment.h"
+#include "txXMLUtils.h"
+#include "txLog.h"
+#include "nsUnicharUtils.h"
+#include "nsAttrName.h"
+#include "nsTArray.h"
+#include "mozilla/dom/Attr.h"
+#include "mozilla/dom/Element.h"
+#include <stdint.h>
+#include <algorithm>
+
+using namespace mozilla::dom;
+
+const uint32_t kUnknownIndex = uint32_t(-1);
+
+txXPathTreeWalker::txXPathTreeWalker(const txXPathTreeWalker& aOther)
+ : mPosition(aOther.mPosition),
+ mCurrentIndex(aOther.mCurrentIndex)
+{
+}
+
+txXPathTreeWalker::txXPathTreeWalker(const txXPathNode& aNode)
+ : mPosition(aNode),
+ mCurrentIndex(kUnknownIndex)
+{
+}
+
+void
+txXPathTreeWalker::moveToRoot()
+{
+ if (mPosition.isDocument()) {
+ return;
+ }
+
+ nsIDocument* root = mPosition.mNode->GetUncomposedDoc();
+ if (root) {
+ mPosition.mIndex = txXPathNode::eDocument;
+ mPosition.mNode = root;
+ }
+ else {
+ nsINode *rootNode = mPosition.Root();
+
+ NS_ASSERTION(rootNode->IsNodeOfType(nsINode::eCONTENT),
+ "root of subtree wasn't an nsIContent");
+
+ mPosition.mIndex = txXPathNode::eContent;
+ mPosition.mNode = rootNode;
+ }
+
+ mCurrentIndex = kUnknownIndex;
+ mDescendants.Clear();
+}
+
+bool
+txXPathTreeWalker::moveToElementById(const nsAString& aID)
+{
+ if (aID.IsEmpty()) {
+ return false;
+ }
+
+ nsIDocument* doc = mPosition.mNode->GetUncomposedDoc();
+
+ nsCOMPtr<nsIContent> content;
+ if (doc) {
+ content = doc->GetElementById(aID);
+ }
+ else {
+ // We're in a disconnected subtree, search only that subtree.
+ nsINode *rootNode = mPosition.Root();
+
+ NS_ASSERTION(rootNode->IsNodeOfType(nsINode::eCONTENT),
+ "root of subtree wasn't an nsIContent");
+
+ content = nsContentUtils::MatchElementId(
+ static_cast<nsIContent*>(rootNode), aID);
+ }
+
+ if (!content) {
+ return false;
+ }
+
+ mPosition.mIndex = txXPathNode::eContent;
+ mPosition.mNode = content;
+ mCurrentIndex = kUnknownIndex;
+ mDescendants.Clear();
+
+ return true;
+}
+
+bool
+txXPathTreeWalker::moveToFirstAttribute()
+{
+ if (!mPosition.isContent()) {
+ return false;
+ }
+
+ return moveToValidAttribute(0);
+}
+
+bool
+txXPathTreeWalker::moveToNextAttribute()
+{
+ // XXX an assertion should be enough here with the current code
+ if (!mPosition.isAttribute()) {
+ return false;
+ }
+
+ return moveToValidAttribute(mPosition.mIndex + 1);
+}
+
+bool
+txXPathTreeWalker::moveToValidAttribute(uint32_t aStartIndex)
+{
+ NS_ASSERTION(!mPosition.isDocument(), "documents doesn't have attrs");
+
+ uint32_t total = mPosition.Content()->GetAttrCount();
+ if (aStartIndex >= total) {
+ return false;
+ }
+
+ uint32_t index;
+ for (index = aStartIndex; index < total; ++index) {
+ const nsAttrName* name = mPosition.Content()->GetAttrNameAt(index);
+
+ // We need to ignore XMLNS attributes.
+ if (name->NamespaceID() != kNameSpaceID_XMLNS) {
+ mPosition.mIndex = index;
+
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+txXPathTreeWalker::moveToNamedAttribute(nsIAtom* aLocalName, int32_t aNSID)
+{
+ if (!mPosition.isContent()) {
+ return false;
+ }
+
+ const nsAttrName* name;
+ uint32_t i;
+ for (i = 0; (name = mPosition.Content()->GetAttrNameAt(i)); ++i) {
+ if (name->Equals(aLocalName, aNSID)) {
+ mPosition.mIndex = i;
+
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+txXPathTreeWalker::moveToFirstChild()
+{
+ if (mPosition.isAttribute()) {
+ return false;
+ }
+
+ NS_ASSERTION(!mPosition.isDocument() ||
+ (mCurrentIndex == kUnknownIndex && mDescendants.IsEmpty()),
+ "we shouldn't have any position info at the document");
+ NS_ASSERTION(mCurrentIndex != kUnknownIndex || mDescendants.IsEmpty(),
+ "Index should be known if parents index are");
+
+ nsIContent* child = mPosition.mNode->GetFirstChild();
+ if (!child) {
+ return false;
+ }
+ mPosition.mIndex = txXPathNode::eContent;
+ mPosition.mNode = child;
+
+ if (mCurrentIndex != kUnknownIndex &&
+ !mDescendants.AppendValue(mCurrentIndex)) {
+ mDescendants.Clear();
+ }
+ mCurrentIndex = 0;
+
+ return true;
+}
+
+bool
+txXPathTreeWalker::moveToLastChild()
+{
+ if (mPosition.isAttribute()) {
+ return false;
+ }
+
+ NS_ASSERTION(!mPosition.isDocument() ||
+ (mCurrentIndex == kUnknownIndex && mDescendants.IsEmpty()),
+ "we shouldn't have any position info at the document");
+ NS_ASSERTION(mCurrentIndex != kUnknownIndex || mDescendants.IsEmpty(),
+ "Index should be known if parents index are");
+
+ uint32_t total = mPosition.mNode->GetChildCount();
+ if (!total) {
+ return false;
+ }
+ mPosition.mNode = mPosition.mNode->GetLastChild();
+
+ if (mCurrentIndex != kUnknownIndex &&
+ !mDescendants.AppendValue(mCurrentIndex)) {
+ mDescendants.Clear();
+ }
+ mCurrentIndex = total - 1;
+
+ return true;
+}
+
+bool
+txXPathTreeWalker::moveToNextSibling()
+{
+ if (!mPosition.isContent()) {
+ return false;
+ }
+
+ return moveToSibling(1);
+}
+
+bool
+txXPathTreeWalker::moveToPreviousSibling()
+{
+ if (!mPosition.isContent()) {
+ return false;
+ }
+
+ return moveToSibling(-1);
+}
+
+bool
+txXPathTreeWalker::moveToParent()
+{
+ if (mPosition.isDocument()) {
+ return false;
+ }
+
+ if (mPosition.isAttribute()) {
+ mPosition.mIndex = txXPathNode::eContent;
+
+ return true;
+ }
+
+ nsINode* parent = mPosition.mNode->GetParentNode();
+ if (!parent) {
+ return false;
+ }
+
+ uint32_t count = mDescendants.Length();
+ if (count) {
+ mCurrentIndex = mDescendants.ValueAt(--count);
+ mDescendants.RemoveValueAt(count);
+ }
+ else {
+ mCurrentIndex = kUnknownIndex;
+ }
+
+ mPosition.mIndex = mPosition.mNode->GetParent() ?
+ txXPathNode::eContent : txXPathNode::eDocument;
+ mPosition.mNode = parent;
+
+ return true;
+}
+
+bool
+txXPathTreeWalker::moveToSibling(int32_t aDir)
+{
+ NS_ASSERTION(mPosition.isContent(),
+ "moveToSibling should only be called for content");
+
+ nsINode* parent = mPosition.mNode->GetParentNode();
+ if (!parent) {
+ return false;
+ }
+ if (mCurrentIndex == kUnknownIndex) {
+ mCurrentIndex = parent->IndexOf(mPosition.mNode);
+ }
+
+ // if mCurrentIndex is 0 we rely on GetChildAt returning null for an
+ // index of uint32_t(-1).
+ uint32_t newIndex = mCurrentIndex + aDir;
+ nsIContent* newChild = parent->GetChildAt(newIndex);
+ if (!newChild) {
+ return false;
+ }
+
+ mPosition.mNode = newChild;
+ mCurrentIndex = newIndex;
+
+ return true;
+}
+
+txXPathNode::txXPathNode(const txXPathNode& aNode)
+ : mNode(aNode.mNode),
+ mRefCountRoot(aNode.mRefCountRoot),
+ mIndex(aNode.mIndex)
+{
+ MOZ_COUNT_CTOR(txXPathNode);
+ if (mRefCountRoot) {
+ NS_ADDREF(Root());
+ }
+}
+
+txXPathNode::~txXPathNode()
+{
+ MOZ_COUNT_DTOR(txXPathNode);
+ if (mRefCountRoot) {
+ nsINode *root = Root();
+ NS_RELEASE(root);
+ }
+}
+
+/* static */
+bool
+txXPathNodeUtils::getAttr(const txXPathNode& aNode, nsIAtom* aLocalName,
+ int32_t aNSID, nsAString& aValue)
+{
+ if (aNode.isDocument() || aNode.isAttribute()) {
+ return false;
+ }
+
+ return aNode.Content()->GetAttr(aNSID, aLocalName, aValue);
+}
+
+/* static */
+already_AddRefed<nsIAtom>
+txXPathNodeUtils::getLocalName(const txXPathNode& aNode)
+{
+ if (aNode.isDocument()) {
+ return nullptr;
+ }
+
+ if (aNode.isContent()) {
+ if (aNode.mNode->IsElement()) {
+ nsCOMPtr<nsIAtom> localName =
+ aNode.Content()->NodeInfo()->NameAtom();
+ return localName.forget();
+ }
+
+ if (aNode.mNode->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION)) {
+ nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aNode.mNode);
+ nsAutoString target;
+ node->GetNodeName(target);
+
+ return NS_Atomize(target);
+ }
+
+ return nullptr;
+ }
+
+ nsCOMPtr<nsIAtom> localName = aNode.Content()->
+ GetAttrNameAt(aNode.mIndex)->LocalName();
+
+ return localName.forget();
+}
+
+nsIAtom*
+txXPathNodeUtils::getPrefix(const txXPathNode& aNode)
+{
+ if (aNode.isDocument()) {
+ return nullptr;
+ }
+
+ if (aNode.isContent()) {
+ // All other nsIContent node types but elements have a null prefix
+ // which is what we want here.
+ return aNode.Content()->NodeInfo()->GetPrefixAtom();
+ }
+
+ return aNode.Content()->GetAttrNameAt(aNode.mIndex)->GetPrefix();
+}
+
+/* static */
+void
+txXPathNodeUtils::getLocalName(const txXPathNode& aNode, nsAString& aLocalName)
+{
+ if (aNode.isDocument()) {
+ aLocalName.Truncate();
+
+ return;
+ }
+
+ if (aNode.isContent()) {
+ if (aNode.mNode->IsElement()) {
+ mozilla::dom::NodeInfo* nodeInfo = aNode.Content()->NodeInfo();
+ nodeInfo->GetName(aLocalName);
+ return;
+ }
+
+ if (aNode.mNode->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION)) {
+ // PIs don't have a nodeinfo but do have a name
+ nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aNode.mNode);
+ node->GetNodeName(aLocalName);
+
+ return;
+ }
+
+ aLocalName.Truncate();
+
+ return;
+ }
+
+ aNode.Content()->GetAttrNameAt(aNode.mIndex)->LocalName()->
+ ToString(aLocalName);
+
+ // Check for html
+ if (aNode.Content()->NodeInfo()->NamespaceEquals(kNameSpaceID_None) &&
+ aNode.Content()->IsHTMLElement()) {
+ nsContentUtils::ASCIIToUpper(aLocalName);
+ }
+}
+
+/* static */
+void
+txXPathNodeUtils::getNodeName(const txXPathNode& aNode, nsAString& aName)
+{
+ if (aNode.isDocument()) {
+ aName.Truncate();
+
+ return;
+ }
+
+ if (aNode.isContent()) {
+ // Elements and PIs have a name
+ if (aNode.mNode->IsElement() ||
+ aNode.mNode->NodeType() ==
+ nsIDOMNode::PROCESSING_INSTRUCTION_NODE) {
+ aName = aNode.Content()->NodeName();
+ return;
+ }
+
+ aName.Truncate();
+
+ return;
+ }
+
+ aNode.Content()->GetAttrNameAt(aNode.mIndex)->GetQualifiedName(aName);
+}
+
+/* static */
+int32_t
+txXPathNodeUtils::getNamespaceID(const txXPathNode& aNode)
+{
+ if (aNode.isDocument()) {
+ return kNameSpaceID_None;
+ }
+
+ if (aNode.isContent()) {
+ return aNode.Content()->GetNameSpaceID();
+ }
+
+ return aNode.Content()->GetAttrNameAt(aNode.mIndex)->NamespaceID();
+}
+
+/* static */
+void
+txXPathNodeUtils::getNamespaceURI(const txXPathNode& aNode, nsAString& aURI)
+{
+ nsContentUtils::NameSpaceManager()->GetNameSpaceURI(getNamespaceID(aNode), aURI);
+}
+
+/* static */
+uint16_t
+txXPathNodeUtils::getNodeType(const txXPathNode& aNode)
+{
+ if (aNode.isDocument()) {
+ return txXPathNodeType::DOCUMENT_NODE;
+ }
+
+ if (aNode.isContent()) {
+ return aNode.mNode->NodeType();
+ }
+
+ return txXPathNodeType::ATTRIBUTE_NODE;
+}
+
+/* static */
+void
+txXPathNodeUtils::appendNodeValue(const txXPathNode& aNode, nsAString& aResult)
+{
+ if (aNode.isAttribute()) {
+ const nsAttrName* name = aNode.Content()->GetAttrNameAt(aNode.mIndex);
+
+ if (aResult.IsEmpty()) {
+ aNode.Content()->GetAttr(name->NamespaceID(), name->LocalName(),
+ aResult);
+ }
+ else {
+ nsAutoString result;
+ aNode.Content()->GetAttr(name->NamespaceID(), name->LocalName(),
+ result);
+ aResult.Append(result);
+ }
+
+ return;
+ }
+
+ if (aNode.isDocument() ||
+ aNode.mNode->IsElement() ||
+ aNode.mNode->IsNodeOfType(nsINode::eDOCUMENT_FRAGMENT)) {
+ nsContentUtils::AppendNodeTextContent(aNode.mNode, true, aResult,
+ mozilla::fallible);
+
+ return;
+ }
+
+ aNode.Content()->AppendTextTo(aResult);
+}
+
+/* static */
+bool
+txXPathNodeUtils::isWhitespace(const txXPathNode& aNode)
+{
+ NS_ASSERTION(aNode.isContent() && isText(aNode), "Wrong type!");
+
+ return aNode.Content()->TextIsOnlyWhitespace();
+}
+
+/* static */
+txXPathNode*
+txXPathNodeUtils::getOwnerDocument(const txXPathNode& aNode)
+{
+ return new txXPathNode(aNode.mNode->OwnerDoc());
+}
+
+const char gPrintfFmt[] = "id0x%p";
+const char gPrintfFmtAttr[] = "id0x%p-%010i";
+
+/* static */
+nsresult
+txXPathNodeUtils::getXSLTId(const txXPathNode& aNode,
+ const txXPathNode& aBase,
+ nsAString& aResult)
+{
+ uintptr_t nodeid = ((uintptr_t)aNode.mNode) - ((uintptr_t)aBase.mNode);
+ if (!aNode.isAttribute()) {
+ CopyASCIItoUTF16(nsPrintfCString(gPrintfFmt, nodeid),
+ aResult);
+ }
+ else {
+ CopyASCIItoUTF16(nsPrintfCString(gPrintfFmtAttr,
+ nodeid, aNode.mIndex), aResult);
+ }
+
+ return NS_OK;
+}
+
+/* static */
+nsresult
+txXPathNodeUtils::getBaseURI(const txXPathNode& aNode, nsAString& aURI)
+{
+ return aNode.mNode->GetBaseURI(aURI);
+}
+
+/* static */
+int
+txXPathNodeUtils::comparePosition(const txXPathNode& aNode,
+ const txXPathNode& aOtherNode)
+{
+ // First check for equal nodes or attribute-nodes on the same element.
+ if (aNode.mNode == aOtherNode.mNode) {
+ if (aNode.mIndex == aOtherNode.mIndex) {
+ return 0;
+ }
+
+ NS_ASSERTION(!aNode.isDocument() && !aOtherNode.isDocument(),
+ "documents should always have a set index");
+
+ if (aNode.isContent() || (!aOtherNode.isContent() &&
+ aNode.mIndex < aOtherNode.mIndex)) {
+ return -1;
+ }
+
+ return 1;
+ }
+
+ // Get document for both nodes.
+ nsIDocument* document = aNode.mNode->GetUncomposedDoc();
+ nsIDocument* otherDocument = aOtherNode.mNode->GetUncomposedDoc();
+
+ // If the nodes have different current documents, compare the document
+ // pointers.
+ if (document != otherDocument) {
+ return document < otherDocument ? -1 : 1;
+ }
+
+ // Now either both nodes are in orphan trees, or they are both in the
+ // same tree.
+
+ // Get parents up the tree.
+ AutoTArray<nsINode*, 8> parents, otherParents;
+ nsINode* node = aNode.mNode;
+ nsINode* otherNode = aOtherNode.mNode;
+ nsINode* parent;
+ nsINode* otherParent;
+ while (node && otherNode) {
+ parent = node->GetParentNode();
+ otherParent = otherNode->GetParentNode();
+
+ // Hopefully this is a common case.
+ if (parent == otherParent) {
+ if (!parent) {
+ // Both node and otherNode are root nodes in respective orphan
+ // tree.
+ return node < otherNode ? -1 : 1;
+ }
+
+ return parent->IndexOf(node) < parent->IndexOf(otherNode) ?
+ -1 : 1;
+ }
+
+ parents.AppendElement(node);
+ otherParents.AppendElement(otherNode);
+ node = parent;
+ otherNode = otherParent;
+ }
+
+ while (node) {
+ parents.AppendElement(node);
+ node = node->GetParentNode();
+ }
+ while (otherNode) {
+ otherParents.AppendElement(otherNode);
+ otherNode = otherNode->GetParentNode();
+ }
+
+ // Walk back down along the parent-chains until we find where they split.
+ int32_t total = parents.Length() - 1;
+ int32_t otherTotal = otherParents.Length() - 1;
+ NS_ASSERTION(total != otherTotal, "Can't have same number of parents");
+
+ int32_t lastIndex = std::min(total, otherTotal);
+ int32_t i;
+ parent = nullptr;
+ for (i = 0; i <= lastIndex; ++i) {
+ node = parents.ElementAt(total - i);
+ otherNode = otherParents.ElementAt(otherTotal - i);
+ if (node != otherNode) {
+ if (!parent) {
+ // The two nodes are in different orphan subtrees.
+ NS_ASSERTION(i == 0, "this shouldn't happen");
+ return node < otherNode ? -1 : 1;
+ }
+
+ int32_t index = parent->IndexOf(node);
+ int32_t otherIndex = parent->IndexOf(otherNode);
+ NS_ASSERTION(index != otherIndex && index >= 0 && otherIndex >= 0,
+ "invalid index in compareTreePosition");
+
+ return index < otherIndex ? -1 : 1;
+ }
+
+ parent = node;
+ }
+
+ // One node is a descendant of the other. The one with the shortest
+ // parent-chain is first in the document.
+ return total < otherTotal ? -1 : 1;
+}
+
+/* static */
+txXPathNode*
+txXPathNativeNode::createXPathNode(nsIContent* aContent, bool aKeepRootAlive)
+{
+ nsINode* root = aKeepRootAlive ? txXPathNode::RootOf(aContent) : nullptr;
+
+ return new txXPathNode(aContent, txXPathNode::eContent, root);
+}
+
+/* static */
+txXPathNode*
+txXPathNativeNode::createXPathNode(nsINode* aNode, bool aKeepRootAlive)
+{
+ uint16_t nodeType = aNode->NodeType();
+ if (nodeType == nsIDOMNode::ATTRIBUTE_NODE) {
+ nsCOMPtr<nsIAttribute> attr = do_QueryInterface(aNode);
+ NS_ASSERTION(attr, "doesn't implement nsIAttribute");
+
+ mozilla::dom::NodeInfo *nodeInfo = attr->NodeInfo();
+ mozilla::dom::Element* parent =
+ static_cast<Attr*>(attr.get())->GetElement();
+ if (!parent) {
+ return nullptr;
+ }
+
+ nsINode* root = aKeepRootAlive ? txXPathNode::RootOf(parent) : nullptr;
+
+ uint32_t i, total = parent->GetAttrCount();
+ for (i = 0; i < total; ++i) {
+ const nsAttrName* name = parent->GetAttrNameAt(i);
+ if (nodeInfo->Equals(name->LocalName(), name->NamespaceID())) {
+ return new txXPathNode(parent, i, root);
+ }
+ }
+
+ NS_ERROR("Couldn't find the attribute in its parent!");
+
+ return nullptr;
+ }
+
+ uint32_t index;
+ nsINode* root = aKeepRootAlive ? aNode : nullptr;
+
+ if (nodeType == nsIDOMNode::DOCUMENT_NODE) {
+ index = txXPathNode::eDocument;
+ }
+ else {
+ index = txXPathNode::eContent;
+ if (root) {
+ root = txXPathNode::RootOf(root);
+ }
+ }
+
+ return new txXPathNode(aNode, index, root);
+}
+
+/* static */
+txXPathNode*
+txXPathNativeNode::createXPathNode(nsIDOMDocument* aDocument)
+{
+ nsCOMPtr<nsIDocument> document = do_QueryInterface(aDocument);
+ return new txXPathNode(document);
+}
+
+/* static */
+nsINode*
+txXPathNativeNode::getNode(const txXPathNode& aNode)
+{
+ if (!aNode.isAttribute()) {
+ return aNode.mNode;
+ }
+
+ const nsAttrName* name = aNode.Content()->GetAttrNameAt(aNode.mIndex);
+
+ nsAutoString namespaceURI;
+ nsContentUtils::NameSpaceManager()->GetNameSpaceURI(name->NamespaceID(), namespaceURI);
+
+ nsCOMPtr<Element> element = do_QueryInterface(aNode.mNode);
+ nsDOMAttributeMap* map = element->Attributes();
+ return map->GetNamedItemNS(namespaceURI,
+ nsDependentAtomString(name->LocalName()));
+}
+
+/* static */
+nsIContent*
+txXPathNativeNode::getContent(const txXPathNode& aNode)
+{
+ NS_ASSERTION(aNode.isContent(),
+ "Only call getContent on nsIContent wrappers!");
+ return aNode.Content();
+}
+
+/* static */
+nsIDocument*
+txXPathNativeNode::getDocument(const txXPathNode& aNode)
+{
+ NS_ASSERTION(aNode.isDocument(),
+ "Only call getDocument on nsIDocument wrappers!");
+ return aNode.Document();
+}
diff --git a/dom/xslt/xpath/txNameTest.cpp b/dom/xslt/xpath/txNameTest.cpp
new file mode 100644
index 000000000..bad043282
--- /dev/null
+++ b/dom/xslt/xpath/txNameTest.cpp
@@ -0,0 +1,95 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "txExpr.h"
+#include "nsIAtom.h"
+#include "nsGkAtoms.h"
+#include "txXPathTreeWalker.h"
+#include "txIXPathContext.h"
+
+txNameTest::txNameTest(nsIAtom* aPrefix, nsIAtom* aLocalName, int32_t aNSID,
+ uint16_t aNodeType)
+ :mPrefix(aPrefix), mLocalName(aLocalName), mNamespace(aNSID),
+ mNodeType(aNodeType)
+{
+ if (aPrefix == nsGkAtoms::_empty)
+ mPrefix = nullptr;
+ NS_ASSERTION(aLocalName, "txNameTest without a local name?");
+ NS_ASSERTION(aNodeType == txXPathNodeType::DOCUMENT_NODE ||
+ aNodeType == txXPathNodeType::ELEMENT_NODE ||
+ aNodeType == txXPathNodeType::ATTRIBUTE_NODE,
+ "Go fix txNameTest::matches");
+}
+
+bool txNameTest::matches(const txXPathNode& aNode, txIMatchContext* aContext)
+{
+ if ((mNodeType == txXPathNodeType::ELEMENT_NODE &&
+ !txXPathNodeUtils::isElement(aNode)) ||
+ (mNodeType == txXPathNodeType::ATTRIBUTE_NODE &&
+ !txXPathNodeUtils::isAttribute(aNode)) ||
+ (mNodeType == txXPathNodeType::DOCUMENT_NODE &&
+ !txXPathNodeUtils::isRoot(aNode))) {
+ return false;
+ }
+
+ // Totally wild?
+ if (mLocalName == nsGkAtoms::_asterisk && !mPrefix)
+ return true;
+
+ // Compare namespaces
+ if (mNamespace != txXPathNodeUtils::getNamespaceID(aNode)
+ && !(mNamespace == kNameSpaceID_None &&
+ txXPathNodeUtils::isHTMLElementInHTMLDocument(aNode))
+ )
+ return false;
+
+ // Name wild?
+ if (mLocalName == nsGkAtoms::_asterisk)
+ return true;
+
+ // Compare local-names
+ return txXPathNodeUtils::localNameEquals(aNode, mLocalName);
+}
+
+/*
+ * Returns the default priority of this txNodeTest
+ */
+double txNameTest::getDefaultPriority()
+{
+ if (mLocalName == nsGkAtoms::_asterisk) {
+ if (!mPrefix)
+ return -0.5;
+ return -0.25;
+ }
+ return 0;
+}
+
+txNodeTest::NodeTestType
+txNameTest::getType()
+{
+ return NAME_TEST;
+}
+
+bool
+txNameTest::isSensitiveTo(Expr::ContextSensitivity aContext)
+{
+ return !!(aContext & Expr::NODE_CONTEXT);
+}
+
+#ifdef TX_TO_STRING
+void
+txNameTest::toString(nsAString& aDest)
+{
+ if (mPrefix) {
+ nsAutoString prefix;
+ mPrefix->ToString(prefix);
+ aDest.Append(prefix);
+ aDest.Append(char16_t(':'));
+ }
+ nsAutoString localName;
+ mLocalName->ToString(localName);
+ aDest.Append(localName);
+}
+#endif
diff --git a/dom/xslt/xpath/txNamedAttributeStep.cpp b/dom/xslt/xpath/txNamedAttributeStep.cpp
new file mode 100644
index 000000000..e042b37af
--- /dev/null
+++ b/dom/xslt/xpath/txNamedAttributeStep.cpp
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "nsIAtom.h"
+#include "txIXPathContext.h"
+#include "txNodeSet.h"
+#include "txExpr.h"
+#include "txXPathTreeWalker.h"
+
+txNamedAttributeStep::txNamedAttributeStep(int32_t aNsID,
+ nsIAtom* aPrefix,
+ nsIAtom* aLocalName)
+ : mNamespace(aNsID),
+ mPrefix(aPrefix),
+ mLocalName(aLocalName)
+{
+}
+
+nsresult
+txNamedAttributeStep::evaluate(txIEvalContext* aContext,
+ txAExprResult** aResult)
+{
+ *aResult = nullptr;
+
+ RefPtr<txNodeSet> nodes;
+ nsresult rv = aContext->recycler()->getNodeSet(getter_AddRefs(nodes));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ txXPathTreeWalker walker(aContext->getContextNode());
+ if (walker.moveToNamedAttribute(mLocalName, mNamespace)) {
+ rv = nodes->append(walker.getCurrentPosition());
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ NS_ADDREF(*aResult = nodes);
+
+ return NS_OK;
+}
+
+TX_IMPL_EXPR_STUBS_0(txNamedAttributeStep, NODESET_RESULT)
+
+bool
+txNamedAttributeStep::isSensitiveTo(ContextSensitivity aContext)
+{
+ return !!(aContext & NODE_CONTEXT);
+}
+
+#ifdef TX_TO_STRING
+void
+txNamedAttributeStep::toString(nsAString& aDest)
+{
+ aDest.Append(char16_t('@'));
+ if (mPrefix) {
+ nsAutoString prefix;
+ mPrefix->ToString(prefix);
+ aDest.Append(prefix);
+ aDest.Append(char16_t(':'));
+ }
+ nsAutoString localName;
+ mLocalName->ToString(localName);
+ aDest.Append(localName);
+}
+#endif
diff --git a/dom/xslt/xpath/txNodeSet.cpp b/dom/xslt/xpath/txNodeSet.cpp
new file mode 100644
index 000000000..a55317e33
--- /dev/null
+++ b/dom/xslt/xpath/txNodeSet.cpp
@@ -0,0 +1,622 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "txNodeSet.h"
+#include "txLog.h"
+#include "nsMemory.h"
+#include "txXPathTreeWalker.h"
+#include <algorithm>
+
+/**
+ * Implementation of an XPath nodeset
+ */
+
+#ifdef NS_BUILD_REFCNT_LOGGING
+#define LOG_CHUNK_MOVE(_start, _new_start, _count) \
+{ \
+ txXPathNode *start = const_cast<txXPathNode*>(_start); \
+ while (start < _start + _count) { \
+ NS_LogDtor(start, "txXPathNode", sizeof(*start)); \
+ ++start; \
+ } \
+ start = const_cast<txXPathNode*>(_new_start); \
+ while (start < _new_start + _count) { \
+ NS_LogCtor(start, "txXPathNode", sizeof(*start)); \
+ ++start; \
+ } \
+}
+#else
+#define LOG_CHUNK_MOVE(_start, _new_start, _count)
+#endif
+
+static const int32_t kTxNodeSetMinSize = 4;
+static const int32_t kTxNodeSetGrowFactor = 2;
+
+#define kForward 1
+#define kReversed -1
+
+txNodeSet::txNodeSet(txResultRecycler* aRecycler)
+ : txAExprResult(aRecycler),
+ mStart(nullptr),
+ mEnd(nullptr),
+ mStartBuffer(nullptr),
+ mEndBuffer(nullptr),
+ mDirection(kForward),
+ mMarks(nullptr)
+{
+}
+
+txNodeSet::txNodeSet(const txXPathNode& aNode, txResultRecycler* aRecycler)
+ : txAExprResult(aRecycler),
+ mStart(nullptr),
+ mEnd(nullptr),
+ mStartBuffer(nullptr),
+ mEndBuffer(nullptr),
+ mDirection(kForward),
+ mMarks(nullptr)
+{
+ if (!ensureGrowSize(1)) {
+ return;
+ }
+
+ new(mStart) txXPathNode(aNode);
+ ++mEnd;
+}
+
+txNodeSet::txNodeSet(const txNodeSet& aSource, txResultRecycler* aRecycler)
+ : txAExprResult(aRecycler),
+ mStart(nullptr),
+ mEnd(nullptr),
+ mStartBuffer(nullptr),
+ mEndBuffer(nullptr),
+ mDirection(kForward),
+ mMarks(nullptr)
+{
+ append(aSource);
+}
+
+txNodeSet::~txNodeSet()
+{
+ delete [] mMarks;
+
+ if (mStartBuffer) {
+ destroyElements(mStart, mEnd);
+
+ free(mStartBuffer);
+ }
+}
+
+nsresult txNodeSet::add(const txXPathNode& aNode)
+{
+ NS_ASSERTION(mDirection == kForward,
+ "only append(aNode) is supported on reversed nodesets");
+
+ if (isEmpty()) {
+ return append(aNode);
+ }
+
+ bool dupe;
+ txXPathNode* pos = findPosition(aNode, mStart, mEnd, dupe);
+
+ if (dupe) {
+ return NS_OK;
+ }
+
+ // save pos, ensureGrowSize messes with the pointers
+ int32_t moveSize = mEnd - pos;
+ int32_t offset = pos - mStart;
+ if (!ensureGrowSize(1)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ // set pos to where it was
+ pos = mStart + offset;
+
+ if (moveSize > 0) {
+ LOG_CHUNK_MOVE(pos, pos + 1, moveSize);
+ memmove(pos + 1, pos, moveSize * sizeof(txXPathNode));
+ }
+
+ new(pos) txXPathNode(aNode);
+ ++mEnd;
+
+ return NS_OK;
+}
+
+nsresult txNodeSet::add(const txNodeSet& aNodes)
+{
+ return add(aNodes, copyElements, nullptr);
+}
+
+nsresult txNodeSet::addAndTransfer(txNodeSet* aNodes)
+{
+ // failure is out-of-memory, transfer didn't happen
+ nsresult rv = add(*aNodes, transferElements, destroyElements);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+#ifdef TX_DONT_RECYCLE_BUFFER
+ if (aNodes->mStartBuffer) {
+ free(aNodes->mStartBuffer);
+ aNodes->mStartBuffer = aNodes->mEndBuffer = nullptr;
+ }
+#endif
+ aNodes->mStart = aNodes->mEnd = aNodes->mStartBuffer;
+
+ return NS_OK;
+}
+
+/**
+ * add(aNodeSet, aTransferOp)
+ *
+ * The code is optimized to make a minimum number of calls to
+ * Node::compareDocumentPosition. The idea is this:
+ * We have the two nodesets (number indicate "document position")
+ *
+ * 1 3 7 <- source 1
+ * 2 3 6 8 9 <- source 2
+ * _ _ _ _ _ _ _ _ <- result
+ *
+ *
+ * When merging these nodesets into the result, the nodes are transfered
+ * in chunks to the end of the buffer so that each chunk does not contain
+ * a node from the other nodeset, in document order.
+ *
+ * We select the last non-transfered node in the first nodeset and find
+ * where in the other nodeset it would be inserted. In this case we would
+ * take the 7 from the first nodeset and find the position between the
+ * 6 and 8 in the second. We then take the nodes after the insert-position
+ * and transfer them to the end of the resulting nodeset. Which in this case
+ * means that we first transfered the 8 and 9 nodes, giving us the following:
+ *
+ * 1 3 7 <- source 1
+ * 2 3 6 <- source 2
+ * _ _ _ _ _ _ 8 9 <- result
+ *
+ * The corresponding procedure is done for the second nodeset, that is
+ * the insertion position of the 6 in the first nodeset is found, which
+ * is between the 3 and the 7. The 7 is memmoved (as it stays within
+ * the same nodeset) to the result buffer.
+ *
+ * As the result buffer is filled from the end, it is safe to share the
+ * buffer between this nodeset and the result.
+ *
+ * This is repeated until both of the nodesets are empty.
+ *
+ * If we find a duplicate node when searching for where insertposition we
+ * check for sequences of duplicate nodes, which can be optimized.
+ *
+ */
+nsresult txNodeSet::add(const txNodeSet& aNodes, transferOp aTransfer,
+ destroyOp aDestroy)
+{
+ NS_ASSERTION(mDirection == kForward,
+ "only append(aNode) is supported on reversed nodesets");
+
+ if (aNodes.isEmpty()) {
+ return NS_OK;
+ }
+
+ if (!ensureGrowSize(aNodes.size())) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // This is probably a rather common case, so lets try to shortcut.
+ if (mStart == mEnd ||
+ txXPathNodeUtils::comparePosition(mEnd[-1], *aNodes.mStart) < 0) {
+ aTransfer(mEnd, aNodes.mStart, aNodes.mEnd);
+ mEnd += aNodes.size();
+
+ return NS_OK;
+ }
+
+ // Last element in this nodeset
+ txXPathNode* thisPos = mEnd;
+
+ // Last element of the other nodeset
+ txXPathNode* otherPos = aNodes.mEnd;
+
+ // Pointer to the insertion point in this nodeset
+ txXPathNode* insertPos = mEndBuffer;
+
+ bool dupe;
+ txXPathNode* pos;
+ int32_t count;
+ while (thisPos > mStart || otherPos > aNodes.mStart) {
+ // Find where the last remaining node of this nodeset would
+ // be inserted in the other nodeset.
+ if (thisPos > mStart) {
+ pos = findPosition(thisPos[-1], aNodes.mStart, otherPos, dupe);
+
+ if (dupe) {
+ const txXPathNode *deletePos = thisPos;
+ --thisPos; // this is already added
+ // check dupe sequence
+ while (thisPos > mStart && pos > aNodes.mStart &&
+ thisPos[-1] == pos[-1]) {
+ --thisPos;
+ --pos;
+ }
+
+ if (aDestroy) {
+ aDestroy(thisPos, deletePos);
+ }
+ }
+ }
+ else {
+ pos = aNodes.mStart;
+ }
+
+ // Transfer the otherNodes after the insertion point to the result
+ count = otherPos - pos;
+ if (count > 0) {
+ insertPos -= count;
+ aTransfer(insertPos, pos, otherPos);
+ otherPos -= count;
+ }
+
+ // Find where the last remaining node of the otherNodeset would
+ // be inserted in this nodeset.
+ if (otherPos > aNodes.mStart) {
+ pos = findPosition(otherPos[-1], mStart, thisPos, dupe);
+
+ if (dupe) {
+ const txXPathNode *deletePos = otherPos;
+ --otherPos; // this is already added
+ // check dupe sequence
+ while (otherPos > aNodes.mStart && pos > mStart &&
+ otherPos[-1] == pos[-1]) {
+ --otherPos;
+ --pos;
+ }
+
+ if (aDestroy) {
+ aDestroy(otherPos, deletePos);
+ }
+ }
+ }
+ else {
+ pos = mStart;
+ }
+
+ // Move the nodes from this nodeset after the insertion point
+ // to the result
+ count = thisPos - pos;
+ if (count > 0) {
+ insertPos -= count;
+ LOG_CHUNK_MOVE(pos, insertPos, count);
+ memmove(insertPos, pos, count * sizeof(txXPathNode));
+ thisPos -= count;
+ }
+ }
+ mStart = insertPos;
+ mEnd = mEndBuffer;
+
+ return NS_OK;
+}
+
+/**
+ * Append API
+ * These functions should be used with care.
+ * They are intended to be used when the caller assures that the resulting
+ * nodeset remains in document order.
+ * Abuse will break document order, and cause errors in the result.
+ * These functions are significantly faster than the add API, as no
+ * order info operations will be performed.
+ */
+
+nsresult
+txNodeSet::append(const txXPathNode& aNode)
+{
+ if (!ensureGrowSize(1)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (mDirection == kForward) {
+ new(mEnd) txXPathNode(aNode);
+ ++mEnd;
+
+ return NS_OK;
+ }
+
+ new(--mStart) txXPathNode(aNode);
+
+ return NS_OK;
+}
+
+nsresult
+txNodeSet::append(const txNodeSet& aNodes)
+{
+ NS_ASSERTION(mDirection == kForward,
+ "only append(aNode) is supported on reversed nodesets");
+
+ if (aNodes.isEmpty()) {
+ return NS_OK;
+ }
+
+ int32_t appended = aNodes.size();
+ if (!ensureGrowSize(appended)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ copyElements(mEnd, aNodes.mStart, aNodes.mEnd);
+ mEnd += appended;
+
+ return NS_OK;
+}
+
+nsresult
+txNodeSet::mark(int32_t aIndex)
+{
+ NS_ASSERTION(aIndex >= 0 && mStart && mEnd - mStart > aIndex,
+ "index out of bounds");
+ if (!mMarks) {
+ int32_t length = size();
+ mMarks = new bool[length];
+ memset(mMarks, 0, length * sizeof(bool));
+ }
+ if (mDirection == kForward) {
+ mMarks[aIndex] = true;
+ }
+ else {
+ mMarks[size() - aIndex - 1] = true;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+txNodeSet::sweep()
+{
+ if (!mMarks) {
+ // sweep everything
+ clear();
+ }
+
+ int32_t chunk, pos = 0;
+ int32_t length = size();
+ txXPathNode* insertion = mStartBuffer;
+
+ while (pos < length) {
+ while (pos < length && !mMarks[pos]) {
+ // delete unmarked
+ mStart[pos].~txXPathNode();
+ ++pos;
+ }
+ // find chunk to move
+ chunk = 0;
+ while (pos < length && mMarks[pos]) {
+ ++pos;
+ ++chunk;
+ }
+ // move chunk
+ if (chunk > 0) {
+ LOG_CHUNK_MOVE(mStart + pos - chunk, insertion, chunk);
+ memmove(insertion, mStart + pos - chunk,
+ chunk * sizeof(txXPathNode));
+ insertion += chunk;
+ }
+ }
+ mStart = mStartBuffer;
+ mEnd = insertion;
+ delete [] mMarks;
+ mMarks = nullptr;
+
+ return NS_OK;
+}
+
+void
+txNodeSet::clear()
+{
+ destroyElements(mStart, mEnd);
+#ifdef TX_DONT_RECYCLE_BUFFER
+ if (mStartBuffer) {
+ free(mStartBuffer);
+ mStartBuffer = mEndBuffer = nullptr;
+ }
+#endif
+ mStart = mEnd = mStartBuffer;
+ delete [] mMarks;
+ mMarks = nullptr;
+ mDirection = kForward;
+}
+
+int32_t
+txNodeSet::indexOf(const txXPathNode& aNode, uint32_t aStart) const
+{
+ NS_ASSERTION(mDirection == kForward,
+ "only append(aNode) is supported on reversed nodesets");
+
+ if (!mStart || mStart == mEnd) {
+ return -1;
+ }
+
+ txXPathNode* pos = mStart + aStart;
+ for (; pos < mEnd; ++pos) {
+ if (*pos == aNode) {
+ return pos - mStart;
+ }
+ }
+
+ return -1;
+}
+
+const txXPathNode&
+txNodeSet::get(int32_t aIndex) const
+{
+ if (mDirection == kForward) {
+ return mStart[aIndex];
+ }
+
+ return mEnd[-aIndex - 1];
+}
+
+short
+txNodeSet::getResultType()
+{
+ return txAExprResult::NODESET;
+}
+
+bool
+txNodeSet::booleanValue()
+{
+ return !isEmpty();
+}
+double
+txNodeSet::numberValue()
+{
+ nsAutoString str;
+ stringValue(str);
+
+ return txDouble::toDouble(str);
+}
+
+void
+txNodeSet::stringValue(nsString& aStr)
+{
+ NS_ASSERTION(mDirection == kForward,
+ "only append(aNode) is supported on reversed nodesets");
+ if (isEmpty()) {
+ return;
+ }
+ txXPathNodeUtils::appendNodeValue(get(0), aStr);
+}
+
+const nsString*
+txNodeSet::stringValuePointer()
+{
+ return nullptr;
+}
+
+bool txNodeSet::ensureGrowSize(int32_t aSize)
+{
+ // check if there is enough place in the buffer as is
+ if (mDirection == kForward && aSize <= mEndBuffer - mEnd) {
+ return true;
+ }
+
+ if (mDirection == kReversed && aSize <= mStart - mStartBuffer) {
+ return true;
+ }
+
+ // check if we just have to align mStart to have enough space
+ int32_t oldSize = mEnd - mStart;
+ int32_t oldLength = mEndBuffer - mStartBuffer;
+ int32_t ensureSize = oldSize + aSize;
+ if (ensureSize <= oldLength) {
+ // just move the buffer
+ txXPathNode* dest = mStartBuffer;
+ if (mDirection == kReversed) {
+ dest = mEndBuffer - oldSize;
+ }
+ LOG_CHUNK_MOVE(mStart, dest, oldSize);
+ memmove(dest, mStart, oldSize * sizeof(txXPathNode));
+ mStart = dest;
+ mEnd = dest + oldSize;
+
+ return true;
+ }
+
+ // This isn't 100% safe. But until someone manages to make a 1gig nodeset
+ // it should be ok.
+ int32_t newLength = std::max(oldLength, kTxNodeSetMinSize);
+
+ while (newLength < ensureSize) {
+ newLength *= kTxNodeSetGrowFactor;
+ }
+
+ txXPathNode* newArr = static_cast<txXPathNode*>
+ (moz_xmalloc(newLength *
+ sizeof(txXPathNode)));
+ if (!newArr) {
+ return false;
+ }
+
+ txXPathNode* dest = newArr;
+ if (mDirection == kReversed) {
+ dest += newLength - oldSize;
+ }
+
+ if (oldSize > 0) {
+ LOG_CHUNK_MOVE(mStart, dest, oldSize);
+ memcpy(dest, mStart, oldSize * sizeof(txXPathNode));
+ }
+
+ if (mStartBuffer) {
+#ifdef DEBUG
+ memset(mStartBuffer, 0,
+ (mEndBuffer - mStartBuffer) * sizeof(txXPathNode));
+#endif
+ free(mStartBuffer);
+ }
+
+ mStartBuffer = newArr;
+ mEndBuffer = mStartBuffer + newLength;
+ mStart = dest;
+ mEnd = dest + oldSize;
+
+ return true;
+}
+
+txXPathNode*
+txNodeSet::findPosition(const txXPathNode& aNode, txXPathNode* aFirst,
+ txXPathNode* aLast, bool& aDupe) const
+{
+ aDupe = false;
+ if (aLast - aFirst <= 2) {
+ // If we search 2 nodes or less there is no point in further divides
+ txXPathNode* pos = aFirst;
+ for (; pos < aLast; ++pos) {
+ int cmp = txXPathNodeUtils::comparePosition(aNode, *pos);
+ if (cmp < 0) {
+ return pos;
+ }
+
+ if (cmp == 0) {
+ aDupe = true;
+
+ return pos;
+ }
+ }
+ return pos;
+ }
+
+ // (cannot add two pointers)
+ txXPathNode* midpos = aFirst + (aLast - aFirst) / 2;
+ int cmp = txXPathNodeUtils::comparePosition(aNode, *midpos);
+ if (cmp == 0) {
+ aDupe = true;
+
+ return midpos;
+ }
+
+ if (cmp > 0) {
+ return findPosition(aNode, midpos + 1, aLast, aDupe);
+ }
+
+ // midpos excluded as end of range
+
+ return findPosition(aNode, aFirst, midpos, aDupe);
+}
+
+/* static */
+void
+txNodeSet::copyElements(txXPathNode* aDest,
+ const txXPathNode* aStart, const txXPathNode* aEnd)
+{
+ const txXPathNode* pos = aStart;
+ while (pos < aEnd) {
+ new(aDest) txXPathNode(*pos);
+ ++aDest;
+ ++pos;
+ }
+}
+
+/* static */
+void
+txNodeSet::transferElements(txXPathNode* aDest,
+ const txXPathNode* aStart, const txXPathNode* aEnd)
+{
+ LOG_CHUNK_MOVE(aStart, aDest, (aEnd - aStart));
+ memcpy(aDest, aStart, (aEnd - aStart) * sizeof(txXPathNode));
+}
diff --git a/dom/xslt/xpath/txNodeSet.h b/dom/xslt/xpath/txNodeSet.h
new file mode 100644
index 000000000..bd33be628
--- /dev/null
+++ b/dom/xslt/xpath/txNodeSet.h
@@ -0,0 +1,217 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/**
+ * Implementation of an XPath NodeSet
+ */
+
+#ifndef txNodeSet_h__
+#define txNodeSet_h__
+
+#include "txExprResult.h"
+#include "nsError.h"
+#include "txXPathNode.h"
+
+class txNodeSet : public txAExprResult
+{
+public:
+ /**
+ * Creates a new empty NodeSet
+ */
+ explicit txNodeSet(txResultRecycler* aRecycler);
+
+ /**
+ * Creates a new NodeSet with one node.
+ */
+ txNodeSet(const txXPathNode& aNode, txResultRecycler* aRecycler);
+
+ /**
+ * Creates a new txNodeSet, copying the node references from the source
+ * NodeSet.
+ */
+ txNodeSet(const txNodeSet& aSource, txResultRecycler* aRecycler);
+
+ /**
+ * Destructor for txNodeSet, deletes the nodes.
+ */
+ virtual ~txNodeSet();
+
+ /**
+ * Adds the specified txXPathNode to this NodeSet if it is not already
+ * in this NodeSet. The node is inserted according to document order.
+ *
+ * @param aNode the txXPathNode to add to the NodeSet
+ * @return errorcode.
+ */
+ nsresult add(const txXPathNode& aNode);
+
+ /**
+ * Adds the nodes in specified NodeSet to this NodeSet. The resulting
+ * NodeSet is sorted in document order and does not contain any duplicate
+ * nodes.
+ *
+ * @param aNodes the NodeSet to add, must be in document order.
+ * @return errorcode.
+ */
+ nsresult add(const txNodeSet& aNodes);
+ nsresult addAndTransfer(txNodeSet* aNodes);
+
+ /**
+ * Append API
+ * These functions should be used with care.
+ * They are intended to be used when the caller assures that the resulting
+ * NodeSet remains in document order.
+ * Abuse will break document order, and cause errors in the result.
+ * These functions are significantly faster than the add API, as no
+ * order info operations will be performed.
+ */
+
+ /**
+ * Appends the specified Node to the end of this NodeSet
+ * @param aNode the Node to append to the NodeSet
+ * @return errorcode.
+ */
+ nsresult append(const txXPathNode& aNode);
+
+ /**
+ * Appends the nodes in the specified NodeSet to the end of this NodeSet
+ * @param aNodes the NodeSet to append to the NodeSet
+ * @return errorcode.
+ */
+ nsresult append(const txNodeSet& aNodes);
+
+ /**
+ * API to implement reverse axes in LocationStep.
+ *
+ * Before adding nodes to the nodeset for a reversed axis, call
+ * setReverse(). This will make the append(aNode) and get() methods treat
+ * the nodeset as required. Do only call append(aNode), get(), mark()
+ * and sweep() while the nodeset is reversed.
+ * Afterwards, call unsetReverse(). The nodes are stored in document
+ * order internally.
+ */
+ void setReverse()
+ {
+ mDirection = -1;
+ }
+ void unsetReverse()
+ {
+ mDirection = 1;
+ }
+
+ /**
+ * API to implement predicates in PredicateExpr
+ *
+ * mark(aIndex) marks the specified member of the nodeset.
+ * sweep() clears all members of the nodeset that haven't been
+ * marked before and clear the mMarks array.
+ */
+ nsresult mark(int32_t aIndex);
+ nsresult sweep();
+
+ /**
+ * Removes all nodes from this nodeset
+ */
+ void clear();
+
+ /**
+ * Returns the index of the specified Node,
+ * or -1 if the Node is not contained in the NodeSet
+ * @param aNode the Node to get the index for
+ * @param aStart index to start searching at
+ * @return index of specified node or -1 if the node does not exist
+ */
+ int32_t indexOf(const txXPathNode& aNode, uint32_t aStart = 0) const;
+
+ /**
+ * Returns true if the specified Node is contained in the set.
+ * @param aNode the Node to search for
+ * @return true if specified Node is contained in the NodeSet
+ */
+ bool contains(const txXPathNode& aNode) const
+ {
+ return indexOf(aNode) >= 0;
+ }
+
+ /**
+ * Returns the Node at the specified node in this NodeSet.
+ * @param aIndex the node of the Node to return
+ * @return Node at specified node
+ */
+ const txXPathNode& get(int32_t aIndex) const;
+
+ /**
+ * Returns true if there are no Nodes in the NodeSet.
+ * @return true if there are no Nodes in the NodeSet.
+ */
+ bool isEmpty() const
+ {
+ return mStart ? mStart == mEnd : true;
+ }
+
+ /**
+ * Returns the number of elements in the NodeSet
+ * @return the number of elements in the NodeSet
+ */
+ int32_t size() const
+ {
+ return mStart ? mEnd - mStart : 0;
+ }
+
+ TX_DECL_EXPRRESULT
+
+private:
+ /**
+ * Ensure that this nodeset can take another aSize nodes.
+ *
+ * Changes mStart and mEnd as well as mBufferStart and mBufferEnd.
+ */
+ bool ensureGrowSize(int32_t aSize);
+
+ /**
+ * Finds position in the buffer where a node should be inserted
+ * to keep the nodeset in document order. Searches the positions
+ * aFirst-aLast, including aFirst, but not aLast.
+ * @param aNode Node to find insert position for.
+ * @param aFirst First item of the search range, included.
+ * @param aLast Last item of the search range, excluded.
+ * @param aDupe out-param. Will be set to true if the node already
+ * exists in the NodeSet, false if it should be
+ * inserted.
+ * @return pointer where to insert the node. The node should be inserted
+ * before the given node. This value is always set, even if aNode
+ * already exists in the NodeSet
+ */
+ txXPathNode* findPosition(const txXPathNode& aNode,
+ txXPathNode* aFirst,
+ txXPathNode* aLast, bool& aDupe) const;
+
+ static void copyElements(txXPathNode* aDest, const txXPathNode* aStart,
+ const txXPathNode* aEnd);
+ static void transferElements(txXPathNode* aDest, const txXPathNode* aStart,
+ const txXPathNode* aEnd);
+ static void destroyElements(const txXPathNode* aStart,
+ const txXPathNode* aEnd)
+ {
+ while (aStart < aEnd) {
+ aStart->~txXPathNode();
+ ++aStart;
+ }
+ }
+
+ typedef void (*transferOp) (txXPathNode* aDest, const txXPathNode* aStart,
+ const txXPathNode* aEnd);
+ typedef void (*destroyOp) (const txXPathNode* aStart,
+ const txXPathNode* aEnd);
+ nsresult add(const txNodeSet& aNodes, transferOp aTransfer,
+ destroyOp aDestroy);
+
+ txXPathNode *mStart, *mEnd, *mStartBuffer, *mEndBuffer;
+ int32_t mDirection;
+ // used for mark() and sweep() in predicates
+ bool* mMarks;
+};
+
+#endif
diff --git a/dom/xslt/xpath/txNodeSetAdaptor.cpp b/dom/xslt/xpath/txNodeSetAdaptor.cpp
new file mode 100644
index 000000000..2656c8dde
--- /dev/null
+++ b/dom/xslt/xpath/txNodeSetAdaptor.cpp
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "txNodeSetAdaptor.h"
+#include "txXPathTreeWalker.h"
+
+txNodeSetAdaptor::txNodeSetAdaptor()
+ : txXPathObjectAdaptor(),
+ mWritable(true)
+{
+}
+
+txNodeSetAdaptor::txNodeSetAdaptor(txNodeSet *aNodeSet)
+ : txXPathObjectAdaptor(aNodeSet),
+ mWritable(false)
+{
+}
+
+NS_IMPL_ISUPPORTS_INHERITED(txNodeSetAdaptor, txXPathObjectAdaptor, txINodeSet)
+
+nsresult
+txNodeSetAdaptor::Init()
+{
+ if (!mValue) {
+ mValue = new txNodeSet(nullptr);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+txNodeSetAdaptor::Item(uint32_t aIndex, nsIDOMNode **aResult)
+{
+ *aResult = nullptr;
+
+ if (aIndex > (uint32_t)NodeSet()->size()) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ return txXPathNativeNode::getNode(NodeSet()->get(aIndex), aResult);
+}
+
+NS_IMETHODIMP
+txNodeSetAdaptor::ItemAsNumber(uint32_t aIndex, double *aResult)
+{
+ if (aIndex > (uint32_t)NodeSet()->size()) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ nsAutoString result;
+ txXPathNodeUtils::appendNodeValue(NodeSet()->get(aIndex), result);
+
+ *aResult = txDouble::toDouble(result);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+txNodeSetAdaptor::ItemAsString(uint32_t aIndex, nsAString &aResult)
+{
+ if (aIndex > (uint32_t)NodeSet()->size()) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ txXPathNodeUtils::appendNodeValue(NodeSet()->get(aIndex), aResult);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+txNodeSetAdaptor::GetLength(uint32_t *aLength)
+{
+ *aLength = (uint32_t)NodeSet()->size();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+txNodeSetAdaptor::Add(nsIDOMNode *aNode)
+{
+ NS_ENSURE_TRUE(mWritable, NS_ERROR_FAILURE);
+
+ nsAutoPtr<txXPathNode> node(txXPathNativeNode::createXPathNode(aNode,
+ true));
+
+ return node ? NodeSet()->add(*node) : NS_ERROR_OUT_OF_MEMORY;
+}
diff --git a/dom/xslt/xpath/txNodeSetAdaptor.h b/dom/xslt/xpath/txNodeSetAdaptor.h
new file mode 100644
index 000000000..4648dca5c
--- /dev/null
+++ b/dom/xslt/xpath/txNodeSetAdaptor.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+#ifndef txNodeSetAdaptor_h__
+#define txNodeSetAdaptor_h__
+
+#include "txINodeSet.h"
+#include "txNodeSet.h"
+#include "txXPathObjectAdaptor.h"
+
+/**
+ * Implements an XPCOM wrapper around an XPath NodeSet.
+ */
+
+class txNodeSetAdaptor : public txXPathObjectAdaptor,
+ public txINodeSet
+{
+public:
+ txNodeSetAdaptor();
+ explicit txNodeSetAdaptor(txNodeSet* aNodeSet);
+
+ nsresult Init();
+
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_TXINODESET
+
+protected:
+ ~txNodeSetAdaptor() {}
+
+private:
+ txNodeSet* NodeSet()
+ {
+ return static_cast<txNodeSet*>(mValue.get());
+ }
+
+ bool mWritable;
+};
+
+#endif // txNodeSetAdaptor_h__
diff --git a/dom/xslt/xpath/txNodeSetContext.cpp b/dom/xslt/xpath/txNodeSetContext.cpp
new file mode 100644
index 000000000..7d98391fd
--- /dev/null
+++ b/dom/xslt/xpath/txNodeSetContext.cpp
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "txNodeSetContext.h"
+#include "txNodeSet.h"
+
+const txXPathNode& txNodeSetContext::getContextNode()
+{
+ return mContextSet->get(mPosition - 1);
+}
+
+uint32_t txNodeSetContext::size()
+{
+ return (uint32_t)mContextSet->size();
+}
+
+uint32_t txNodeSetContext::position()
+{
+ NS_ASSERTION(mPosition, "Should have called next() at least once");
+ return mPosition;
+}
+
+nsresult txNodeSetContext::getVariable(int32_t aNamespace, nsIAtom* aLName,
+ txAExprResult*& aResult)
+{
+ NS_ASSERTION(mInner, "mInner is null!!!");
+ return mInner->getVariable(aNamespace, aLName, aResult);
+}
+
+bool txNodeSetContext::isStripSpaceAllowed(const txXPathNode& aNode)
+{
+ NS_ASSERTION(mInner, "mInner is null!!!");
+ return mInner->isStripSpaceAllowed(aNode);
+}
+
+void* txNodeSetContext::getPrivateContext()
+{
+ NS_ASSERTION(mInner, "mInner is null!!!");
+ return mInner->getPrivateContext();
+}
+
+txResultRecycler* txNodeSetContext::recycler()
+{
+ NS_ASSERTION(mInner, "mInner is null!!!");
+ return mInner->recycler();
+}
+
+void txNodeSetContext::receiveError(const nsAString& aMsg, nsresult aRes)
+{
+ NS_ASSERTION(mInner, "mInner is null!!!");
+#ifdef DEBUG
+ nsAutoString error(NS_LITERAL_STRING("forwarded error: "));
+ error.Append(aMsg);
+ mInner->receiveError(error, aRes);
+#else
+ mInner->receiveError(aMsg, aRes);
+#endif
+}
diff --git a/dom/xslt/xpath/txNodeSetContext.h b/dom/xslt/xpath/txNodeSetContext.h
new file mode 100644
index 000000000..2c133cf33
--- /dev/null
+++ b/dom/xslt/xpath/txNodeSetContext.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+#ifndef __TX_XPATH_SET_CONTEXT
+#define __TX_XPATH_SET_CONTEXT
+
+#include "txIXPathContext.h"
+#include "txNodeSet.h"
+#include "nsAutoPtr.h"
+
+class txNodeSetContext : public txIEvalContext
+{
+public:
+ txNodeSetContext(txNodeSet* aContextNodeSet, txIMatchContext* aContext)
+ : mContextSet(aContextNodeSet), mPosition(0), mInner(aContext)
+ {
+ }
+
+ // Iteration over the given NodeSet
+ bool hasNext()
+ {
+ return mPosition < size();
+ }
+ void next()
+ {
+ NS_ASSERTION(mPosition < size(), "Out of bounds.");
+ mPosition++;
+ }
+ void setPosition(uint32_t aPosition)
+ {
+ NS_ASSERTION(aPosition > 0 &&
+ aPosition <= size(), "Out of bounds.");
+ mPosition = aPosition;
+ }
+
+ TX_DECL_EVAL_CONTEXT;
+
+protected:
+ RefPtr<txNodeSet> mContextSet;
+ uint32_t mPosition;
+ txIMatchContext* mInner;
+};
+
+#endif // __TX_XPATH_SET_CONTEXT
diff --git a/dom/xslt/xpath/txNodeTypeTest.cpp b/dom/xslt/xpath/txNodeTypeTest.cpp
new file mode 100644
index 000000000..650a1ae5f
--- /dev/null
+++ b/dom/xslt/xpath/txNodeTypeTest.cpp
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "txExpr.h"
+#include "nsIAtom.h"
+#include "txIXPathContext.h"
+#include "txXPathTreeWalker.h"
+
+bool txNodeTypeTest::matches(const txXPathNode& aNode,
+ txIMatchContext* aContext)
+{
+ switch (mNodeType) {
+ case COMMENT_TYPE:
+ {
+ return txXPathNodeUtils::isComment(aNode);
+ }
+ case TEXT_TYPE:
+ {
+ return txXPathNodeUtils::isText(aNode) &&
+ !aContext->isStripSpaceAllowed(aNode);
+ }
+ case PI_TYPE:
+ {
+ return txXPathNodeUtils::isProcessingInstruction(aNode) &&
+ (!mNodeName ||
+ txXPathNodeUtils::localNameEquals(aNode, mNodeName));
+ }
+ case NODE_TYPE:
+ {
+ return !txXPathNodeUtils::isText(aNode) ||
+ !aContext->isStripSpaceAllowed(aNode);
+ }
+ }
+ return true;
+}
+
+txNodeTest::NodeTestType
+txNodeTypeTest::getType()
+{
+ return NODETYPE_TEST;
+}
+
+/*
+ * Returns the default priority of this txNodeTest
+ */
+double txNodeTypeTest::getDefaultPriority()
+{
+ return mNodeName ? 0 : -0.5;
+}
+
+bool
+txNodeTypeTest::isSensitiveTo(Expr::ContextSensitivity aContext)
+{
+ return !!(aContext & Expr::NODE_CONTEXT);
+}
+
+#ifdef TX_TO_STRING
+void
+txNodeTypeTest::toString(nsAString& aDest)
+{
+ switch (mNodeType) {
+ case COMMENT_TYPE:
+ aDest.AppendLiteral("comment()");
+ break;
+ case TEXT_TYPE:
+ aDest.AppendLiteral("text()");
+ break;
+ case PI_TYPE:
+ aDest.AppendLiteral("processing-instruction(");
+ if (mNodeName) {
+ nsAutoString str;
+ mNodeName->ToString(str);
+ aDest.Append(char16_t('\''));
+ aDest.Append(str);
+ aDest.Append(char16_t('\''));
+ }
+ aDest.Append(char16_t(')'));
+ break;
+ case NODE_TYPE:
+ aDest.AppendLiteral("node()");
+ break;
+ }
+}
+#endif
diff --git a/dom/xslt/xpath/txNumberExpr.cpp b/dom/xslt/xpath/txNumberExpr.cpp
new file mode 100644
index 000000000..1defe905b
--- /dev/null
+++ b/dom/xslt/xpath/txNumberExpr.cpp
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "mozilla/FloatingPoint.h"
+
+#include "txExpr.h"
+#include <math.h>
+#include "txIXPathContext.h"
+
+nsresult
+txNumberExpr::evaluate(txIEvalContext* aContext, txAExprResult** aResult)
+{
+ *aResult = nullptr;
+
+ RefPtr<txAExprResult> exprRes;
+ nsresult rv = mRightExpr->evaluate(aContext, getter_AddRefs(exprRes));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ double rightDbl = exprRes->numberValue();
+
+ rv = mLeftExpr->evaluate(aContext, getter_AddRefs(exprRes));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ double leftDbl = exprRes->numberValue();
+ double result = 0;
+
+ switch (mOp) {
+ case ADD:
+ result = leftDbl + rightDbl;
+ break;
+
+ case SUBTRACT:
+ result = leftDbl - rightDbl;
+ break;
+
+ case DIVIDE:
+ if (rightDbl == 0) {
+#if defined(XP_WIN)
+ /* XXX MSVC miscompiles such that (NaN == 0) */
+ if (mozilla::IsNaN(rightDbl))
+ result = mozilla::UnspecifiedNaN<double>();
+ else
+#endif
+ if (leftDbl == 0 || mozilla::IsNaN(leftDbl))
+ result = mozilla::UnspecifiedNaN<double>();
+ else if (mozilla::IsNegative(leftDbl) != mozilla::IsNegative(rightDbl))
+ result = mozilla::NegativeInfinity<double>();
+ else
+ result = mozilla::PositiveInfinity<double>();
+ }
+ else
+ result = leftDbl / rightDbl;
+ break;
+
+ case MODULUS:
+ if (rightDbl == 0) {
+ result = mozilla::UnspecifiedNaN<double>();
+ }
+ else {
+#if defined(XP_WIN)
+ /* Workaround MS fmod bug where 42 % (1/0) => NaN, not 42. */
+ if (!mozilla::IsInfinite(leftDbl) && mozilla::IsInfinite(rightDbl))
+ result = leftDbl;
+ else
+#endif
+ result = fmod(leftDbl, rightDbl);
+ }
+ break;
+
+ case MULTIPLY:
+ result = leftDbl * rightDbl;
+ break;
+ }
+
+ return aContext->recycler()->getNumberResult(result, aResult);
+} //-- evaluate
+
+TX_IMPL_EXPR_STUBS_2(txNumberExpr, NUMBER_RESULT, mLeftExpr, mRightExpr)
+
+bool
+txNumberExpr::isSensitiveTo(ContextSensitivity aContext)
+{
+ return mLeftExpr->isSensitiveTo(aContext) ||
+ mRightExpr->isSensitiveTo(aContext);
+}
+
+#ifdef TX_TO_STRING
+void
+txNumberExpr::toString(nsAString& str)
+{
+ mLeftExpr->toString(str);
+
+ switch (mOp) {
+ case ADD:
+ str.AppendLiteral(" + ");
+ break;
+ case SUBTRACT:
+ str.AppendLiteral(" - ");
+ break;
+ case DIVIDE:
+ str.AppendLiteral(" div ");
+ break;
+ case MODULUS:
+ str.AppendLiteral(" mod ");
+ break;
+ case MULTIPLY:
+ str.AppendLiteral(" * ");
+ break;
+ }
+
+ mRightExpr->toString(str);
+
+}
+#endif
diff --git a/dom/xslt/xpath/txNumberResult.cpp b/dom/xslt/xpath/txNumberResult.cpp
new file mode 100644
index 000000000..0bcbecdc5
--- /dev/null
+++ b/dom/xslt/xpath/txNumberResult.cpp
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/**
+ * NumberResult
+ * Represents the a number as the result of evaluating an Expr
+**/
+
+#include "mozilla/FloatingPoint.h"
+
+#include "txExprResult.h"
+
+/**
+ * Default Constructor
+**/
+
+/**
+ * Creates a new NumberResult with the value of the given double parameter
+ * @param dbl the double to use for initialization of this NumberResult's value
+**/
+NumberResult::NumberResult(double aValue, txResultRecycler* aRecycler)
+ : txAExprResult(aRecycler), value(aValue)
+{
+} //-- NumberResult
+
+/*
+ * Virtual Methods from ExprResult
+*/
+
+short NumberResult::getResultType() {
+ return txAExprResult::NUMBER;
+} //-- getResultType
+
+void
+NumberResult::stringValue(nsString& aResult)
+{
+ txDouble::toString(value, aResult);
+}
+
+const nsString*
+NumberResult::stringValuePointer()
+{
+ return nullptr;
+}
+
+bool NumberResult::booleanValue() {
+ // OG+
+ // As per the XPath spec, the boolean value of a number is true if and only if
+ // it is neither positive 0 nor negative 0 nor NaN
+ return (bool)(value != 0.0 && !mozilla::IsNaN(value));
+ // OG-
+} //-- booleanValue
+
+double NumberResult::numberValue() {
+ return this->value;
+} //-- numberValue
+
diff --git a/dom/xslt/xpath/txPathExpr.cpp b/dom/xslt/xpath/txPathExpr.cpp
new file mode 100644
index 000000000..bcff47e9f
--- /dev/null
+++ b/dom/xslt/xpath/txPathExpr.cpp
@@ -0,0 +1,253 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "txExpr.h"
+#include "txNodeSet.h"
+#include "txNodeSetContext.h"
+#include "txSingleNodeContext.h"
+#include "txXMLUtils.h"
+#include "txXPathTreeWalker.h"
+
+ //------------/
+ //- PathExpr -/
+//------------/
+
+/**
+ * Adds the Expr to this PathExpr
+ * @param expr the Expr to add to this PathExpr
+**/
+nsresult
+PathExpr::addExpr(Expr* aExpr, PathOperator aPathOp)
+{
+ NS_ASSERTION(!mItems.IsEmpty() || aPathOp == RELATIVE_OP,
+ "First step has to be relative in PathExpr");
+ PathExprItem* pxi = mItems.AppendElement();
+ if (!pxi) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ pxi->expr = aExpr;
+ pxi->pathOp = aPathOp;
+
+ return NS_OK;
+}
+
+ //-----------------------------/
+ //- Virtual methods from Expr -/
+//-----------------------------/
+
+/**
+ * Evaluates this Expr based on the given context node and processor state
+ * @param context the context node for evaluation of this Expr
+ * @param ps the ContextState containing the stack information needed
+ * for evaluation
+ * @return the result of the evaluation
+**/
+nsresult
+PathExpr::evaluate(txIEvalContext* aContext, txAExprResult** aResult)
+{
+ *aResult = nullptr;
+
+ // We need to evaluate the first step with the current context since it
+ // can depend on the context size and position. For example:
+ // key('books', concat('book', position()))
+ RefPtr<txAExprResult> res;
+ nsresult rv = mItems[0].expr->evaluate(aContext, getter_AddRefs(res));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_ENSURE_TRUE(res->getResultType() == txAExprResult::NODESET,
+ NS_ERROR_XSLT_NODESET_EXPECTED);
+
+ RefPtr<txNodeSet> nodes = static_cast<txNodeSet*>
+ (static_cast<txAExprResult*>
+ (res));
+ if (nodes->isEmpty()) {
+ res.forget(aResult);
+
+ return NS_OK;
+ }
+ res = nullptr; // To allow recycling
+
+ // Evaluate remaining steps
+ uint32_t i, len = mItems.Length();
+ for (i = 1; i < len; ++i) {
+ PathExprItem& pxi = mItems[i];
+ RefPtr<txNodeSet> tmpNodes;
+ txNodeSetContext eContext(nodes, aContext);
+ while (eContext.hasNext()) {
+ eContext.next();
+
+ RefPtr<txNodeSet> resNodes;
+ if (pxi.pathOp == DESCENDANT_OP) {
+ rv = aContext->recycler()->getNodeSet(getter_AddRefs(resNodes));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = evalDescendants(pxi.expr, eContext.getContextNode(),
+ &eContext, resNodes);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ else {
+ RefPtr<txAExprResult> res;
+ rv = pxi.expr->evaluate(&eContext, getter_AddRefs(res));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (res->getResultType() != txAExprResult::NODESET) {
+ //XXX ErrorReport: report nonnodeset error
+ return NS_ERROR_XSLT_NODESET_EXPECTED;
+ }
+ resNodes = static_cast<txNodeSet*>
+ (static_cast<txAExprResult*>
+ (res));
+ }
+
+ if (tmpNodes) {
+ if (!resNodes->isEmpty()) {
+ RefPtr<txNodeSet> oldSet;
+ oldSet.swap(tmpNodes);
+ rv = aContext->recycler()->
+ getNonSharedNodeSet(oldSet, getter_AddRefs(tmpNodes));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ oldSet.swap(resNodes);
+ rv = aContext->recycler()->
+ getNonSharedNodeSet(oldSet, getter_AddRefs(resNodes));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ tmpNodes->addAndTransfer(resNodes);
+ }
+ }
+ else {
+ tmpNodes = resNodes;
+ }
+ }
+ nodes = tmpNodes;
+ if (nodes->isEmpty()) {
+ break;
+ }
+ }
+
+ *aResult = nodes;
+ NS_ADDREF(*aResult);
+
+ return NS_OK;
+} //-- evaluate
+
+/**
+ * Selects from the descendants of the context node
+ * all nodes that match the Expr
+**/
+nsresult
+PathExpr::evalDescendants(Expr* aStep, const txXPathNode& aNode,
+ txIMatchContext* aContext, txNodeSet* resNodes)
+{
+ txSingleNodeContext eContext(aNode, aContext);
+ RefPtr<txAExprResult> res;
+ nsresult rv = aStep->evaluate(&eContext, getter_AddRefs(res));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (res->getResultType() != txAExprResult::NODESET) {
+ //XXX ErrorReport: report nonnodeset error
+ return NS_ERROR_XSLT_NODESET_EXPECTED;
+ }
+
+ txNodeSet* oldSet = static_cast<txNodeSet*>
+ (static_cast<txAExprResult*>(res));
+ RefPtr<txNodeSet> newSet;
+ rv = aContext->recycler()->getNonSharedNodeSet(oldSet,
+ getter_AddRefs(newSet));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ resNodes->addAndTransfer(newSet);
+
+ bool filterWS = aContext->isStripSpaceAllowed(aNode);
+
+ txXPathTreeWalker walker(aNode);
+ if (!walker.moveToFirstChild()) {
+ return NS_OK;
+ }
+
+ do {
+ const txXPathNode& node = walker.getCurrentPosition();
+ if (!(filterWS && txXPathNodeUtils::isText(node) &&
+ txXPathNodeUtils::isWhitespace(node))) {
+ rv = evalDescendants(aStep, node, aContext, resNodes);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ } while (walker.moveToNextSibling());
+
+ return NS_OK;
+} //-- evalDescendants
+
+Expr::ExprType
+PathExpr::getType()
+{
+ return PATH_EXPR;
+}
+
+TX_IMPL_EXPR_STUBS_BASE(PathExpr, NODESET_RESULT)
+
+Expr*
+PathExpr::getSubExprAt(uint32_t aPos)
+{
+ return aPos < mItems.Length() ? mItems[aPos].expr.get() : nullptr;
+}
+void
+PathExpr::setSubExprAt(uint32_t aPos, Expr* aExpr)
+{
+ NS_ASSERTION(aPos < mItems.Length(), "setting bad subexpression index");
+ mItems[aPos].expr.forget();
+ mItems[aPos].expr = aExpr;
+}
+
+
+bool
+PathExpr::isSensitiveTo(ContextSensitivity aContext)
+{
+ if (mItems[0].expr->isSensitiveTo(aContext)) {
+ return true;
+ }
+
+ // We're creating a new node/nodeset so we can ignore those bits.
+ Expr::ContextSensitivity context =
+ aContext & ~(Expr::NODE_CONTEXT | Expr::NODESET_CONTEXT);
+ if (context == NO_CONTEXT) {
+ return false;
+ }
+
+ uint32_t i, len = mItems.Length();
+ for (i = 0; i < len; ++i) {
+ NS_ASSERTION(!mItems[i].expr->isSensitiveTo(Expr::NODESET_CONTEXT),
+ "Step cannot depend on nodeset-context");
+ if (mItems[i].expr->isSensitiveTo(context)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+#ifdef TX_TO_STRING
+void
+PathExpr::toString(nsAString& dest)
+{
+ if (!mItems.IsEmpty()) {
+ NS_ASSERTION(mItems[0].pathOp == RELATIVE_OP,
+ "First step should be relative");
+ mItems[0].expr->toString(dest);
+ }
+
+ uint32_t i, len = mItems.Length();
+ for (i = 1; i < len; ++i) {
+ switch (mItems[i].pathOp) {
+ case DESCENDANT_OP:
+ dest.AppendLiteral("//");
+ break;
+ case RELATIVE_OP:
+ dest.Append(char16_t('/'));
+ break;
+ }
+ mItems[i].expr->toString(dest);
+ }
+}
+#endif
diff --git a/dom/xslt/xpath/txPredicateList.cpp b/dom/xslt/xpath/txPredicateList.cpp
new file mode 100644
index 000000000..937d4148f
--- /dev/null
+++ b/dom/xslt/xpath/txPredicateList.cpp
@@ -0,0 +1,85 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "txExpr.h"
+#include "txNodeSet.h"
+#include "txNodeSetContext.h"
+
+/*
+ * Represents an ordered list of Predicates,
+ * for use with Step and Filter Expressions
+ */
+
+nsresult
+PredicateList::evaluatePredicates(txNodeSet* nodes,
+ txIMatchContext* aContext)
+{
+ NS_ASSERTION(nodes, "called evaluatePredicates with nullptr NodeSet");
+ nsresult rv = NS_OK;
+
+ uint32_t i, len = mPredicates.Length();
+ for (i = 0; i < len && !nodes->isEmpty(); ++i) {
+ txNodeSetContext predContext(nodes, aContext);
+ /*
+ * add nodes to newNodes that match the expression
+ * or, if the result is a number, add the node with the right
+ * position
+ */
+ int32_t index = 0;
+ while (predContext.hasNext()) {
+ predContext.next();
+ RefPtr<txAExprResult> exprResult;
+ rv = mPredicates[i]->evaluate(&predContext,
+ getter_AddRefs(exprResult));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // handle default, [position() == numberValue()]
+ if (exprResult->getResultType() == txAExprResult::NUMBER) {
+ if ((double)predContext.position() == exprResult->numberValue()) {
+ nodes->mark(index);
+ }
+ }
+ else if (exprResult->booleanValue()) {
+ nodes->mark(index);
+ }
+ ++index;
+ }
+ // sweep the non-marked nodes
+ nodes->sweep();
+ }
+
+ return NS_OK;
+}
+
+bool
+PredicateList::isSensitiveTo(Expr::ContextSensitivity aContext)
+{
+ // We're creating a new node/nodeset so we can ignore those bits.
+ Expr::ContextSensitivity context =
+ aContext & ~(Expr::NODE_CONTEXT | Expr::NODESET_CONTEXT);
+ if (context == Expr::NO_CONTEXT) {
+ return false;
+ }
+
+ uint32_t i, len = mPredicates.Length();
+ for (i = 0; i < len; ++i) {
+ if (mPredicates[i]->isSensitiveTo(context)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+#ifdef TX_TO_STRING
+void PredicateList::toString(nsAString& dest)
+{
+ for (uint32_t i = 0; i < mPredicates.Length(); ++i) {
+ dest.Append(char16_t('['));
+ mPredicates[i]->toString(dest);
+ dest.Append(char16_t(']'));
+ }
+}
+#endif
diff --git a/dom/xslt/xpath/txPredicatedNodeTest.cpp b/dom/xslt/xpath/txPredicatedNodeTest.cpp
new file mode 100644
index 000000000..4726f48f2
--- /dev/null
+++ b/dom/xslt/xpath/txPredicatedNodeTest.cpp
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "txExpr.h"
+#include "txExprResult.h"
+#include "txSingleNodeContext.h"
+
+txPredicatedNodeTest::txPredicatedNodeTest(txNodeTest* aNodeTest,
+ Expr* aPredicate)
+ : mNodeTest(aNodeTest),
+ mPredicate(aPredicate)
+{
+ NS_ASSERTION(!mPredicate->isSensitiveTo(Expr::NODESET_CONTEXT),
+ "predicate must not be context-nodeset-sensitive");
+}
+
+bool
+txPredicatedNodeTest::matches(const txXPathNode& aNode,
+ txIMatchContext* aContext)
+{
+ if (!mNodeTest->matches(aNode, aContext)) {
+ return false;
+ }
+
+ txSingleNodeContext context(aNode, aContext);
+ RefPtr<txAExprResult> res;
+ nsresult rv = mPredicate->evaluate(&context, getter_AddRefs(res));
+ NS_ENSURE_SUCCESS(rv, false);
+
+ return res->booleanValue();
+}
+
+double
+txPredicatedNodeTest::getDefaultPriority()
+{
+ return 0.5;
+}
+
+bool
+txPredicatedNodeTest::isSensitiveTo(Expr::ContextSensitivity aContext)
+{
+ return mNodeTest->isSensitiveTo(aContext) ||
+ mPredicate->isSensitiveTo(aContext);
+}
+
+#ifdef TX_TO_STRING
+void
+txPredicatedNodeTest::toString(nsAString& aDest)
+{
+ mNodeTest->toString(aDest);
+ aDest.Append(char16_t('['));
+ mPredicate->toString(aDest);
+ aDest.Append(char16_t(']'));
+}
+#endif
diff --git a/dom/xslt/xpath/txRelationalExpr.cpp b/dom/xslt/xpath/txRelationalExpr.cpp
new file mode 100644
index 000000000..5621ede7b
--- /dev/null
+++ b/dom/xslt/xpath/txRelationalExpr.cpp
@@ -0,0 +1,202 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "txExpr.h"
+#include "txNodeSet.h"
+#include "txIXPathContext.h"
+#include "txXPathTreeWalker.h"
+
+/**
+ * Compares the two ExprResults based on XPath 1.0 Recommendation (section 3.4)
+ */
+bool
+RelationalExpr::compareResults(txIEvalContext* aContext, txAExprResult* aLeft,
+ txAExprResult* aRight)
+{
+ short ltype = aLeft->getResultType();
+ short rtype = aRight->getResultType();
+ nsresult rv = NS_OK;
+
+ // Handle case for just Left NodeSet or Both NodeSets
+ if (ltype == txAExprResult::NODESET) {
+ if (rtype == txAExprResult::BOOLEAN) {
+ BooleanResult leftBool(aLeft->booleanValue());
+ return compareResults(aContext, &leftBool, aRight);
+ }
+
+ txNodeSet* nodeSet = static_cast<txNodeSet*>(aLeft);
+ RefPtr<StringResult> strResult;
+ rv = aContext->recycler()->getStringResult(getter_AddRefs(strResult));
+ NS_ENSURE_SUCCESS(rv, false);
+
+ int32_t i;
+ for (i = 0; i < nodeSet->size(); ++i) {
+ strResult->mValue.Truncate();
+ txXPathNodeUtils::appendNodeValue(nodeSet->get(i),
+ strResult->mValue);
+ if (compareResults(aContext, strResult, aRight)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ // Handle case for Just Right NodeSet
+ if (rtype == txAExprResult::NODESET) {
+ if (ltype == txAExprResult::BOOLEAN) {
+ BooleanResult rightBool(aRight->booleanValue());
+ return compareResults(aContext, aLeft, &rightBool);
+ }
+
+ txNodeSet* nodeSet = static_cast<txNodeSet*>(aRight);
+ RefPtr<StringResult> strResult;
+ rv = aContext->recycler()->getStringResult(getter_AddRefs(strResult));
+ NS_ENSURE_SUCCESS(rv, false);
+
+ int32_t i;
+ for (i = 0; i < nodeSet->size(); ++i) {
+ strResult->mValue.Truncate();
+ txXPathNodeUtils::appendNodeValue(nodeSet->get(i),
+ strResult->mValue);
+ if (compareResults(aContext, aLeft, strResult)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ // Neither is a NodeSet
+ if (mOp == EQUAL || mOp == NOT_EQUAL) {
+ bool result;
+ const nsString *lString, *rString;
+
+ // If either is a bool, compare as bools.
+ if (ltype == txAExprResult::BOOLEAN ||
+ rtype == txAExprResult::BOOLEAN) {
+ result = aLeft->booleanValue() == aRight->booleanValue();
+ }
+
+ // If either is a number, compare as numbers.
+ else if (ltype == txAExprResult::NUMBER ||
+ rtype == txAExprResult::NUMBER) {
+ double lval = aLeft->numberValue();
+ double rval = aRight->numberValue();
+ result = (lval == rval);
+ }
+
+ // Otherwise compare as strings. Try to use the stringobject in
+ // StringResult if possible since that is a common case.
+ else if ((lString = aLeft->stringValuePointer())) {
+ if ((rString = aRight->stringValuePointer())) {
+ result = lString->Equals(*rString);
+ }
+ else {
+ nsAutoString rStr;
+ aRight->stringValue(rStr);
+ result = lString->Equals(rStr);
+ }
+ }
+ else if ((rString = aRight->stringValuePointer())) {
+ nsAutoString lStr;
+ aLeft->stringValue(lStr);
+ result = rString->Equals(lStr);
+ }
+ else {
+ nsAutoString lStr, rStr;
+ aLeft->stringValue(lStr);
+ aRight->stringValue(rStr);
+ result = lStr.Equals(rStr);
+ }
+
+ return mOp == EQUAL ? result : !result;
+ }
+
+ double leftDbl = aLeft->numberValue();
+ double rightDbl = aRight->numberValue();
+ switch (mOp) {
+ case LESS_THAN:
+ {
+ return (leftDbl < rightDbl);
+ }
+ case LESS_OR_EQUAL:
+ {
+ return (leftDbl <= rightDbl);
+ }
+ case GREATER_THAN:
+ {
+ return (leftDbl > rightDbl);
+ }
+ case GREATER_OR_EQUAL:
+ {
+ return (leftDbl >= rightDbl);
+ }
+ default:
+ {
+ NS_NOTREACHED("We should have caught all cases");
+ }
+ }
+
+ return false;
+}
+
+nsresult
+RelationalExpr::evaluate(txIEvalContext* aContext, txAExprResult** aResult)
+{
+ *aResult = nullptr;
+ RefPtr<txAExprResult> lResult;
+ nsresult rv = mLeftExpr->evaluate(aContext, getter_AddRefs(lResult));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ RefPtr<txAExprResult> rResult;
+ rv = mRightExpr->evaluate(aContext, getter_AddRefs(rResult));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aContext->recycler()->
+ getBoolResult(compareResults(aContext, lResult, rResult), aResult);
+
+ return NS_OK;
+}
+
+TX_IMPL_EXPR_STUBS_2(RelationalExpr, BOOLEAN_RESULT, mLeftExpr, mRightExpr)
+
+bool
+RelationalExpr::isSensitiveTo(ContextSensitivity aContext)
+{
+ return mLeftExpr->isSensitiveTo(aContext) ||
+ mRightExpr->isSensitiveTo(aContext);
+}
+
+#ifdef TX_TO_STRING
+void
+RelationalExpr::toString(nsAString& str)
+{
+ mLeftExpr->toString(str);
+
+ switch (mOp) {
+ case NOT_EQUAL:
+ str.AppendLiteral("!=");
+ break;
+ case LESS_THAN:
+ str.Append(char16_t('<'));
+ break;
+ case LESS_OR_EQUAL:
+ str.AppendLiteral("<=");
+ break;
+ case GREATER_THAN :
+ str.Append(char16_t('>'));
+ break;
+ case GREATER_OR_EQUAL:
+ str.AppendLiteral(">=");
+ break;
+ default:
+ str.Append(char16_t('='));
+ break;
+ }
+
+ mRightExpr->toString(str);
+}
+#endif
diff --git a/dom/xslt/xpath/txResultRecycler.cpp b/dom/xslt/xpath/txResultRecycler.cpp
new file mode 100644
index 000000000..774a0345c
--- /dev/null
+++ b/dom/xslt/xpath/txResultRecycler.cpp
@@ -0,0 +1,216 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "txResultRecycler.h"
+#include "txExprResult.h"
+#include "txNodeSet.h"
+
+txResultRecycler::txResultRecycler()
+ : mEmptyStringResult(new StringResult(nullptr)),
+ mTrueResult(new BooleanResult(true)),
+ mFalseResult(new BooleanResult(false))
+{
+}
+
+txResultRecycler::~txResultRecycler()
+{
+ txStackIterator stringIter(&mStringResults);
+ while (stringIter.hasNext()) {
+ delete static_cast<StringResult*>(stringIter.next());
+ }
+ txStackIterator nodesetIter(&mNodeSetResults);
+ while (nodesetIter.hasNext()) {
+ delete static_cast<txNodeSet*>(nodesetIter.next());
+ }
+ txStackIterator numberIter(&mNumberResults);
+ while (numberIter.hasNext()) {
+ delete static_cast<NumberResult*>(numberIter.next());
+ }
+}
+
+
+void
+txResultRecycler::recycle(txAExprResult* aResult)
+{
+ NS_ASSERTION(aResult->mRefCnt == 0, "In-use txAExprResult recycled");
+ RefPtr<txResultRecycler> kungFuDeathGrip;
+ aResult->mRecycler.swap(kungFuDeathGrip);
+
+ nsresult rv = NS_OK;
+ switch (aResult->getResultType()) {
+ case txAExprResult::STRING:
+ {
+ rv = mStringResults.push(static_cast<StringResult*>(aResult));
+ if (NS_FAILED(rv)) {
+ delete aResult;
+ }
+ return;
+ }
+ case txAExprResult::NODESET:
+ {
+ static_cast<txNodeSet*>(aResult)->clear();
+ rv = mNodeSetResults.push(static_cast<txNodeSet*>(aResult));
+ if (NS_FAILED(rv)) {
+ delete aResult;
+ }
+ return;
+ }
+ case txAExprResult::NUMBER:
+ {
+ rv = mNumberResults.push(static_cast<NumberResult*>(aResult));
+ if (NS_FAILED(rv)) {
+ delete aResult;
+ }
+ return;
+ }
+ default:
+ {
+ delete aResult;
+ }
+ }
+}
+
+nsresult
+txResultRecycler::getStringResult(StringResult** aResult)
+{
+ if (mStringResults.isEmpty()) {
+ *aResult = new StringResult(this);
+ }
+ else {
+ *aResult = static_cast<StringResult*>(mStringResults.pop());
+ (*aResult)->mValue.Truncate();
+ (*aResult)->mRecycler = this;
+ }
+ NS_ADDREF(*aResult);
+
+ return NS_OK;
+}
+
+nsresult
+txResultRecycler::getStringResult(const nsAString& aValue,
+ txAExprResult** aResult)
+{
+ if (mStringResults.isEmpty()) {
+ *aResult = new StringResult(aValue, this);
+ }
+ else {
+ StringResult* strRes =
+ static_cast<StringResult*>(mStringResults.pop());
+ strRes->mValue = aValue;
+ strRes->mRecycler = this;
+ *aResult = strRes;
+ }
+ NS_ADDREF(*aResult);
+
+ return NS_OK;
+}
+
+void
+txResultRecycler::getEmptyStringResult(txAExprResult** aResult)
+{
+ *aResult = mEmptyStringResult;
+ NS_ADDREF(*aResult);
+}
+
+nsresult
+txResultRecycler::getNodeSet(txNodeSet** aResult)
+{
+ if (mNodeSetResults.isEmpty()) {
+ *aResult = new txNodeSet(this);
+ }
+ else {
+ *aResult = static_cast<txNodeSet*>(mNodeSetResults.pop());
+ (*aResult)->mRecycler = this;
+ }
+ NS_ADDREF(*aResult);
+
+ return NS_OK;
+}
+
+nsresult
+txResultRecycler::getNodeSet(txNodeSet* aNodeSet, txNodeSet** aResult)
+{
+ if (mNodeSetResults.isEmpty()) {
+ *aResult = new txNodeSet(*aNodeSet, this);
+ }
+ else {
+ *aResult = static_cast<txNodeSet*>(mNodeSetResults.pop());
+ (*aResult)->append(*aNodeSet);
+ (*aResult)->mRecycler = this;
+ }
+ NS_ADDREF(*aResult);
+
+ return NS_OK;
+}
+
+nsresult
+txResultRecycler::getNodeSet(const txXPathNode& aNode, txAExprResult** aResult)
+{
+ if (mNodeSetResults.isEmpty()) {
+ *aResult = new txNodeSet(aNode, this);
+ }
+ else {
+ txNodeSet* nodes = static_cast<txNodeSet*>(mNodeSetResults.pop());
+ nodes->append(aNode);
+ nodes->mRecycler = this;
+ *aResult = nodes;
+ }
+ NS_ADDREF(*aResult);
+
+ return NS_OK;
+}
+
+nsresult
+txResultRecycler::getNumberResult(double aValue, txAExprResult** aResult)
+{
+ if (mNumberResults.isEmpty()) {
+ *aResult = new NumberResult(aValue, this);
+ }
+ else {
+ NumberResult* numRes =
+ static_cast<NumberResult*>(mNumberResults.pop());
+ numRes->value = aValue;
+ numRes->mRecycler = this;
+ *aResult = numRes;
+ }
+ NS_ADDREF(*aResult);
+
+ return NS_OK;
+}
+
+void
+txResultRecycler::getBoolResult(bool aValue, txAExprResult** aResult)
+{
+ *aResult = aValue ? mTrueResult : mFalseResult;
+ NS_ADDREF(*aResult);
+}
+
+nsresult
+txResultRecycler::getNonSharedNodeSet(txNodeSet* aNodeSet, txNodeSet** aResult)
+{
+ if (aNodeSet->mRefCnt > 1) {
+ return getNodeSet(aNodeSet, aResult);
+ }
+
+ *aResult = aNodeSet;
+ NS_ADDREF(*aResult);
+
+ return NS_OK;
+}
+
+void
+txAExprResult::Release()
+{
+ --mRefCnt;
+ NS_LOG_RELEASE(this, mRefCnt, "txAExprResult");
+ if (mRefCnt == 0) {
+ if (mRecycler) {
+ mRecycler->recycle(this);
+ }
+ else {
+ delete this;
+ }
+ }
+}
diff --git a/dom/xslt/xpath/txResultRecycler.h b/dom/xslt/xpath/txResultRecycler.h
new file mode 100644
index 000000000..eec7d75e8
--- /dev/null
+++ b/dom/xslt/xpath/txResultRecycler.h
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+#ifndef txResultRecycler_h__
+#define txResultRecycler_h__
+
+#include "nsCOMPtr.h"
+#include "nsAutoPtr.h"
+#include "txStack.h"
+
+class txAExprResult;
+class StringResult;
+class txNodeSet;
+class txXPathNode;
+class NumberResult;
+class BooleanResult;
+
+class txResultRecycler
+{
+public:
+ txResultRecycler();
+ ~txResultRecycler();
+
+ void AddRef()
+ {
+ ++mRefCnt;
+ NS_LOG_ADDREF(this, mRefCnt, "txResultRecycler", sizeof(*this));
+ }
+ void Release()
+ {
+ --mRefCnt;
+ NS_LOG_RELEASE(this, mRefCnt, "txResultRecycler");
+ if (mRefCnt == 0) {
+ mRefCnt = 1; //stabilize
+ delete this;
+ }
+ }
+
+ /**
+ * Returns an txAExprResult to this recycler for reuse.
+ * @param aResult result to recycle
+ */
+ void recycle(txAExprResult* aResult);
+
+ /**
+ * Functions to return results that will be fully used by the caller.
+ * Returns nullptr on out-of-memory and an inited result otherwise.
+ */
+ nsresult getStringResult(StringResult** aResult);
+ nsresult getStringResult(const nsAString& aValue, txAExprResult** aResult);
+ nsresult getNodeSet(txNodeSet** aResult);
+ nsresult getNodeSet(txNodeSet* aNodeSet, txNodeSet** aResult);
+ nsresult getNodeSet(const txXPathNode& aNode, txAExprResult** aResult);
+ nsresult getNumberResult(double aValue, txAExprResult** aResult);
+
+ /**
+ * Functions to return a txAExprResult that is shared across several
+ * clients and must not be modified. Never returns nullptr.
+ */
+ void getEmptyStringResult(txAExprResult** aResult);
+ void getBoolResult(bool aValue, txAExprResult** aResult);
+
+ /**
+ * Functions that return non-shared resultsobjects
+ */
+ nsresult getNonSharedNodeSet(txNodeSet* aNodeSet, txNodeSet** aResult);
+
+private:
+ nsAutoRefCnt mRefCnt;
+ txStack mStringResults;
+ txStack mNodeSetResults;
+ txStack mNumberResults;
+ RefPtr<StringResult> mEmptyStringResult;
+ RefPtr<BooleanResult> mTrueResult;
+ RefPtr<BooleanResult> mFalseResult;
+};
+
+#endif //txResultRecycler_h__
diff --git a/dom/xslt/xpath/txRootExpr.cpp b/dom/xslt/xpath/txRootExpr.cpp
new file mode 100644
index 000000000..f6ef6abcb
--- /dev/null
+++ b/dom/xslt/xpath/txRootExpr.cpp
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "txExpr.h"
+#include "txNodeSet.h"
+#include "txIXPathContext.h"
+#include "txXPathTreeWalker.h"
+
+/**
+ * Evaluates this Expr based on the given context node and processor state
+ * @param context the context node for evaluation of this Expr
+ * @param ps the ContextState containing the stack information needed
+ * for evaluation
+ * @return the result of the evaluation
+**/
+nsresult
+RootExpr::evaluate(txIEvalContext* aContext, txAExprResult** aResult)
+{
+ txXPathTreeWalker walker(aContext->getContextNode());
+ walker.moveToRoot();
+
+ return aContext->recycler()->getNodeSet(walker.getCurrentPosition(),
+ aResult);
+}
+
+TX_IMPL_EXPR_STUBS_0(RootExpr, NODESET_RESULT)
+
+bool
+RootExpr::isSensitiveTo(ContextSensitivity aContext)
+{
+ return !!(aContext & NODE_CONTEXT);
+}
+
+#ifdef TX_TO_STRING
+void
+RootExpr::toString(nsAString& dest)
+{
+ if (mSerialize)
+ dest.Append(char16_t('/'));
+}
+#endif
diff --git a/dom/xslt/xpath/txSingleNodeContext.h b/dom/xslt/xpath/txSingleNodeContext.h
new file mode 100644
index 000000000..e66083d80
--- /dev/null
+++ b/dom/xslt/xpath/txSingleNodeContext.h
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+#ifndef __TX_XPATH_SINGLENODE_CONTEXT
+#define __TX_XPATH_SINGLENODE_CONTEXT
+
+#include "mozilla/Attributes.h"
+#include "txIXPathContext.h"
+
+class txSingleNodeContext : public txIEvalContext
+{
+public:
+ txSingleNodeContext(const txXPathNode& aContextNode,
+ txIMatchContext* aContext)
+ : mNode(aContextNode),
+ mInner(aContext)
+ {
+ NS_ASSERTION(aContext, "txIMatchContext must be given");
+ }
+
+ nsresult getVariable(int32_t aNamespace, nsIAtom* aLName,
+ txAExprResult*& aResult) override
+ {
+ NS_ASSERTION(mInner, "mInner is null!!!");
+ return mInner->getVariable(aNamespace, aLName, aResult);
+ }
+
+ bool isStripSpaceAllowed(const txXPathNode& aNode) override
+ {
+ NS_ASSERTION(mInner, "mInner is null!!!");
+ return mInner->isStripSpaceAllowed(aNode);
+ }
+
+ void* getPrivateContext() override
+ {
+ NS_ASSERTION(mInner, "mInner is null!!!");
+ return mInner->getPrivateContext();
+ }
+
+ txResultRecycler* recycler() override
+ {
+ NS_ASSERTION(mInner, "mInner is null!!!");
+ return mInner->recycler();
+ }
+
+ void receiveError(const nsAString& aMsg, nsresult aRes) override
+ {
+ NS_ASSERTION(mInner, "mInner is null!!!");
+#ifdef DEBUG
+ nsAutoString error(NS_LITERAL_STRING("forwarded error: "));
+ error.Append(aMsg);
+ mInner->receiveError(error, aRes);
+#else
+ mInner->receiveError(aMsg, aRes);
+#endif
+ }
+
+ const txXPathNode& getContextNode() override
+ {
+ return mNode;
+ }
+
+ uint32_t size() override
+ {
+ return 1;
+ }
+
+ uint32_t position() override
+ {
+ return 1;
+ }
+
+private:
+ const txXPathNode& mNode;
+ txIMatchContext* mInner;
+};
+
+#endif // __TX_XPATH_SINGLENODE_CONTEXT
diff --git a/dom/xslt/xpath/txStringResult.cpp b/dom/xslt/xpath/txStringResult.cpp
new file mode 100644
index 000000000..f3fbe4eaf
--- /dev/null
+++ b/dom/xslt/xpath/txStringResult.cpp
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/**
+ * StringResult
+ * Represents a String as a Result of evaluating an Expr
+**/
+#include "txExprResult.h"
+
+/**
+ * Default Constructor
+**/
+StringResult::StringResult(txResultRecycler* aRecycler)
+ : txAExprResult(aRecycler)
+{
+}
+
+/**
+ * Creates a new StringResult with the value of the given String parameter
+ * @param str the String to use for initialization of this StringResult's value
+**/
+StringResult::StringResult(const nsAString& aValue, txResultRecycler* aRecycler)
+ : txAExprResult(aRecycler), mValue(aValue)
+{
+}
+
+/*
+ * Virtual Methods from ExprResult
+*/
+
+short StringResult::getResultType() {
+ return txAExprResult::STRING;
+} //-- getResultType
+
+void
+StringResult::stringValue(nsString& aResult)
+{
+ aResult.Append(mValue);
+}
+
+const nsString*
+StringResult::stringValuePointer()
+{
+ return &mValue;
+}
+
+bool StringResult::booleanValue() {
+ return !mValue.IsEmpty();
+} //-- booleanValue
+
+double StringResult::numberValue() {
+ return txDouble::toDouble(mValue);
+} //-- numberValue
+
diff --git a/dom/xslt/xpath/txUnaryExpr.cpp b/dom/xslt/xpath/txUnaryExpr.cpp
new file mode 100644
index 000000000..95682b5b2
--- /dev/null
+++ b/dom/xslt/xpath/txUnaryExpr.cpp
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "txExpr.h"
+#include "txIXPathContext.h"
+
+/*
+ * Evaluates this Expr based on the given context node and processor state
+ * @param context the context node for evaluation of this Expr
+ * @param ps the ContextState containing the stack information needed
+ * for evaluation.
+ * @return the result of the evaluation.
+ */
+nsresult
+UnaryExpr::evaluate(txIEvalContext* aContext, txAExprResult** aResult)
+{
+ *aResult = nullptr;
+
+ RefPtr<txAExprResult> exprRes;
+ nsresult rv = expr->evaluate(aContext, getter_AddRefs(exprRes));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ double value = exprRes->numberValue();
+#ifdef HPUX
+ /*
+ * Negation of a zero doesn't produce a negative
+ * zero on HPUX. Perform the operation by multiplying with
+ * -1.
+ */
+ return aContext->recycler()->getNumberResult(-1 * value, aResult);
+#else
+ return aContext->recycler()->getNumberResult(-value, aResult);
+#endif
+}
+
+TX_IMPL_EXPR_STUBS_1(UnaryExpr, NODESET_RESULT, expr)
+
+bool
+UnaryExpr::isSensitiveTo(ContextSensitivity aContext)
+{
+ return expr->isSensitiveTo(aContext);
+}
+
+#ifdef TX_TO_STRING
+void
+UnaryExpr::toString(nsAString& str)
+{
+ if (!expr)
+ return;
+ str.Append(char16_t('-'));
+ expr->toString(str);
+}
+#endif
diff --git a/dom/xslt/xpath/txUnionExpr.cpp b/dom/xslt/xpath/txUnionExpr.cpp
new file mode 100644
index 000000000..0bde2c38a
--- /dev/null
+++ b/dom/xslt/xpath/txUnionExpr.cpp
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "txExpr.h"
+#include "txIXPathContext.h"
+#include "txNodeSet.h"
+
+ //-------------/
+ //- UnionExpr -/
+//-------------/
+
+ //-----------------------------/
+ //- Virtual methods from Expr -/
+//-----------------------------/
+
+/**
+ * Evaluates this Expr based on the given context node and processor state
+ * @param context the context node for evaluation of this Expr
+ * @param ps the ContextState containing the stack information needed
+ * for evaluation
+ * @return the result of the evaluation
+**/
+nsresult
+UnionExpr::evaluate(txIEvalContext* aContext, txAExprResult** aResult)
+{
+ *aResult = nullptr;
+ RefPtr<txNodeSet> nodes;
+ nsresult rv = aContext->recycler()->getNodeSet(getter_AddRefs(nodes));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ uint32_t i, len = mExpressions.Length();
+ for (i = 0; i < len; ++i) {
+ RefPtr<txAExprResult> exprResult;
+ rv = mExpressions[i]->evaluate(aContext, getter_AddRefs(exprResult));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (exprResult->getResultType() != txAExprResult::NODESET) {
+ //XXX ErrorReport: report nonnodeset error
+ return NS_ERROR_XSLT_NODESET_EXPECTED;
+ }
+
+ RefPtr<txNodeSet> resultSet, ownedSet;
+ resultSet = static_cast<txNodeSet*>
+ (static_cast<txAExprResult*>(exprResult));
+ exprResult = nullptr;
+ rv = aContext->recycler()->
+ getNonSharedNodeSet(resultSet, getter_AddRefs(ownedSet));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = nodes->addAndTransfer(ownedSet);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ *aResult = nodes;
+ NS_ADDREF(*aResult);
+
+ return NS_OK;
+} //-- evaluate
+
+Expr::ExprType
+UnionExpr::getType()
+{
+ return UNION_EXPR;
+}
+
+TX_IMPL_EXPR_STUBS_LIST(UnionExpr, NODESET_RESULT, mExpressions)
+
+bool
+UnionExpr::isSensitiveTo(ContextSensitivity aContext)
+{
+ uint32_t i, len = mExpressions.Length();
+ for (i = 0; i < len; ++i) {
+ if (mExpressions[i]->isSensitiveTo(aContext)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+#ifdef TX_TO_STRING
+void
+UnionExpr::toString(nsAString& dest)
+{
+ uint32_t i;
+ for (i = 0; i < mExpressions.Length(); ++i) {
+ if (i > 0)
+ dest.AppendLiteral(" | ");
+ mExpressions[i]->toString(dest);
+ }
+}
+#endif
diff --git a/dom/xslt/xpath/txUnionNodeTest.cpp b/dom/xslt/xpath/txUnionNodeTest.cpp
new file mode 100644
index 000000000..421ea680c
--- /dev/null
+++ b/dom/xslt/xpath/txUnionNodeTest.cpp
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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 "mozilla/FloatingPoint.h"
+
+#include "txExpr.h"
+#include "txExprResult.h"
+#include "txSingleNodeContext.h"
+
+bool
+txUnionNodeTest::matches(const txXPathNode& aNode,
+ txIMatchContext* aContext)
+{
+ uint32_t i, len = mNodeTests.Length();
+ for (i = 0; i < len; ++i) {
+ if (mNodeTests[i]->matches(aNode, aContext)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+double
+txUnionNodeTest::getDefaultPriority()
+{
+ NS_ERROR("Don't call getDefaultPriority on txUnionPattern");
+ return mozilla::UnspecifiedNaN<double>();
+}
+
+bool
+txUnionNodeTest::isSensitiveTo(Expr::ContextSensitivity aContext)
+{
+ uint32_t i, len = mNodeTests.Length();
+ for (i = 0; i < len; ++i) {
+ if (mNodeTests[i]->isSensitiveTo(aContext)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+#ifdef TX_TO_STRING
+void
+txUnionNodeTest::toString(nsAString& aDest)
+{
+ aDest.Append('(');
+ for (uint32_t i = 0; i < mNodeTests.Length(); ++i) {
+ if (i != 0) {
+ aDest.AppendLiteral(" | ");
+ }
+ mNodeTests[i]->toString(aDest);
+ }
+ aDest.Append(')');
+}
+#endif
diff --git a/dom/xslt/xpath/txVariableRefExpr.cpp b/dom/xslt/xpath/txVariableRefExpr.cpp
new file mode 100644
index 000000000..813e17b8f
--- /dev/null
+++ b/dom/xslt/xpath/txVariableRefExpr.cpp
@@ -0,0 +1,69 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "txExpr.h"
+#include "nsIAtom.h"
+#include "txNodeSet.h"
+#include "nsGkAtoms.h"
+#include "txIXPathContext.h"
+
+ //-------------------/
+ //- VariableRefExpr -/
+//-------------------/
+
+/**
+ * Creates a VariableRefExpr with the given variable name
+**/
+VariableRefExpr::VariableRefExpr(nsIAtom* aPrefix, nsIAtom* aLocalName,
+ int32_t aNSID)
+ : mPrefix(aPrefix), mLocalName(aLocalName), mNamespace(aNSID)
+{
+ NS_ASSERTION(mLocalName, "VariableRefExpr without local name?");
+ if (mPrefix == nsGkAtoms::_empty)
+ mPrefix = nullptr;
+}
+
+/**
+ * Evaluates this Expr based on the given context node and processor state
+ * @param context the context node for evaluation of this Expr
+ * @param ps the ContextState containing the stack information needed
+ * for evaluation
+ * @return the result of the evaluation
+**/
+nsresult
+VariableRefExpr::evaluate(txIEvalContext* aContext, txAExprResult** aResult)
+{
+ nsresult rv = aContext->getVariable(mNamespace, mLocalName, *aResult);
+ if (NS_FAILED(rv)) {
+ // XXX report error, undefined variable
+ return rv;
+ }
+ return NS_OK;
+}
+
+TX_IMPL_EXPR_STUBS_0(VariableRefExpr, ANY_RESULT)
+
+bool
+VariableRefExpr::isSensitiveTo(ContextSensitivity aContext)
+{
+ return !!(aContext & VARIABLES_CONTEXT);
+}
+
+#ifdef TX_TO_STRING
+void
+VariableRefExpr::toString(nsAString& aDest)
+{
+ aDest.Append(char16_t('$'));
+ if (mPrefix) {
+ nsAutoString prefix;
+ mPrefix->ToString(prefix);
+ aDest.Append(prefix);
+ aDest.Append(char16_t(':'));
+ }
+ nsAutoString lname;
+ mLocalName->ToString(lname);
+ aDest.Append(lname);
+}
+#endif
diff --git a/dom/xslt/xpath/txXPCOMExtensionFunction.cpp b/dom/xslt/xpath/txXPCOMExtensionFunction.cpp
new file mode 100644
index 000000000..4913702aa
--- /dev/null
+++ b/dom/xslt/xpath/txXPCOMExtensionFunction.cpp
@@ -0,0 +1,617 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "nsAutoPtr.h"
+#include "nsComponentManagerUtils.h"
+#include "nsDependentString.h"
+#include "nsIAtom.h"
+#include "nsIInterfaceInfoManager.h"
+#include "nsServiceManagerUtils.h"
+#include "txExpr.h"
+#include "txIFunctionEvaluationContext.h"
+#include "txIXPathContext.h"
+#include "txNodeSetAdaptor.h"
+#include "txXPathTreeWalker.h"
+#include "xptcall.h"
+#include "txXPathObjectAdaptor.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/dom/ScriptSettings.h"
+#include "nsIClassInfo.h"
+#include "nsIInterfaceInfo.h"
+#include "js/RootingAPI.h"
+
+NS_IMPL_ISUPPORTS(txXPathObjectAdaptor, txIXPathObject)
+
+class txFunctionEvaluationContext final : public txIFunctionEvaluationContext
+{
+public:
+ txFunctionEvaluationContext(txIEvalContext *aContext, nsISupports *aState);
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_TXIFUNCTIONEVALUATIONCONTEXT
+
+ void ClearContext()
+ {
+ mContext = nullptr;
+ }
+
+private:
+ ~txFunctionEvaluationContext() {}
+
+ txIEvalContext *mContext;
+ nsCOMPtr<nsISupports> mState;
+};
+
+txFunctionEvaluationContext::txFunctionEvaluationContext(txIEvalContext *aContext,
+ nsISupports *aState)
+ : mContext(aContext),
+ mState(aState)
+{
+}
+
+NS_IMPL_ISUPPORTS(txFunctionEvaluationContext, txIFunctionEvaluationContext)
+
+NS_IMETHODIMP
+txFunctionEvaluationContext::GetPosition(uint32_t *aPosition)
+{
+ NS_ENSURE_TRUE(mContext, NS_ERROR_FAILURE);
+
+ *aPosition = mContext->position();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+txFunctionEvaluationContext::GetSize(uint32_t *aSize)
+{
+ NS_ENSURE_TRUE(mContext, NS_ERROR_FAILURE);
+
+ *aSize = mContext->size();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+txFunctionEvaluationContext::GetContextNode(nsIDOMNode **aNode)
+{
+ NS_ENSURE_TRUE(mContext, NS_ERROR_FAILURE);
+
+ return txXPathNativeNode::getNode(mContext->getContextNode(), aNode);
+}
+
+NS_IMETHODIMP
+txFunctionEvaluationContext::GetState(nsISupports **aState)
+{
+ NS_IF_ADDREF(*aState = mState);
+
+ return NS_OK;
+}
+
+enum txArgumentType {
+ eBOOLEAN = nsXPTType::T_BOOL,
+ eNUMBER = nsXPTType::T_DOUBLE,
+ eSTRING = nsXPTType::T_DOMSTRING,
+ eNODESET,
+ eCONTEXT,
+ eOBJECT,
+ eUNKNOWN
+};
+
+class txXPCOMExtensionFunctionCall : public FunctionCall
+{
+public:
+ txXPCOMExtensionFunctionCall(nsISupports *aHelper, const nsIID &aIID,
+ uint16_t aMethodIndex,
+#ifdef TX_TO_STRING
+ nsIAtom *aName,
+#endif
+ nsISupports *aState);
+
+ TX_DECL_FUNCTION
+
+private:
+ txArgumentType GetParamType(const nsXPTParamInfo &aParam,
+ nsIInterfaceInfo *aInfo);
+
+ nsCOMPtr<nsISupports> mHelper;
+ nsIID mIID;
+ uint16_t mMethodIndex;
+#ifdef TX_TO_STRING
+ nsCOMPtr<nsIAtom> mName;
+#endif
+ nsCOMPtr<nsISupports> mState;
+};
+
+txXPCOMExtensionFunctionCall::txXPCOMExtensionFunctionCall(nsISupports *aHelper,
+ const nsIID &aIID,
+ uint16_t aMethodIndex,
+#ifdef TX_TO_STRING
+ nsIAtom *aName,
+#endif
+ nsISupports *aState)
+ : mHelper(aHelper),
+ mIID(aIID),
+ mMethodIndex(aMethodIndex),
+#ifdef TX_TO_STRING
+ mName(aName),
+#endif
+ mState(aState)
+{
+}
+
+class txInterfacesArrayHolder
+{
+public:
+ txInterfacesArrayHolder(nsIID **aArray, uint32_t aCount) : mArray(aArray),
+ mCount(aCount)
+ {
+ }
+ ~txInterfacesArrayHolder()
+ {
+ NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(mCount, mArray);
+ }
+
+private:
+ nsIID **mArray;
+ uint32_t mCount;
+};
+
+static nsresult
+LookupFunction(const char *aContractID, nsIAtom* aName, nsIID &aIID,
+ uint16_t &aMethodIndex, nsISupports **aHelper)
+{
+ nsresult rv;
+ nsCOMPtr<nsISupports> helper = do_GetService(aContractID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIClassInfo> classInfo = do_QueryInterface(helper, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIInterfaceInfoManager> iim =
+ do_GetService(NS_INTERFACEINFOMANAGER_SERVICE_CONTRACTID);
+ NS_ENSURE_TRUE(iim, NS_ERROR_FAILURE);
+
+ nsIID** iidArray = nullptr;
+ uint32_t iidCount = 0;
+ rv = classInfo->GetInterfaces(&iidCount, &iidArray);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ txInterfacesArrayHolder holder(iidArray, iidCount);
+
+ // Remove any minus signs and uppercase the following letter (so
+ // foo-bar becomes fooBar). Note that if there are any names that already
+ // have uppercase letters they might cause false matches (both fooBar and
+ // foo-bar matching fooBar).
+ const char16_t *name = aName->GetUTF16String();
+ nsAutoCString methodName;
+ char16_t letter;
+ bool upperNext = false;
+ while ((letter = *name)) {
+ if (letter == '-') {
+ upperNext = true;
+ }
+ else {
+ MOZ_ASSERT(nsCRT::IsAscii(letter),
+ "invalid static_cast coming up");
+ methodName.Append(upperNext ?
+ nsCRT::ToUpper(static_cast<char>(letter)) :
+ letter);
+ upperNext = false;
+ }
+ ++name;
+ }
+
+ uint32_t i;
+ for (i = 0; i < iidCount; ++i) {
+ nsIID *iid = iidArray[i];
+
+ nsCOMPtr<nsIInterfaceInfo> info;
+ rv = iim->GetInfoForIID(iid, getter_AddRefs(info));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ uint16_t methodIndex;
+ const nsXPTMethodInfo *methodInfo;
+ rv = info->GetMethodInfoForName(methodName.get(), &methodIndex,
+ &methodInfo);
+ if (NS_SUCCEEDED(rv)) {
+ // Exclude notxpcom and hidden. Also check that we have at least a
+ // return value (the xpidl compiler ensures that that return value
+ // is the last argument).
+ uint8_t paramCount = methodInfo->GetParamCount();
+ if (methodInfo->IsNotXPCOM() || methodInfo->IsHidden() ||
+ paramCount == 0 ||
+ !methodInfo->GetParam(paramCount - 1).IsRetval()) {
+ return NS_ERROR_FAILURE;
+ }
+
+ aIID = *iid;
+ aMethodIndex = methodIndex;
+ return helper->QueryInterface(aIID, (void**)aHelper);
+ }
+ }
+
+ return NS_ERROR_XPATH_UNKNOWN_FUNCTION;
+}
+
+/* static */
+nsresult
+TX_ResolveFunctionCallXPCOM(const nsCString &aContractID, int32_t aNamespaceID,
+ nsIAtom* aName, nsISupports *aState,
+ FunctionCall **aFunction)
+{
+ nsIID iid;
+ uint16_t methodIndex = 0;
+ nsCOMPtr<nsISupports> helper;
+
+ nsresult rv = LookupFunction(aContractID.get(), aName, iid, methodIndex,
+ getter_AddRefs(helper));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!aFunction) {
+ return NS_OK;
+ }
+
+ *aFunction = new txXPCOMExtensionFunctionCall(helper, iid, methodIndex,
+#ifdef TX_TO_STRING
+ aName,
+#endif
+ aState);
+ return NS_OK;
+}
+
+txArgumentType
+txXPCOMExtensionFunctionCall::GetParamType(const nsXPTParamInfo &aParam,
+ nsIInterfaceInfo *aInfo)
+{
+ uint8_t tag = aParam.GetType().TagPart();
+ switch (tag) {
+ case nsXPTType::T_BOOL:
+ case nsXPTType::T_DOUBLE:
+ case nsXPTType::T_DOMSTRING:
+ {
+ return txArgumentType(tag);
+ }
+ case nsXPTType::T_INTERFACE:
+ case nsXPTType::T_INTERFACE_IS:
+ {
+ nsIID iid;
+ aInfo->GetIIDForParamNoAlloc(mMethodIndex, &aParam, &iid);
+ if (iid.Equals(NS_GET_IID(txINodeSet))) {
+ return eNODESET;
+ }
+ if (iid.Equals(NS_GET_IID(txIFunctionEvaluationContext))) {
+ return eCONTEXT;
+ }
+ if (iid.Equals(NS_GET_IID(txIXPathObject))) {
+ return eOBJECT;
+ }
+ return eUNKNOWN;
+ }
+ default:
+ {
+ // XXX Error!
+ return eUNKNOWN;
+ }
+ }
+}
+
+class txParamArrayHolder
+{
+public:
+ txParamArrayHolder()
+ : mCount(0)
+ {
+ }
+ txParamArrayHolder(txParamArrayHolder&& rhs)
+ : mArray(mozilla::Move(rhs.mArray))
+ , mCount(rhs.mCount)
+ {
+ rhs.mCount = 0;
+ }
+ ~txParamArrayHolder();
+
+ bool Init(uint8_t aCount);
+ operator nsXPTCVariant*() const
+ {
+ return mArray.get();
+ }
+
+ void trace(JSTracer* trc) {
+ for (uint8_t i = 0; i < mCount; ++i) {
+ if (mArray[i].type == nsXPTType::T_JSVAL) {
+ JS::UnsafeTraceRoot(trc, &mArray[i].val.j, "txParam value");
+ }
+ }
+ }
+
+private:
+ mozilla::UniquePtr<nsXPTCVariant[]> mArray;
+ uint8_t mCount;
+};
+
+txParamArrayHolder::~txParamArrayHolder()
+{
+ uint8_t i;
+ for (i = 0; i < mCount; ++i) {
+ nsXPTCVariant &variant = mArray[i];
+ if (variant.DoesValNeedCleanup()) {
+ if (variant.type.TagPart() == nsXPTType::T_DOMSTRING)
+ delete (nsAString*)variant.val.p;
+ else {
+ MOZ_ASSERT(variant.type.TagPart() == nsXPTType::T_INTERFACE ||
+ variant.type.TagPart() == nsXPTType::T_INTERFACE_IS,
+ "We only support cleanup of strings and interfaces "
+ "here, and this looks like neither!");
+ static_cast<nsISupports*>(variant.val.p)->Release();
+ }
+ }
+ }
+}
+
+bool
+txParamArrayHolder::Init(uint8_t aCount)
+{
+ mCount = aCount;
+ mArray = mozilla::MakeUnique<nsXPTCVariant[]>(mCount);
+ if (!mArray) {
+ return false;
+ }
+
+ memset(mArray.get(), 0, mCount * sizeof(nsXPTCVariant));
+
+ return true;
+}
+
+nsresult
+txXPCOMExtensionFunctionCall::evaluate(txIEvalContext* aContext,
+ txAExprResult** aResult)
+{
+ nsCOMPtr<nsIInterfaceInfoManager> iim =
+ do_GetService(NS_INTERFACEINFOMANAGER_SERVICE_CONTRACTID);
+ NS_ENSURE_TRUE(iim, NS_ERROR_FAILURE);
+
+ nsCOMPtr<nsIInterfaceInfo> info;
+ nsresult rv = iim->GetInfoForIID(&mIID, getter_AddRefs(info));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ const nsXPTMethodInfo *methodInfo;
+ rv = info->GetMethodInfo(mMethodIndex, &methodInfo);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ uint8_t paramCount = methodInfo->GetParamCount();
+ uint8_t inArgs = paramCount - 1;
+
+ JS::Rooted<txParamArrayHolder> invokeParams(mozilla::dom::RootingCx());
+ if (!invokeParams.get().Init(paramCount)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ const nsXPTParamInfo &paramInfo = methodInfo->GetParam(0);
+ txArgumentType type = GetParamType(paramInfo, info);
+ if (type == eUNKNOWN) {
+ return NS_ERROR_FAILURE;
+ }
+
+ txFunctionEvaluationContext *context;
+ uint32_t paramStart = 0;
+ if (type == eCONTEXT) {
+ if (paramInfo.IsOut()) {
+ // We don't support out values.
+ return NS_ERROR_FAILURE;
+ }
+
+ // Create context wrapper.
+ context = new txFunctionEvaluationContext(aContext, mState);
+
+ nsXPTCVariant &invokeParam = invokeParams.get()[0];
+ invokeParam.type = paramInfo.GetType();
+ invokeParam.SetValNeedsCleanup();
+ NS_ADDREF((txIFunctionEvaluationContext*&)invokeParam.val.p = context);
+
+ // Skip first argument, since it's the context.
+ paramStart = 1;
+ }
+ else {
+ context = nullptr;
+ }
+
+ // XXX varargs
+ if (!requireParams(inArgs - paramStart, inArgs - paramStart, aContext)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ uint32_t i;
+ for (i = paramStart; i < inArgs; ++i) {
+ Expr* expr = mParams[i - paramStart];
+
+ const nsXPTParamInfo &paramInfo = methodInfo->GetParam(i);
+ txArgumentType type = GetParamType(paramInfo, info);
+ if (type == eUNKNOWN) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsXPTCVariant &invokeParam = invokeParams.get()[i];
+ if (paramInfo.IsOut()) {
+ // We don't support out values.
+ return NS_ERROR_FAILURE;
+ }
+
+ invokeParam.type = paramInfo.GetType();
+ switch (type) {
+ case eNODESET:
+ {
+ RefPtr<txNodeSet> nodes;
+ rv = evaluateToNodeSet(expr, aContext, getter_AddRefs(nodes));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ txNodeSetAdaptor *adaptor = new txNodeSetAdaptor(nodes);
+ if (!adaptor) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ nsCOMPtr<txINodeSet> nodeSet = adaptor;
+ rv = adaptor->Init();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ invokeParam.SetValNeedsCleanup();
+ nodeSet.swap((txINodeSet*&)invokeParam.val.p);
+ break;
+ }
+ case eBOOLEAN:
+ {
+ rv = expr->evaluateToBool(aContext, invokeParam.val.b);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ break;
+ }
+ case eNUMBER:
+ {
+ double dbl;
+ rv = evaluateToNumber(mParams[0], aContext, &dbl);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ invokeParam.val.d = dbl;
+ break;
+ }
+ case eSTRING:
+ {
+ nsString *value = new nsString();
+ if (!value) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ rv = expr->evaluateToString(aContext, *value);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ invokeParam.SetValNeedsCleanup();
+ invokeParam.val.p = value;
+ break;
+ }
+ case eOBJECT:
+ {
+ RefPtr<txAExprResult> exprRes;
+ rv = expr->evaluate(aContext, getter_AddRefs(exprRes));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<txIXPathObject> adaptor =
+ new txXPathObjectAdaptor(exprRes);
+ if (!adaptor) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ invokeParam.SetValNeedsCleanup();
+ adaptor.swap((txIXPathObject*&)invokeParam.val.p);
+ break;
+ }
+ case eCONTEXT:
+ case eUNKNOWN:
+ {
+ // We only support passing the context as the *first* argument.
+ return NS_ERROR_FAILURE;
+ }
+ }
+ }
+
+ const nsXPTParamInfo &returnInfo = methodInfo->GetParam(inArgs);
+ txArgumentType returnType = GetParamType(returnInfo, info);
+ if (returnType == eUNKNOWN) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsXPTCVariant &returnParam = invokeParams.get()[inArgs];
+ returnParam.type = returnInfo.GetType();
+ if (returnType == eSTRING) {
+ nsString *value = new nsString();
+ returnParam.SetValNeedsCleanup();
+ returnParam.val.p = value;
+ }
+ else {
+ returnParam.SetIndirect();
+ if (returnType == eNODESET || returnType == eOBJECT) {
+ returnParam.SetValNeedsCleanup();
+ }
+ }
+
+ rv = NS_InvokeByIndex(mHelper, mMethodIndex, paramCount, invokeParams.get());
+
+ // In case someone is holding on to the txFunctionEvaluationContext which
+ // could thus stay alive longer than this function.
+ if (context) {
+ context->ClearContext();
+ }
+
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ switch (returnType) {
+ case eNODESET:
+ {
+ txINodeSet* nodeSet = static_cast<txINodeSet*>(returnParam.val.p);
+ nsCOMPtr<txIXPathObject> object = do_QueryInterface(nodeSet, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_ADDREF(*aResult = object->GetResult());
+
+ return NS_OK;
+ }
+ case eBOOLEAN:
+ {
+ aContext->recycler()->getBoolResult(returnParam.val.b, aResult);
+
+ return NS_OK;
+ }
+ case eNUMBER:
+ {
+ return aContext->recycler()->getNumberResult(returnParam.val.d,
+ aResult);
+ }
+ case eSTRING:
+ {
+ nsString *returned = static_cast<nsString*>
+ (returnParam.val.p);
+ return aContext->recycler()->getStringResult(*returned, aResult);
+ }
+ case eOBJECT:
+ {
+ txIXPathObject *object =
+ static_cast<txIXPathObject*>(returnParam.val.p);
+
+ NS_ADDREF(*aResult = object->GetResult());
+
+ return NS_OK;
+ }
+ default:
+ {
+ // Huh?
+ return NS_ERROR_FAILURE;
+ }
+ }
+}
+
+Expr::ResultType
+txXPCOMExtensionFunctionCall::getReturnType()
+{
+ // It doesn't really matter what we return here, but it might
+ // be a good idea to try to keep this as unoptimizable as possible
+ return ANY_RESULT;
+}
+
+bool
+txXPCOMExtensionFunctionCall::isSensitiveTo(ContextSensitivity aContext)
+{
+ // It doesn't really matter what we return here, but it might
+ // be a good idea to try to keep this as unoptimizable as possible
+ return true;
+}
+
+#ifdef TX_TO_STRING
+nsresult
+txXPCOMExtensionFunctionCall::getNameAtom(nsIAtom** aAtom)
+{
+ NS_ADDREF(*aAtom = mName);
+
+ return NS_OK;
+}
+#endif
diff --git a/dom/xslt/xpath/txXPathNode.h b/dom/xslt/xpath/txXPathNode.h
new file mode 100644
index 000000000..53b6b6d84
--- /dev/null
+++ b/dom/xslt/xpath/txXPathNode.h
@@ -0,0 +1,136 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+#ifndef txXPathNode_h__
+#define txXPathNode_h__
+
+#include "nsAutoPtr.h"
+#include "nsIContent.h"
+#include "nsIDocument.h"
+#include "nsIDOMNode.h"
+#include "nsNameSpaceManager.h"
+#include "nsContentUtils.h" // For NameSpaceManager().
+
+typedef nsIDOMNode txXPathNodeType;
+
+class txXPathNode
+{
+public:
+ bool operator==(const txXPathNode& aNode) const;
+ bool operator!=(const txXPathNode& aNode) const
+ {
+ return !(*this == aNode);
+ }
+ ~txXPathNode();
+
+private:
+ friend class txNodeSet;
+ friend class txXPathNativeNode;
+ friend class txXPathNodeUtils;
+ friend class txXPathTreeWalker;
+
+ txXPathNode(const txXPathNode& aNode);
+
+ explicit txXPathNode(nsIDocument* aDocument) : mNode(aDocument),
+ mRefCountRoot(0),
+ mIndex(eDocument)
+ {
+ MOZ_COUNT_CTOR(txXPathNode);
+ }
+ txXPathNode(nsINode *aNode, uint32_t aIndex, nsINode *aRoot)
+ : mNode(aNode),
+ mRefCountRoot(aRoot ? 1 : 0),
+ mIndex(aIndex)
+ {
+ MOZ_COUNT_CTOR(txXPathNode);
+ if (aRoot) {
+ NS_ADDREF(aRoot);
+ }
+ }
+
+ static nsINode *RootOf(nsINode *aNode)
+ {
+ nsINode *ancestor, *root = aNode;
+ while ((ancestor = root->GetParentNode())) {
+ root = ancestor;
+ }
+ return root;
+ }
+ nsINode *Root() const
+ {
+ return RootOf(mNode);
+ }
+ nsINode *GetRootToAddRef() const
+ {
+ return mRefCountRoot ? Root() : nullptr;
+ }
+
+ bool isDocument() const
+ {
+ return mIndex == eDocument;
+ }
+ bool isContent() const
+ {
+ return mIndex == eContent;
+ }
+ bool isAttribute() const
+ {
+ return mIndex != eDocument && mIndex != eContent;
+ }
+
+ nsIContent* Content() const
+ {
+ NS_ASSERTION(isContent() || isAttribute(), "wrong type");
+ return static_cast<nsIContent*>(mNode);
+ }
+ nsIDocument* Document() const
+ {
+ NS_ASSERTION(isDocument(), "wrong type");
+ return static_cast<nsIDocument*>(mNode);
+ }
+
+ enum PositionType
+ {
+ eDocument = (1 << 30),
+ eContent = eDocument - 1
+ };
+
+ nsINode* mNode;
+ uint32_t mRefCountRoot : 1;
+ uint32_t mIndex : 31;
+};
+
+class txNamespaceManager
+{
+public:
+ static int32_t getNamespaceID(const nsAString& aNamespaceURI);
+ static nsresult getNamespaceURI(const int32_t aID, nsAString& aResult);
+};
+
+/* static */
+inline int32_t
+txNamespaceManager::getNamespaceID(const nsAString& aNamespaceURI)
+{
+ int32_t namespaceID = kNameSpaceID_Unknown;
+ nsContentUtils::NameSpaceManager()->
+ RegisterNameSpace(aNamespaceURI, namespaceID);
+ return namespaceID;
+}
+
+/* static */
+inline nsresult
+txNamespaceManager::getNamespaceURI(const int32_t aID, nsAString& aResult)
+{
+ return nsContentUtils::NameSpaceManager()->
+ GetNameSpaceURI(aID, aResult);
+}
+
+inline bool
+txXPathNode::operator==(const txXPathNode& aNode) const
+{
+ return mIndex == aNode.mIndex && mNode == aNode.mNode;
+}
+
+#endif /* txXPathNode_h__ */
diff --git a/dom/xslt/xpath/txXPathObjectAdaptor.h b/dom/xslt/xpath/txXPathObjectAdaptor.h
new file mode 100644
index 000000000..1df5abb78
--- /dev/null
+++ b/dom/xslt/xpath/txXPathObjectAdaptor.h
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+#ifndef txXPathObjectAdaptor_h__
+#define txXPathObjectAdaptor_h__
+
+#include "txExprResult.h"
+#include "txINodeSet.h"
+#include "txIXPathObject.h"
+
+/**
+ * Implements an XPCOM wrapper around XPath data types boolean, number, string,
+ * or nodeset.
+ */
+
+class txXPathObjectAdaptor : public txIXPathObject
+{
+public:
+ explicit txXPathObjectAdaptor(txAExprResult* aValue) : mValue(aValue)
+ {
+ NS_ASSERTION(aValue,
+ "Don't create a txXPathObjectAdaptor if you don't have a "
+ "txAExprResult");
+ }
+
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHOD_(txAExprResult*) GetResult() override
+ {
+ return mValue;
+ }
+
+protected:
+ txXPathObjectAdaptor() : mValue(nullptr)
+ {
+ }
+
+ virtual ~txXPathObjectAdaptor() {}
+
+ RefPtr<txAExprResult> mValue;
+};
+
+#endif // txXPathObjectAdaptor_h__
diff --git a/dom/xslt/xpath/txXPathOptimizer.cpp b/dom/xslt/xpath/txXPathOptimizer.cpp
new file mode 100644
index 000000000..756dd253d
--- /dev/null
+++ b/dom/xslt/xpath/txXPathOptimizer.cpp
@@ -0,0 +1,282 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "mozilla/Assertions.h"
+#include "txXPathOptimizer.h"
+#include "txExprResult.h"
+#include "nsIAtom.h"
+#include "nsGkAtoms.h"
+#include "txXPathNode.h"
+#include "txExpr.h"
+#include "txIXPathContext.h"
+
+class txEarlyEvalContext : public txIEvalContext
+{
+public:
+ explicit txEarlyEvalContext(txResultRecycler* aRecycler)
+ : mRecycler(aRecycler)
+ {
+ }
+
+ // txIEvalContext
+ nsresult getVariable(int32_t aNamespace, nsIAtom* aLName,
+ txAExprResult*& aResult)
+ {
+ MOZ_CRASH("shouldn't depend on this context");
+ }
+ bool isStripSpaceAllowed(const txXPathNode& aNode)
+ {
+ MOZ_CRASH("shouldn't depend on this context");
+ }
+ void* getPrivateContext()
+ {
+ MOZ_CRASH("shouldn't depend on this context");
+ }
+ txResultRecycler* recycler()
+ {
+ return mRecycler;
+ }
+ void receiveError(const nsAString& aMsg, nsresult aRes)
+ {
+ }
+ const txXPathNode& getContextNode()
+ {
+ MOZ_CRASH("shouldn't depend on this context");
+ }
+ uint32_t size()
+ {
+ MOZ_CRASH("shouldn't depend on this context");
+ }
+ uint32_t position()
+ {
+ MOZ_CRASH("shouldn't depend on this context");
+ }
+
+private:
+ txResultRecycler* mRecycler;
+};
+
+
+nsresult
+txXPathOptimizer::optimize(Expr* aInExpr, Expr** aOutExpr)
+{
+ *aOutExpr = nullptr;
+ nsresult rv = NS_OK;
+
+ // First check if the expression will produce the same result
+ // under any context.
+ Expr::ExprType exprType = aInExpr->getType();
+ if (exprType != Expr::LITERAL_EXPR &&
+ !aInExpr->isSensitiveTo(Expr::ANY_CONTEXT)) {
+ RefPtr<txResultRecycler> recycler = new txResultRecycler;
+ txEarlyEvalContext context(recycler);
+ RefPtr<txAExprResult> exprRes;
+
+ // Don't throw if this fails since it could be that the expression
+ // is or contains an error-expression.
+ rv = aInExpr->evaluate(&context, getter_AddRefs(exprRes));
+ if (NS_SUCCEEDED(rv)) {
+ *aOutExpr = new txLiteralExpr(exprRes);
+ }
+
+ return NS_OK;
+ }
+
+ // Then optimize sub expressions
+ uint32_t i = 0;
+ Expr* subExpr;
+ while ((subExpr = aInExpr->getSubExprAt(i))) {
+ Expr* newExpr = nullptr;
+ rv = optimize(subExpr, &newExpr);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (newExpr) {
+ delete subExpr;
+ aInExpr->setSubExprAt(i, newExpr);
+ }
+
+ ++i;
+ }
+
+ // Finally see if current expression can be optimized
+ switch (exprType) {
+ case Expr::LOCATIONSTEP_EXPR:
+ return optimizeStep(aInExpr, aOutExpr);
+
+ case Expr::PATH_EXPR:
+ return optimizePath(aInExpr, aOutExpr);
+
+ case Expr::UNION_EXPR:
+ return optimizeUnion(aInExpr, aOutExpr);
+
+ default:
+ break;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+txXPathOptimizer::optimizeStep(Expr* aInExpr, Expr** aOutExpr)
+{
+ LocationStep* step = static_cast<LocationStep*>(aInExpr);
+
+ if (step->getAxisIdentifier() == LocationStep::ATTRIBUTE_AXIS) {
+ // Test for @foo type steps.
+ txNameTest* nameTest = nullptr;
+ if (!step->getSubExprAt(0) &&
+ step->getNodeTest()->getType() == txNameTest::NAME_TEST &&
+ (nameTest = static_cast<txNameTest*>(step->getNodeTest()))->
+ mLocalName != nsGkAtoms::_asterisk) {
+
+ *aOutExpr = new txNamedAttributeStep(nameTest->mNamespace,
+ nameTest->mPrefix,
+ nameTest->mLocalName);
+ return NS_OK; // return since we no longer have a step-object.
+ }
+ }
+
+ // Test for predicates that can be combined into the nodetest
+ Expr* pred;
+ while ((pred = step->getSubExprAt(0)) &&
+ !pred->canReturnType(Expr::NUMBER_RESULT) &&
+ !pred->isSensitiveTo(Expr::NODESET_CONTEXT)) {
+ txNodeTest* predTest = new txPredicatedNodeTest(step->getNodeTest(), pred);
+ step->dropFirst();
+ step->setNodeTest(predTest);
+ }
+
+ return NS_OK;
+}
+
+nsresult
+txXPathOptimizer::optimizePath(Expr* aInExpr, Expr** aOutExpr)
+{
+ PathExpr* path = static_cast<PathExpr*>(aInExpr);
+
+ uint32_t i;
+ Expr* subExpr;
+ // look for steps like "//foo" that can be turned into "/descendant::foo"
+ // and "//." that can be turned into "/descendant-or-self::node()"
+ for (i = 0; (subExpr = path->getSubExprAt(i)); ++i) {
+ if (path->getPathOpAt(i) == PathExpr::DESCENDANT_OP &&
+ subExpr->getType() == Expr::LOCATIONSTEP_EXPR &&
+ !subExpr->getSubExprAt(0)) {
+ LocationStep* step = static_cast<LocationStep*>(subExpr);
+ if (step->getAxisIdentifier() == LocationStep::CHILD_AXIS) {
+ step->setAxisIdentifier(LocationStep::DESCENDANT_AXIS);
+ path->setPathOpAt(i, PathExpr::RELATIVE_OP);
+ }
+ else if (step->getAxisIdentifier() == LocationStep::SELF_AXIS) {
+ step->setAxisIdentifier(LocationStep::DESCENDANT_OR_SELF_AXIS);
+ path->setPathOpAt(i, PathExpr::RELATIVE_OP);
+ }
+ }
+ }
+
+ // look for expressions that start with a "./"
+ subExpr = path->getSubExprAt(0);
+ LocationStep* step;
+ if (subExpr->getType() == Expr::LOCATIONSTEP_EXPR &&
+ path->getSubExprAt(1) &&
+ path->getPathOpAt(1) != PathExpr::DESCENDANT_OP) {
+ step = static_cast<LocationStep*>(subExpr);
+ if (step->getAxisIdentifier() == LocationStep::SELF_AXIS &&
+ !step->getSubExprAt(0)) {
+ txNodeTest* test = step->getNodeTest();
+ txNodeTypeTest* typeTest;
+ if (test->getType() == txNodeTest::NODETYPE_TEST &&
+ (typeTest = static_cast<txNodeTypeTest*>(test))->
+ getNodeTestType() == txNodeTypeTest::NODE_TYPE) {
+ // We have a '.' as first step followed by a single '/'.
+
+ // Check if there are only two steps. If so, return the second
+ // as resulting expression.
+ if (!path->getSubExprAt(2)) {
+ *aOutExpr = path->getSubExprAt(1);
+ path->setSubExprAt(1, nullptr);
+
+ return NS_OK;
+ }
+
+ // Just delete the '.' step and leave the rest of the PathExpr
+ path->deleteExprAt(0);
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult
+txXPathOptimizer::optimizeUnion(Expr* aInExpr, Expr** aOutExpr)
+{
+ UnionExpr* uni = static_cast<UnionExpr*>(aInExpr);
+
+ // Check for expressions like "foo | bar" and
+ // "descendant::foo | descendant::bar"
+
+ nsresult rv;
+ uint32_t current;
+ Expr* subExpr;
+ for (current = 0; (subExpr = uni->getSubExprAt(current)); ++current) {
+ if (subExpr->getType() != Expr::LOCATIONSTEP_EXPR ||
+ subExpr->getSubExprAt(0)) {
+ continue;
+ }
+
+ LocationStep* currentStep = static_cast<LocationStep*>(subExpr);
+ LocationStep::LocationStepType axis = currentStep->getAxisIdentifier();
+
+ txUnionNodeTest* unionTest = nullptr;
+
+ // Check if there are any other steps with the same axis and merge
+ // them with currentStep
+ uint32_t i;
+ for (i = current + 1; (subExpr = uni->getSubExprAt(i)); ++i) {
+ if (subExpr->getType() != Expr::LOCATIONSTEP_EXPR ||
+ subExpr->getSubExprAt(0)) {
+ continue;
+ }
+
+ LocationStep* step = static_cast<LocationStep*>(subExpr);
+ if (step->getAxisIdentifier() != axis) {
+ continue;
+ }
+
+ // Create a txUnionNodeTest if needed
+ if (!unionTest) {
+ nsAutoPtr<txNodeTest> owner(unionTest = new txUnionNodeTest);
+ rv = unionTest->addNodeTest(currentStep->getNodeTest());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ currentStep->setNodeTest(unionTest);
+ owner.forget();
+ }
+
+ // Merge the nodetest into the union
+ rv = unionTest->addNodeTest(step->getNodeTest());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ step->setNodeTest(nullptr);
+
+ // Remove the step from the UnionExpr
+ uni->deleteExprAt(i);
+ --i;
+ }
+
+ // Check if all expressions were merged into a single step. If so,
+ // return the step as the new expression.
+ if (unionTest && current == 0 && !uni->getSubExprAt(1)) {
+ // Make sure the step doesn't get deleted when the UnionExpr is
+ uni->setSubExprAt(0, nullptr);
+ *aOutExpr = currentStep;
+
+ // Return right away since we no longer have a union
+ return NS_OK;
+ }
+ }
+
+ return NS_OK;
+}
diff --git a/dom/xslt/xpath/txXPathOptimizer.h b/dom/xslt/xpath/txXPathOptimizer.h
new file mode 100644
index 000000000..a933ac3ad
--- /dev/null
+++ b/dom/xslt/xpath/txXPathOptimizer.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+#ifndef txXPathOptimizer_h__
+#define txXPathOptimizer_h__
+
+#include "txCore.h"
+
+class Expr;
+
+class txXPathOptimizer
+{
+public:
+ /**
+ * Optimize the given expression.
+ * @param aInExpr Expression to optimize.
+ * @param aOutExpr Resulting expression, null if optimization didn't
+ * result in a new expression.
+ */
+ nsresult optimize(Expr* aInExpr, Expr** aOutExpr);
+
+private:
+ // Helper methods for optimizing specific classes
+ nsresult optimizeStep(Expr* aInExpr, Expr** aOutExpr);
+ nsresult optimizePath(Expr* aInExpr, Expr** aOutExpr);
+ nsresult optimizeUnion(Expr* aInExpr, Expr** aOutExpr);
+};
+
+#endif
diff --git a/dom/xslt/xpath/txXPathTreeWalker.h b/dom/xslt/xpath/txXPathTreeWalker.h
new file mode 100644
index 000000000..99a07ffe7
--- /dev/null
+++ b/dom/xslt/xpath/txXPathTreeWalker.h
@@ -0,0 +1,287 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+#ifndef txXPathTreeWalker_h__
+#define txXPathTreeWalker_h__
+
+#include "txCore.h"
+#include "txXPathNode.h"
+#include "nsIContentInlines.h"
+#include "nsTArray.h"
+
+class nsIAtom;
+class nsIDOMDocument;
+
+class txUint32Array : public nsTArray<uint32_t>
+{
+public:
+ bool AppendValue(uint32_t aValue)
+ {
+ return AppendElement(aValue) != nullptr;
+ }
+ bool RemoveValueAt(uint32_t aIndex)
+ {
+ if (aIndex < Length()) {
+ RemoveElementAt(aIndex);
+ }
+ return true;
+ }
+ uint32_t ValueAt(uint32_t aIndex) const
+ {
+ return (aIndex < Length()) ? ElementAt(aIndex) : 0;
+ }
+};
+
+class txXPathTreeWalker
+{
+public:
+ txXPathTreeWalker(const txXPathTreeWalker& aOther);
+ explicit txXPathTreeWalker(const txXPathNode& aNode);
+
+ bool getAttr(nsIAtom* aLocalName, int32_t aNSID, nsAString& aValue) const;
+ int32_t getNamespaceID() const;
+ uint16_t getNodeType() const;
+ void appendNodeValue(nsAString& aResult) const;
+ void getNodeName(nsAString& aName) const;
+
+ void moveTo(const txXPathTreeWalker& aWalker);
+
+ void moveToRoot();
+ bool moveToParent();
+ bool moveToElementById(const nsAString& aID);
+ bool moveToFirstAttribute();
+ bool moveToNextAttribute();
+ bool moveToNamedAttribute(nsIAtom* aLocalName, int32_t aNSID);
+ bool moveToFirstChild();
+ bool moveToLastChild();
+ bool moveToNextSibling();
+ bool moveToPreviousSibling();
+
+ bool isOnNode(const txXPathNode& aNode) const;
+
+ const txXPathNode& getCurrentPosition() const;
+
+private:
+ txXPathNode mPosition;
+
+ bool moveToValidAttribute(uint32_t aStartIndex);
+ bool moveToSibling(int32_t aDir);
+
+ uint32_t mCurrentIndex;
+ txUint32Array mDescendants;
+};
+
+class txXPathNodeUtils
+{
+public:
+ static bool getAttr(const txXPathNode& aNode, nsIAtom* aLocalName,
+ int32_t aNSID, nsAString& aValue);
+ static already_AddRefed<nsIAtom> getLocalName(const txXPathNode& aNode);
+ static nsIAtom* getPrefix(const txXPathNode& aNode);
+ static void getLocalName(const txXPathNode& aNode, nsAString& aLocalName);
+ static void getNodeName(const txXPathNode& aNode,
+ nsAString& aName);
+ static int32_t getNamespaceID(const txXPathNode& aNode);
+ static void getNamespaceURI(const txXPathNode& aNode, nsAString& aURI);
+ static uint16_t getNodeType(const txXPathNode& aNode);
+ static void appendNodeValue(const txXPathNode& aNode, nsAString& aResult);
+ static bool isWhitespace(const txXPathNode& aNode);
+ static txXPathNode* getOwnerDocument(const txXPathNode& aNode);
+ static int32_t getUniqueIdentifier(const txXPathNode& aNode);
+ static nsresult getXSLTId(const txXPathNode& aNode,
+ const txXPathNode& aBase, nsAString& aResult);
+ static void release(txXPathNode* aNode);
+ static nsresult getBaseURI(const txXPathNode& aNode, nsAString& aURI);
+ static int comparePosition(const txXPathNode& aNode,
+ const txXPathNode& aOtherNode);
+ static bool localNameEquals(const txXPathNode& aNode,
+ nsIAtom* aLocalName);
+ static bool isRoot(const txXPathNode& aNode);
+ static bool isElement(const txXPathNode& aNode);
+ static bool isAttribute(const txXPathNode& aNode);
+ static bool isProcessingInstruction(const txXPathNode& aNode);
+ static bool isComment(const txXPathNode& aNode);
+ static bool isText(const txXPathNode& aNode);
+ static inline bool isHTMLElementInHTMLDocument(const txXPathNode& aNode)
+ {
+ if (!aNode.isContent()) {
+ return false;
+ }
+ nsIContent* content = aNode.Content();
+ return content->IsHTMLElement() && content->IsInHTMLDocument();
+ }
+};
+
+class txXPathNativeNode
+{
+public:
+ static txXPathNode* createXPathNode(nsINode* aNode,
+ bool aKeepRootAlive = false);
+ static txXPathNode* createXPathNode(nsIDOMNode* aNode,
+ bool aKeepRootAlive = false)
+ {
+ nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
+ return createXPathNode(node, aKeepRootAlive);
+ }
+ static txXPathNode* createXPathNode(nsIContent* aContent,
+ bool aKeepRootAlive = false);
+ static txXPathNode* createXPathNode(nsIDOMDocument* aDocument);
+ static nsINode* getNode(const txXPathNode& aNode);
+ static nsresult getNode(const txXPathNode& aNode, nsIDOMNode** aResult)
+ {
+ return CallQueryInterface(getNode(aNode), aResult);
+ }
+ static nsIContent* getContent(const txXPathNode& aNode);
+ static nsIDocument* getDocument(const txXPathNode& aNode);
+ static void addRef(const txXPathNode& aNode)
+ {
+ NS_ADDREF(aNode.mNode);
+ }
+ static void release(const txXPathNode& aNode)
+ {
+ nsINode *node = aNode.mNode;
+ NS_RELEASE(node);
+ }
+};
+
+inline const txXPathNode&
+txXPathTreeWalker::getCurrentPosition() const
+{
+ return mPosition;
+}
+
+inline bool
+txXPathTreeWalker::getAttr(nsIAtom* aLocalName, int32_t aNSID,
+ nsAString& aValue) const
+{
+ return txXPathNodeUtils::getAttr(mPosition, aLocalName, aNSID, aValue);
+}
+
+inline int32_t
+txXPathTreeWalker::getNamespaceID() const
+{
+ return txXPathNodeUtils::getNamespaceID(mPosition);
+}
+
+inline void
+txXPathTreeWalker::appendNodeValue(nsAString& aResult) const
+{
+ txXPathNodeUtils::appendNodeValue(mPosition, aResult);
+}
+
+inline void
+txXPathTreeWalker::getNodeName(nsAString& aName) const
+{
+ txXPathNodeUtils::getNodeName(mPosition, aName);
+}
+
+inline void
+txXPathTreeWalker::moveTo(const txXPathTreeWalker& aWalker)
+{
+ nsINode *root = nullptr;
+ if (mPosition.mRefCountRoot) {
+ root = mPosition.Root();
+ }
+ mPosition.mIndex = aWalker.mPosition.mIndex;
+ mPosition.mRefCountRoot = aWalker.mPosition.mRefCountRoot;
+ mPosition.mNode = aWalker.mPosition.mNode;
+ nsINode *newRoot = nullptr;
+ if (mPosition.mRefCountRoot) {
+ newRoot = mPosition.Root();
+ }
+ if (root != newRoot) {
+ NS_IF_ADDREF(newRoot);
+ NS_IF_RELEASE(root);
+ }
+
+ mCurrentIndex = aWalker.mCurrentIndex;
+ mDescendants.Clear();
+}
+
+inline bool
+txXPathTreeWalker::isOnNode(const txXPathNode& aNode) const
+{
+ return (mPosition == aNode);
+}
+
+/* static */
+inline int32_t
+txXPathNodeUtils::getUniqueIdentifier(const txXPathNode& aNode)
+{
+ NS_PRECONDITION(!aNode.isAttribute(),
+ "Not implemented for attributes.");
+ return NS_PTR_TO_INT32(aNode.mNode);
+}
+
+/* static */
+inline void
+txXPathNodeUtils::release(txXPathNode* aNode)
+{
+ NS_RELEASE(aNode->mNode);
+}
+
+/* static */
+inline bool
+txXPathNodeUtils::localNameEquals(const txXPathNode& aNode,
+ nsIAtom* aLocalName)
+{
+ if (aNode.isContent() &&
+ aNode.Content()->IsElement()) {
+ return aNode.Content()->NodeInfo()->Equals(aLocalName);
+ }
+
+ nsCOMPtr<nsIAtom> localName = txXPathNodeUtils::getLocalName(aNode);
+
+ return localName == aLocalName;
+}
+
+/* static */
+inline bool
+txXPathNodeUtils::isRoot(const txXPathNode& aNode)
+{
+ return !aNode.isAttribute() && !aNode.mNode->GetParentNode();
+}
+
+/* static */
+inline bool
+txXPathNodeUtils::isElement(const txXPathNode& aNode)
+{
+ return aNode.isContent() &&
+ aNode.Content()->IsElement();
+}
+
+
+/* static */
+inline bool
+txXPathNodeUtils::isAttribute(const txXPathNode& aNode)
+{
+ return aNode.isAttribute();
+}
+
+/* static */
+inline bool
+txXPathNodeUtils::isProcessingInstruction(const txXPathNode& aNode)
+{
+ return aNode.isContent() &&
+ aNode.Content()->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION);
+}
+
+/* static */
+inline bool
+txXPathNodeUtils::isComment(const txXPathNode& aNode)
+{
+ return aNode.isContent() &&
+ aNode.Content()->IsNodeOfType(nsINode::eCOMMENT);
+}
+
+/* static */
+inline bool
+txXPathNodeUtils::isText(const txXPathNode& aNode)
+{
+ return aNode.isContent() &&
+ aNode.Content()->IsNodeOfType(nsINode::eTEXT);
+}
+
+#endif /* txXPathTreeWalker_h__ */