summaryrefslogtreecommitdiffstats
path: root/dom/xslt
diff options
context:
space:
mode:
Diffstat (limited to 'dom/xslt')
-rw-r--r--dom/xslt/base/moz.build26
-rw-r--r--dom/xslt/base/txCore.h51
-rw-r--r--dom/xslt/base/txDouble.cpp215
-rw-r--r--dom/xslt/base/txErrorObserver.h41
-rw-r--r--dom/xslt/base/txExpandedName.cpp41
-rw-r--r--dom/xslt/base/txExpandedName.h70
-rw-r--r--dom/xslt/base/txExpandedNameMap.cpp104
-rw-r--r--dom/xslt/base/txExpandedNameMap.h203
-rw-r--r--dom/xslt/base/txList.cpp278
-rw-r--r--dom/xslt/base/txList.h161
-rw-r--r--dom/xslt/base/txLog.h24
-rw-r--r--dom/xslt/base/txNamespaceMap.cpp98
-rw-r--r--dom/xslt/base/txNamespaceMap.h43
-rw-r--r--dom/xslt/base/txOwningArray.h32
-rw-r--r--dom/xslt/base/txStack.h122
-rw-r--r--dom/xslt/base/txStringUtils.h34
-rw-r--r--dom/xslt/base/txURIUtils.cpp78
-rw-r--r--dom/xslt/base/txURIUtils.h37
-rw-r--r--dom/xslt/crashtests/1089049.html3
-rw-r--r--dom/xslt/crashtests/111994.xml5
-rw-r--r--dom/xslt/crashtests/111994.xsl13
-rw-r--r--dom/xslt/crashtests/1205163.xml6
-rw-r--r--dom/xslt/crashtests/1205163.xsl11
-rw-r--r--dom/xslt/crashtests/1243337.xml3
-rw-r--r--dom/xslt/crashtests/1243337.xsl6
-rw-r--r--dom/xslt/crashtests/182460-select.xml3
-rw-r--r--dom/xslt/crashtests/182460-selects.xsl5
-rw-r--r--dom/xslt/crashtests/182460-table.xhtml9
-rw-r--r--dom/xslt/crashtests/226425.xml4
-rw-r--r--dom/xslt/crashtests/226425.xsl10
-rw-r--r--dom/xslt/crashtests/406106-1.html18
-rw-r--r--dom/xslt/crashtests/483444.xml19
-rw-r--r--dom/xslt/crashtests/485217.xml8
-rw-r--r--dom/xslt/crashtests/485217.xsl18
-rw-r--r--dom/xslt/crashtests/485286.xml12
-rw-r--r--dom/xslt/crashtests/527558_1.xml17
-rw-r--r--dom/xslt/crashtests/528300.xml22
-rw-r--r--dom/xslt/crashtests/528488.xml19
-rw-r--r--dom/xslt/crashtests/528963.xml22
-rw-r--r--dom/xslt/crashtests/545927.html28
-rw-r--r--dom/xslt/crashtests/601543.html4
-rw-r--r--dom/xslt/crashtests/602115.html22
-rw-r--r--dom/xslt/crashtests/603844.html32
-rw-r--r--dom/xslt/crashtests/667315.xml11
-rw-r--r--dom/xslt/crashtests/91332.xml10
-rw-r--r--dom/xslt/crashtests/91332.xsl21
-rw-r--r--dom/xslt/crashtests/crashtests.list20
-rw-r--r--dom/xslt/moz.build31
-rw-r--r--dom/xslt/nsIDocumentTransformer.h65
-rw-r--r--dom/xslt/nsIXSLTProcessor.idl94
-rw-r--r--dom/xslt/nsIXSLTProcessorPrivate.idl23
-rw-r--r--dom/xslt/tests/XSLTMark/XSLTMark-static.js49
-rw-r--r--dom/xslt/tests/XSLTMark/XSLTMark-test.js46
-rw-r--r--dom/xslt/tests/XSLTMark/XSLTMark-view.js175
-rw-r--r--dom/xslt/tests/XSLTMark/XSLTMark.css8
-rw-r--r--dom/xslt/tests/XSLTMark/XSLTMark.xul52
-rw-r--r--dom/xslt/tests/buster/DiffDOM.js103
-rw-r--r--dom/xslt/tests/buster/DumpDOM.js85
-rw-r--r--dom/xslt/tests/buster/ReadMe22
-rw-r--r--dom/xslt/tests/buster/buster-files.js81
-rw-r--r--dom/xslt/tests/buster/buster-handlers.js37
-rw-r--r--dom/xslt/tests/buster/buster-statics.js87
-rw-r--r--dom/xslt/tests/buster/buster-test.js356
-rw-r--r--dom/xslt/tests/buster/buster-view.js193
-rw-r--r--dom/xslt/tests/buster/buster.css20
-rw-r--r--dom/xslt/tests/buster/buster.xul196
-rw-r--r--dom/xslt/tests/buster/helper/generate-rdf.pl95
-rw-r--r--dom/xslt/tests/buster/install.js17
-rw-r--r--dom/xslt/tests/buster/jar.mn18
-rw-r--r--dom/xslt/tests/buster/result-inspector.xul37
-rw-r--r--dom/xslt/tests/buster/result-view.css16
-rw-r--r--dom/xslt/tests/buster/result-view.js105
-rw-r--r--dom/xslt/tests/buster/result-view.xul46
-rw-r--r--dom/xslt/tests/buster/xslt-qa-overlay.js10
-rw-r--r--dom/xslt/tests/buster/xslt-qa-overlay.xul12
-rw-r--r--dom/xslt/tests/mochitest/file_bug1135764.xml3
-rw-r--r--dom/xslt/tests/mochitest/file_bug1135764.xsl19
-rw-r--r--dom/xslt/tests/mochitest/mochitest.ini20
-rw-r--r--dom/xslt/tests/mochitest/test_bug1072116.html37
-rw-r--r--dom/xslt/tests/mochitest/test_bug1135764.html53
-rw-r--r--dom/xslt/tests/mochitest/test_bug319374.xhtml109
-rw-r--r--dom/xslt/tests/mochitest/test_bug427060.html49
-rw-r--r--dom/xslt/tests/mochitest/test_bug440974.html46
-rw-r--r--dom/xslt/tests/mochitest/test_bug453441.html57
-rw-r--r--dom/xslt/tests/mochitest/test_bug468208.html35
-rw-r--r--dom/xslt/tests/mochitest/test_bug511487.html59
-rw-r--r--dom/xslt/tests/mochitest/test_bug551412.html48
-rw-r--r--dom/xslt/tests/mochitest/test_bug551654.html49
-rw-r--r--dom/xslt/tests/mochitest/test_bug566629.html70
-rw-r--r--dom/xslt/tests/mochitest/test_bug566629.xhtml73
-rw-r--r--dom/xslt/tests/mochitest/test_bug603159.html54
-rw-r--r--dom/xslt/tests/mochitest/test_bug616774.html28
-rw-r--r--dom/xslt/tests/mochitest/test_bug667315.html46
-rw-r--r--dom/xslt/tests/mochitest/test_exslt_regex.html60
-rw-r--r--dom/xslt/tests/mochitest/test_parameter.html31
-rw-r--r--dom/xslt/txIEXSLTRegExFunctions.idl21
-rw-r--r--dom/xslt/txIFunctionEvaluationContext.idl18
-rw-r--r--dom/xslt/txINodeSet.idl16
-rw-r--r--dom/xslt/txIXPathObject.idl18
-rw-r--r--dom/xslt/xml/moz.build19
-rw-r--r--dom/xslt/xml/txXMLParser.cpp62
-rw-r--r--dom/xslt/xml/txXMLParser.h26
-rw-r--r--dom/xslt/xml/txXMLUtils.cpp181
-rw-r--r--dom/xslt/xml/txXMLUtils.h82
-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
-rw-r--r--dom/xslt/xslt/moz.build62
-rw-r--r--dom/xslt/xslt/txBufferingHandler.cpp464
-rw-r--r--dom/xslt/xslt/txBufferingHandler.h47
-rw-r--r--dom/xslt/xslt/txCurrentFunctionCall.cpp65
-rw-r--r--dom/xslt/xslt/txDocumentFunctionCall.cpp167
-rw-r--r--dom/xslt/xslt/txEXSLTFunctions.cpp734
-rw-r--r--dom/xslt/xslt/txEXSLTRegExFunctions.js69
-rw-r--r--dom/xslt/xslt/txEXSLTRegExFunctions.manifest3
-rw-r--r--dom/xslt/xslt/txExecutionState.cpp561
-rw-r--r--dom/xslt/xslt/txExecutionState.h181
-rw-r--r--dom/xslt/xslt/txFormatNumberFunctionCall.cpp430
-rw-r--r--dom/xslt/xslt/txGenerateIdFunctionCall.cpp114
-rw-r--r--dom/xslt/xslt/txInstructions.cpp944
-rw-r--r--dom/xslt/xslt/txInstructions.h388
-rw-r--r--dom/xslt/xslt/txKey.h212
-rw-r--r--dom/xslt/xslt/txKeyFunctionCall.cpp395
-rw-r--r--dom/xslt/xslt/txMozillaStylesheetCompiler.cpp718
-rw-r--r--dom/xslt/xslt/txMozillaTextOutput.cpp256
-rw-r--r--dom/xslt/xslt/txMozillaTextOutput.h43
-rw-r--r--dom/xslt/xslt/txMozillaXMLOutput.cpp1075
-rw-r--r--dom/xslt/xslt/txMozillaXMLOutput.h134
-rw-r--r--dom/xslt/xslt/txMozillaXSLTProcessor.cpp1591
-rw-r--r--dom/xslt/xslt/txMozillaXSLTProcessor.h196
-rw-r--r--dom/xslt/xslt/txNodeSorter.cpp260
-rw-r--r--dom/xslt/xslt/txNodeSorter.h58
-rw-r--r--dom/xslt/xslt/txOutputFormat.cpp132
-rw-r--r--dom/xslt/xslt/txOutputFormat.h73
-rw-r--r--dom/xslt/xslt/txPatternOptimizer.cpp76
-rw-r--r--dom/xslt/xslt/txPatternOptimizer.h32
-rw-r--r--dom/xslt/xslt/txPatternParser.cpp307
-rw-r--r--dom/xslt/xslt/txPatternParser.h37
-rw-r--r--dom/xslt/xslt/txRtfHandler.cpp79
-rw-r--r--dom/xslt/xslt/txRtfHandler.h48
-rw-r--r--dom/xslt/xslt/txStylesheet.cpp591
-rw-r--r--dom/xslt/xslt/txStylesheet.h204
-rw-r--r--dom/xslt/xslt/txStylesheetCompileHandlers.cpp2944
-rw-r--r--dom/xslt/xslt/txStylesheetCompileHandlers.h56
-rw-r--r--dom/xslt/xslt/txStylesheetCompiler.cpp1135
-rw-r--r--dom/xslt/xslt/txStylesheetCompiler.h267
-rw-r--r--dom/xslt/xslt/txTextHandler.cpp90
-rw-r--r--dom/xslt/xslt/txTextHandler.h26
-rw-r--r--dom/xslt/xslt/txToplevelItems.cpp58
-rw-r--r--dom/xslt/xslt/txToplevelItems.h135
-rw-r--r--dom/xslt/xslt/txUnknownHandler.cpp201
-rw-r--r--dom/xslt/xslt/txUnknownHandler.h39
-rw-r--r--dom/xslt/xslt/txVariableMap.h78
-rw-r--r--dom/xslt/xslt/txXMLEventHandler.h198
-rw-r--r--dom/xslt/xslt/txXPathResultComparator.cpp222
-rw-r--r--dom/xslt/xslt/txXPathResultComparator.h96
-rw-r--r--dom/xslt/xslt/txXSLTEnvironmentFunctionCall.cpp138
-rw-r--r--dom/xslt/xslt/txXSLTFunctions.h161
-rw-r--r--dom/xslt/xslt/txXSLTNumber.cpp728
-rw-r--r--dom/xslt/xslt/txXSLTNumber.h73
-rw-r--r--dom/xslt/xslt/txXSLTNumberCounters.cpp214
-rw-r--r--dom/xslt/xslt/txXSLTPatterns.cpp526
-rw-r--r--dom/xslt/xslt/txXSLTPatterns.h247
-rw-r--r--dom/xslt/xslt/txXSLTProcessor.cpp54
-rw-r--r--dom/xslt/xslt/txXSLTProcessor.h28
218 files changed, 34876 insertions, 0 deletions
diff --git a/dom/xslt/base/moz.build b/dom/xslt/base/moz.build
new file mode 100644
index 000000000..7d9cd70fa
--- /dev/null
+++ b/dom/xslt/base/moz.build
@@ -0,0 +1,26 @@
+# -*- 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/.
+
+UNIFIED_SOURCES += [
+ 'txDouble.cpp',
+ 'txExpandedName.cpp',
+ 'txExpandedNameMap.cpp',
+ 'txList.cpp',
+ 'txNamespaceMap.cpp',
+ 'txURIUtils.cpp',
+]
+
+LOCAL_INCLUDES += [
+ '..',
+ '../xml',
+ '../xpath',
+ '../xslt',
+]
+
+FINAL_LIBRARY = 'xul'
+
+if CONFIG['GNU_CXX']:
+ CXXFLAGS += ['-Wno-error=shadow']
diff --git a/dom/xslt/base/txCore.h b/dom/xslt/base/txCore.h
new file mode 100644
index 000000000..3bce3c089
--- /dev/null
+++ b/dom/xslt/base/txCore.h
@@ -0,0 +1,51 @@
+/* -*- 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 __txCore_h__
+#define __txCore_h__
+
+#include "nscore.h"
+#include "nsDebug.h"
+#include "nsISupportsImpl.h"
+
+class nsAString;
+
+class txObject
+{
+public:
+ txObject()
+ {
+ MOZ_COUNT_CTOR(txObject);
+ }
+
+ /**
+ * Deletes this txObject
+ */
+ virtual ~txObject()
+ {
+ MOZ_COUNT_DTOR(txObject);
+ }
+};
+
+/**
+ * Utility class for doubles
+ */
+class txDouble
+{
+public:
+ /**
+ * Converts the value of the given double to a string, and appends
+ * the result to the destination string.
+ */
+ static void toString(double aValue, nsAString& aDest);
+
+ /**
+ * Converts the given String to a double, if the string value does not
+ * represent a double, NaN will be returned.
+ */
+ static double toDouble(const nsAString& aStr);
+};
+
+#endif
diff --git a/dom/xslt/base/txDouble.cpp b/dom/xslt/base/txDouble.cpp
new file mode 100644
index 000000000..f52d1b885
--- /dev/null
+++ b/dom/xslt/base/txDouble.cpp
@@ -0,0 +1,215 @@
+/* -*- 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 "nsString.h"
+#include "txCore.h"
+#include "txXMLUtils.h"
+#include <math.h>
+#include <stdlib.h>
+#include <algorithm>
+#ifdef WIN32
+#include <float.h>
+#endif
+#include "prdtoa.h"
+
+/*
+ * Utility class for doubles
+ */
+
+/*
+ * Converts the given String to a double, if the String value does not
+ * represent a double, NaN will be returned
+ */
+class txStringToDouble
+{
+public:
+ typedef char16_t input_type;
+ typedef char16_t value_type;
+ txStringToDouble(): mState(eWhitestart), mSign(ePositive) {}
+
+ void
+ write(const input_type* aSource, uint32_t aSourceLength)
+ {
+ if (mState == eIllegal) {
+ return;
+ }
+ uint32_t i = 0;
+ char16_t c;
+ for ( ; i < aSourceLength; ++i) {
+ c = aSource[i];
+ switch (mState) {
+ case eWhitestart:
+ if (c == '-') {
+ mState = eDecimal;
+ mSign = eNegative;
+ }
+ else if (c >= '0' && c <= '9') {
+ mState = eDecimal;
+ mBuffer.Append((char)c);
+ }
+ else if (c == '.') {
+ mState = eMantissa;
+ mBuffer.Append((char)c);
+ }
+ else if (!XMLUtils::isWhitespace(c)) {
+ mState = eIllegal;
+ return;
+ }
+ break;
+ case eDecimal:
+ if (c >= '0' && c <= '9') {
+ mBuffer.Append((char)c);
+ }
+ else if (c == '.') {
+ mState = eMantissa;
+ mBuffer.Append((char)c);
+ }
+ else if (XMLUtils::isWhitespace(c)) {
+ mState = eWhiteend;
+ }
+ else {
+ mState = eIllegal;
+ return;
+ }
+ break;
+ case eMantissa:
+ if (c >= '0' && c <= '9') {
+ mBuffer.Append((char)c);
+ }
+ else if (XMLUtils::isWhitespace(c)) {
+ mState = eWhiteend;
+ }
+ else {
+ mState = eIllegal;
+ return;
+ }
+ break;
+ case eWhiteend:
+ if (!XMLUtils::isWhitespace(c)) {
+ mState = eIllegal;
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ double
+ getDouble()
+ {
+ if (mState == eIllegal || mBuffer.IsEmpty() ||
+ (mBuffer.Length() == 1 && mBuffer[0] == '.')) {
+ return mozilla::UnspecifiedNaN<double>();
+ }
+ return mSign*PR_strtod(mBuffer.get(), 0);
+ }
+private:
+ nsAutoCString mBuffer;
+ enum {
+ eWhitestart,
+ eDecimal,
+ eMantissa,
+ eWhiteend,
+ eIllegal
+ } mState;
+ enum {
+ eNegative = -1,
+ ePositive = 1
+ } mSign;
+};
+
+double txDouble::toDouble(const nsAString& aSrc)
+{
+ txStringToDouble sink;
+ nsAString::const_iterator fromBegin, fromEnd;
+ copy_string(aSrc.BeginReading(fromBegin), aSrc.EndReading(fromEnd), sink);
+ return sink.getDouble();
+}
+
+/*
+ * Converts the value of the given double to a String, and places
+ * The result into the destination String.
+ * @return the given dest string
+ */
+void txDouble::toString(double aValue, nsAString& aDest)
+{
+
+ // check for special cases
+
+ if (mozilla::IsNaN(aValue)) {
+ aDest.AppendLiteral("NaN");
+ return;
+ }
+ if (mozilla::IsInfinite(aValue)) {
+ if (aValue < 0)
+ aDest.Append(char16_t('-'));
+ aDest.AppendLiteral("Infinity");
+ return;
+ }
+
+ // Mantissa length is 17, so this is plenty
+ const int buflen = 20;
+ char buf[buflen];
+
+ int intDigits, sign;
+ char* endp;
+ PR_dtoa(aValue, 0, 0, &intDigits, &sign, &endp, buf, buflen - 1);
+
+ // compute length
+ int32_t length = endp - buf;
+ if (length > intDigits) {
+ // decimal point needed
+ ++length;
+ if (intDigits < 1) {
+ // leading zeros, -intDigits + 1
+ length += 1 - intDigits;
+ }
+ }
+ else {
+ // trailing zeros, total length given by intDigits
+ length = intDigits;
+ }
+ if (aValue < 0)
+ ++length;
+ // grow the string
+ uint32_t oldlength = aDest.Length();
+ if (!aDest.SetLength(oldlength + length, mozilla::fallible))
+ return; // out of memory
+ nsAString::iterator dest;
+ aDest.BeginWriting(dest).advance(int32_t(oldlength));
+ if (aValue < 0) {
+ *dest = '-'; ++dest;
+ }
+ int i;
+ // leading zeros
+ if (intDigits < 1) {
+ *dest = '0'; ++dest;
+ *dest = '.'; ++dest;
+ for (i = 0; i > intDigits; --i) {
+ *dest = '0'; ++dest;
+ }
+ }
+ // mantissa
+ int firstlen = std::min<size_t>(intDigits, endp - buf);
+ for (i = 0; i < firstlen; i++) {
+ *dest = buf[i]; ++dest;
+ }
+ if (i < endp - buf) {
+ if (i > 0) {
+ *dest = '.'; ++dest;
+ }
+ for (; i < endp - buf; i++) {
+ *dest = buf[i]; ++dest;
+ }
+ }
+ // trailing zeros
+ for (; i < intDigits; i++) {
+ *dest = '0'; ++dest;
+ }
+}
diff --git a/dom/xslt/base/txErrorObserver.h b/dom/xslt/base/txErrorObserver.h
new file mode 100644
index 000000000..3c6be8294
--- /dev/null
+++ b/dom/xslt/base/txErrorObserver.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 MITRE_ERROROBSERVER_H
+#define MITRE_ERROROBSERVER_H
+
+#include "txCore.h"
+
+/**
+ * A simple interface for observing errors
+**/
+class ErrorObserver {
+
+public:
+
+ /**
+ * Default Destructor for ErrorObserver
+ **/
+ virtual ~ErrorObserver() {};
+
+ /**
+ * Notifies this Error observer of a new error aRes
+ **/
+ virtual void receiveError(const nsAString& errorMessage, nsresult aRes) = 0;
+
+ /**
+ * Notifies this Error observer of a new error, with default
+ * error code NS_ERROR_FAILURE
+ **/
+ void receiveError(const nsAString& errorMessage)
+ {
+ receiveError(errorMessage, NS_ERROR_FAILURE);
+ }
+
+
+
+}; //-- ErrorObserver
+
+#endif
diff --git a/dom/xslt/base/txExpandedName.cpp b/dom/xslt/base/txExpandedName.cpp
new file mode 100644
index 000000000..43283e425
--- /dev/null
+++ b/dom/xslt/base/txExpandedName.cpp
@@ -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/. */
+
+#include "txExpandedName.h"
+#include "nsString.h"
+#include "nsReadableUtils.h"
+#include "txStringUtils.h"
+#include "txNamespaceMap.h"
+#include "txXMLUtils.h"
+
+nsresult
+txExpandedName::init(const nsAString& aQName, txNamespaceMap* aResolver,
+ bool aUseDefault)
+{
+ const nsAFlatString& qName = PromiseFlatString(aQName);
+ const char16_t* colon;
+ bool valid = XMLUtils::isValidQName(qName, &colon);
+ if (!valid) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (colon) {
+ nsCOMPtr<nsIAtom> prefix = NS_Atomize(Substring(qName.get(), colon));
+ int32_t namespaceID = aResolver->lookupNamespace(prefix);
+ if (namespaceID == kNameSpaceID_Unknown)
+ return NS_ERROR_FAILURE;
+ mNamespaceID = namespaceID;
+
+ const char16_t *end;
+ qName.EndReading(end);
+ mLocalName = NS_Atomize(Substring(colon + 1, end));
+ }
+ else {
+ mNamespaceID = aUseDefault ? aResolver->lookupNamespace(nullptr) :
+ kNameSpaceID_None;
+ mLocalName = NS_Atomize(aQName);
+ }
+ return NS_OK;
+}
diff --git a/dom/xslt/base/txExpandedName.h b/dom/xslt/base/txExpandedName.h
new file mode 100644
index 000000000..b49fa5134
--- /dev/null
+++ b/dom/xslt/base/txExpandedName.h
@@ -0,0 +1,70 @@
+/* -*- 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_EXPANDEDNAME_H
+#define TRANSFRMX_EXPANDEDNAME_H
+
+#include "nsCOMPtr.h"
+#include "nsIAtom.h"
+#include "mozilla/dom/NameSpaceConstants.h"
+
+class txNamespaceMap;
+
+class txExpandedName {
+public:
+ txExpandedName() : mNamespaceID(kNameSpaceID_None)
+ {
+ }
+
+ txExpandedName(int32_t aNsID,
+ nsIAtom* aLocalName) : mNamespaceID(aNsID),
+ mLocalName(aLocalName)
+ {
+ }
+
+ txExpandedName(const txExpandedName& aOther) :
+ mNamespaceID(aOther.mNamespaceID),
+ mLocalName(aOther.mLocalName)
+ {
+ }
+
+ nsresult init(const nsAString& aQName, txNamespaceMap* aResolver,
+ bool aUseDefault);
+
+ void reset()
+ {
+ mNamespaceID = kNameSpaceID_None;
+ mLocalName = nullptr;
+ }
+
+ bool isNull()
+ {
+ return mNamespaceID == kNameSpaceID_None && !mLocalName;
+ }
+
+ txExpandedName& operator = (const txExpandedName& rhs)
+ {
+ mNamespaceID = rhs.mNamespaceID;
+ mLocalName = rhs.mLocalName;
+ return *this;
+ }
+
+ bool operator == (const txExpandedName& rhs) const
+ {
+ return ((mLocalName == rhs.mLocalName) &&
+ (mNamespaceID == rhs.mNamespaceID));
+ }
+
+ bool operator != (const txExpandedName& rhs) const
+ {
+ return ((mLocalName != rhs.mLocalName) ||
+ (mNamespaceID != rhs.mNamespaceID));
+ }
+
+ int32_t mNamespaceID;
+ nsCOMPtr<nsIAtom> mLocalName;
+};
+
+#endif
diff --git a/dom/xslt/base/txExpandedNameMap.cpp b/dom/xslt/base/txExpandedNameMap.cpp
new file mode 100644
index 000000000..b033d6155
--- /dev/null
+++ b/dom/xslt/base/txExpandedNameMap.cpp
@@ -0,0 +1,104 @@
+/* -*- 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 "txExpandedNameMap.h"
+#include "txCore.h"
+
+class txMapItemComparator
+{
+ public:
+ bool Equals(const txExpandedNameMap_base::MapItem& aItem,
+ const txExpandedName& aKey) const {
+ return aItem.mNamespaceID == aKey.mNamespaceID &&
+ aItem.mLocalName == aKey.mLocalName;
+ }
+};
+
+/**
+ * Adds an item, if an item with this key already exists an error is
+ * returned
+ * @param aKey key for item to add
+ * @param aValue value of item to add
+ * @return errorcode
+ */
+nsresult txExpandedNameMap_base::addItem(const txExpandedName& aKey,
+ void* aValue)
+{
+ size_t pos = mItems.IndexOf(aKey, 0, txMapItemComparator());
+ if (pos != mItems.NoIndex) {
+ return NS_ERROR_XSLT_ALREADY_SET;
+ }
+
+ MapItem* item = mItems.AppendElement();
+ NS_ENSURE_TRUE(item, NS_ERROR_OUT_OF_MEMORY);
+
+ item->mNamespaceID = aKey.mNamespaceID;
+ item->mLocalName = aKey.mLocalName;
+ item->mValue = aValue;
+
+ return NS_OK;
+}
+
+/**
+ * Sets an item, if an item with this key already exists it is overwritten
+ * with the new value
+ * @param aKey key for item to set
+ * @param aValue value of item to set
+ * @return errorcode
+ */
+nsresult txExpandedNameMap_base::setItem(const txExpandedName& aKey,
+ void* aValue,
+ void** aOldValue)
+{
+ *aOldValue = nullptr;
+ size_t pos = mItems.IndexOf(aKey, 0, txMapItemComparator());
+ if (pos != mItems.NoIndex) {
+ *aOldValue = mItems[pos].mValue;
+ mItems[pos].mValue = aValue;
+ return NS_OK;
+ }
+
+ MapItem* item = mItems.AppendElement();
+ NS_ENSURE_TRUE(item, NS_ERROR_OUT_OF_MEMORY);
+
+ item->mNamespaceID = aKey.mNamespaceID;
+ item->mLocalName = aKey.mLocalName;
+ item->mValue = aValue;
+
+ return NS_OK;
+}
+
+/**
+ * Gets an item
+ * @param aKey key for item to get
+ * @return item with specified key, or null if no such item exists
+ */
+void* txExpandedNameMap_base::getItem(const txExpandedName& aKey) const
+{
+ size_t pos = mItems.IndexOf(aKey, 0, txMapItemComparator());
+ if (pos != mItems.NoIndex) {
+ return mItems[pos].mValue;
+ }
+
+ return nullptr;
+}
+
+/**
+ * Removes an item, deleting it if the map owns the values
+ * @param aKey key for item to remove
+ * @return item with specified key, or null if it has been deleted
+ * or no such item exists
+ */
+void* txExpandedNameMap_base::removeItem(const txExpandedName& aKey)
+{
+ void* value = nullptr;
+ size_t pos = mItems.IndexOf(aKey, 0, txMapItemComparator());
+ if (pos != mItems.NoIndex) {
+ value = mItems[pos].mValue;
+ mItems.RemoveElementAt(pos);
+ }
+
+ return value;
+}
diff --git a/dom/xslt/base/txExpandedNameMap.h b/dom/xslt/base/txExpandedNameMap.h
new file mode 100644
index 000000000..de0861427
--- /dev/null
+++ b/dom/xslt/base/txExpandedNameMap.h
@@ -0,0 +1,203 @@
+/* -*- 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_EXPANDEDNAMEMAP_H
+#define TRANSFRMX_EXPANDEDNAMEMAP_H
+
+#include "nsAutoPtr.h"
+#include "nsError.h"
+#include "txExpandedName.h"
+#include "nsTArray.h"
+
+class txExpandedNameMap_base {
+protected:
+ /**
+ * Adds an item, if an item with this key already exists an error is
+ * returned
+ * @param aKey key for item to add
+ * @param aValue value of item to add
+ * @return errorcode
+ */
+ nsresult addItem(const txExpandedName& aKey, void* aValue);
+
+ /**
+ * Sets an item, if an item with this key already exists it is overwritten
+ * with the new value
+ * @param aKey key for item to set
+ * @param aValue value of item to set
+ * @return errorcode
+ */
+ nsresult setItem(const txExpandedName& aKey, void* aValue,
+ void** aOldValue);
+
+ /**
+ * Gets an item
+ * @param aKey key for item to get
+ * @return item with specified key, or null if no such item exists
+ */
+ void* getItem(const txExpandedName& aKey) const;
+
+ /**
+ * Removes an item, deleting it if the map owns the values
+ * @param aKey key for item to remove
+ * @return item with specified key, or null if it has been deleted
+ * or no such item exists
+ */
+ void* removeItem(const txExpandedName& aKey);
+
+ /**
+ * Clears the items
+ */
+ void clearItems()
+ {
+ mItems.Clear();
+ }
+
+ class iterator_base {
+ public:
+ explicit iterator_base(txExpandedNameMap_base& aMap)
+ : mMap(aMap),
+ mCurrentPos(uint32_t(-1))
+ {
+ }
+
+ bool next()
+ {
+ return ++mCurrentPos < mMap.mItems.Length();
+ }
+
+ const txExpandedName key()
+ {
+ NS_ASSERTION(mCurrentPos < mMap.mItems.Length(),
+ "invalid position in txExpandedNameMap::iterator");
+ return txExpandedName(mMap.mItems[mCurrentPos].mNamespaceID,
+ mMap.mItems[mCurrentPos].mLocalName);
+ }
+
+ protected:
+ void* itemValue()
+ {
+ NS_ASSERTION(mCurrentPos < mMap.mItems.Length(),
+ "invalid position in txExpandedNameMap::iterator");
+ return mMap.mItems[mCurrentPos].mValue;
+ }
+
+ private:
+ txExpandedNameMap_base& mMap;
+ uint32_t mCurrentPos;
+ };
+
+ friend class iterator_base;
+
+ friend class txMapItemComparator;
+ struct MapItem {
+ int32_t mNamespaceID;
+ nsCOMPtr<nsIAtom> mLocalName;
+ void* mValue;
+ };
+
+ nsTArray<MapItem> mItems;
+};
+
+template<class E>
+class txExpandedNameMap : public txExpandedNameMap_base
+{
+public:
+ nsresult add(const txExpandedName& aKey, E* aValue)
+ {
+ return addItem(aKey, (void*)aValue);
+ }
+
+ nsresult set(const txExpandedName& aKey, E* aValue)
+ {
+ void* oldValue;
+ return setItem(aKey, (void*)aValue, &oldValue);
+ }
+
+ E* get(const txExpandedName& aKey) const
+ {
+ return (E*)getItem(aKey);
+ }
+
+ E* remove(const txExpandedName& aKey)
+ {
+ return (E*)removeItem(aKey);
+ }
+
+ void clear()
+ {
+ clearItems();
+ }
+
+ class iterator : public iterator_base
+ {
+ public:
+ explicit iterator(txExpandedNameMap& aMap)
+ : iterator_base(aMap)
+ {
+ }
+
+ E* value()
+ {
+ return (E*)itemValue();
+ }
+ };
+};
+
+template<class E>
+class txOwningExpandedNameMap : public txExpandedNameMap_base
+{
+public:
+ ~txOwningExpandedNameMap()
+ {
+ clear();
+ }
+
+ nsresult add(const txExpandedName& aKey, E* aValue)
+ {
+ return addItem(aKey, (void*)aValue);
+ }
+
+ nsresult set(const txExpandedName& aKey, E* aValue)
+ {
+ nsAutoPtr<E> oldValue;
+ return setItem(aKey, (void*)aValue, getter_Transfers(oldValue));
+ }
+
+ E* get(const txExpandedName& aKey) const
+ {
+ return (E*)getItem(aKey);
+ }
+
+ void remove(const txExpandedName& aKey)
+ {
+ delete (E*)removeItem(aKey);
+ }
+
+ void clear()
+ {
+ uint32_t i, len = mItems.Length();
+ for (i = 0; i < len; ++i) {
+ delete (E*)mItems[i].mValue;
+ }
+ clearItems();
+ }
+
+ class iterator : public iterator_base
+ {
+ public:
+ explicit iterator(txOwningExpandedNameMap& aMap)
+ : iterator_base(aMap)
+ {
+ }
+
+ E* value()
+ {
+ return (E*)itemValue();
+ }
+ };
+};
+
+#endif //TRANSFRMX_EXPANDEDNAMEMAP_H
diff --git a/dom/xslt/base/txList.cpp b/dom/xslt/base/txList.cpp
new file mode 100644
index 000000000..fa4952ad9
--- /dev/null
+++ b/dom/xslt/base/txList.cpp
@@ -0,0 +1,278 @@
+/* -*- 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 "txList.h"
+
+ //----------------------------/
+ //- Implementation of txList -/
+//----------------------------/
+
+/**
+ * Default constructor for a txList;
+**/
+
+txList::txList() {
+ firstItem = 0;
+ lastItem = 0;
+ itemCount = 0;
+} //-- txList;
+
+/**
+ * txList destructor, cleans up ListItems, but will not delete the Object
+ * references
+*/
+txList::~txList() {
+ clear();
+} //-- ~txList
+
+nsresult txList::add(void* objPtr)
+{
+ return insertBefore(objPtr, 0);
+} //-- add
+
+/**
+ * Returns the number of items in this txList
+**/
+int32_t List::getLength() {
+ return itemCount;
+} //-- getLength
+
+
+/**
+ * Inserts the given Object pointer as the item just after refItem.
+ * If refItem is a null pointer the Object will be inserted at the
+ * beginning of the txList (ie, insert after nothing).
+ * This method assumes refItem is a member of this list, and since this
+ * is a private method, I feel that's a valid assumption
+**/
+nsresult txList::insertAfter(void* objPtr, ListItem* refItem)
+{
+ //-- if refItem == null insert at front
+ if (!refItem)
+ return insertBefore(objPtr, firstItem);
+ return insertBefore(objPtr, refItem->nextItem);
+} //-- insertAfter
+
+/**
+ * Inserts the given Object pointer as the item just before refItem.
+ * If refItem is a null pointer the Object will be inserted at the
+ * end of the txList (ie, insert before nothing).
+ * This method assumes refItem is a member of this list, and since this
+ * is a private method, I feel that's a valid assumption
+**/
+nsresult txList::insertBefore(void* objPtr, ListItem* refItem)
+{
+ ListItem* item = new ListItem;
+ item->objPtr = objPtr;
+ item->nextItem = 0;
+ item->prevItem = 0;
+
+ //-- if refItem == null insert at end
+ if (!refItem) {
+ //-- add to back of list
+ if (lastItem) {
+ lastItem->nextItem = item;
+ item->prevItem = lastItem;
+ }
+ lastItem = item;
+ if (!firstItem)
+ firstItem = item;
+ }
+ else {
+ //-- insert before given item
+ item->nextItem = refItem;
+ item->prevItem = refItem->prevItem;
+ refItem->prevItem = item;
+
+ if (item->prevItem)
+ item->prevItem->nextItem = item;
+ else
+ firstItem = item;
+ }
+
+ // increase the item count
+ ++itemCount;
+
+ return NS_OK;
+} //-- insertBefore
+
+txList::ListItem* txList::remove(ListItem* item) {
+
+ if (!item)
+ return item;
+
+ //-- adjust the previous item's next pointer
+ if (item->prevItem) {
+ item->prevItem->nextItem = item->nextItem;
+ }
+ //-- adjust the next item's previous pointer
+ if (item->nextItem) {
+ item->nextItem->prevItem = item->prevItem;
+ }
+
+ //-- adjust first and last items
+ if (item == firstItem)
+ firstItem = item->nextItem;
+ if (item == lastItem)
+ lastItem = item->prevItem;
+
+ //-- decrease Item count
+ --itemCount;
+ return item;
+} //-- remove
+
+void txList::clear()
+{
+ ListItem* item = firstItem;
+ while (item) {
+ ListItem* tItem = item;
+ item = item->nextItem;
+ delete tItem;
+ }
+ firstItem = 0;
+ lastItem = 0;
+ itemCount = 0;
+}
+
+ //------------------------------------/
+ //- Implementation of txListIterator -/
+//------------------------------------/
+
+
+/**
+ * Creates a new txListIterator for the given txList
+ * @param list, the txList to create an Iterator for
+**/
+txListIterator::txListIterator(txList* list) {
+ this->list = list;
+ currentItem = 0;
+ atEndOfList = false;
+} //-- txListIterator
+
+/**
+ * Adds the Object pointer to the txList pointed to by this txListIterator.
+ * The Object pointer is inserted as the next item in the txList
+ * based on the current position within the txList
+ * @param objPtr the Object pointer to add to the list
+**/
+nsresult txListIterator::addAfter(void* objPtr)
+{
+ if (currentItem || !atEndOfList)
+ return list->insertAfter(objPtr, currentItem);
+ return list->insertBefore(objPtr, 0);
+
+} //-- addAfter
+
+/**
+ * Adds the Object pointer to the txList pointed to by this txListIterator.
+ * The Object pointer is inserted as the previous item in the txList
+ * based on the current position within the txList
+ * @param objPtr the Object pointer to add to the list
+**/
+nsresult txListIterator::addBefore(void* objPtr)
+{
+ if (currentItem || atEndOfList)
+ return list->insertBefore(objPtr, currentItem);
+ return list->insertAfter(objPtr, 0);
+
+} //-- addBefore
+
+/**
+ * Returns true if a successful call to the next() method can be made
+ * @return true if a successful call to the next() method can be made,
+ * otherwise false
+**/
+bool txListIterator::hasNext() {
+ bool hasNext = false;
+ if (currentItem)
+ hasNext = (currentItem->nextItem != 0);
+ else if (!atEndOfList)
+ hasNext = (list->firstItem != 0);
+
+ return hasNext;
+} //-- hasNext
+
+/**
+ * Returns the next Object pointer in the list
+**/
+void* txListIterator::next() {
+
+ void* obj = 0;
+ if (currentItem)
+ currentItem = currentItem->nextItem;
+ else if (!atEndOfList)
+ currentItem = list->firstItem;
+
+ if (currentItem)
+ obj = currentItem->objPtr;
+ else
+ atEndOfList = true;
+
+ return obj;
+} //-- next
+
+/**
+ * Returns the previous Object in the list
+**/
+void* txListIterator::previous() {
+
+ void* obj = 0;
+
+ if (currentItem)
+ currentItem = currentItem->prevItem;
+ else if (atEndOfList)
+ currentItem = list->lastItem;
+
+ if (currentItem)
+ obj = currentItem->objPtr;
+
+ atEndOfList = false;
+
+ return obj;
+} //-- previous
+
+/**
+ * Returns the current Object
+**/
+void* txListIterator::current() {
+
+ if (currentItem)
+ return currentItem->objPtr;
+
+ return 0;
+} //-- current
+
+/**
+ * Removes the Object last returned by the next() or previous() methods;
+ * @return the removed Object pointer
+**/
+void* txListIterator::remove() {
+
+ void* obj = 0;
+ if (currentItem) {
+ obj = currentItem->objPtr;
+ txList::ListItem* item = currentItem;
+ previous(); //-- make previous item the current item
+ list->remove(item);
+ delete item;
+ }
+ return obj;
+} //-- remove
+
+/**
+ * Resets the current location within the txList to the beginning of the txList
+**/
+void txListIterator::reset() {
+ atEndOfList = false;
+ currentItem = 0;
+} //-- reset
+
+/**
+ * Move the iterator to right after the last element
+**/
+void txListIterator::resetToEnd() {
+ atEndOfList = true;
+ currentItem = 0;
+} //-- moveToEnd
diff --git a/dom/xslt/base/txList.h b/dom/xslt/base/txList.h
new file mode 100644
index 000000000..180bab1af
--- /dev/null
+++ b/dom/xslt/base/txList.h
@@ -0,0 +1,161 @@
+/* -*- 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_LIST_H
+#define TRANSFRMX_LIST_H
+
+#include "txCore.h"
+
+class txListIterator;
+
+/**
+ * Represents an ordered list of Object pointers. Modeled after a Java 2 List.
+**/
+class txList : public txObject {
+
+friend class txListIterator;
+
+public:
+
+ /**
+ * Creates an empty txList
+ **/
+ txList();
+
+ /**
+ * txList destructor, object references will not be deleted.
+ **/
+ ~txList();
+
+ /**
+ * Returns the number of items in this txList
+ **/
+ int32_t getLength();
+
+ /**
+ * Returns true if there are no items in this txList
+ */
+ inline bool isEmpty()
+ {
+ return itemCount == 0;
+ }
+
+ /**
+ * Adds the given Object to the list
+ **/
+ nsresult add(void* objPtr);
+
+ /*
+ * Removes all the objects from the list
+ */
+ void clear();
+
+protected:
+
+ struct ListItem {
+ ListItem* nextItem;
+ ListItem* prevItem;
+ void* objPtr;
+ };
+
+ /**
+ * Removes the given ListItem pointer from the list
+ **/
+ ListItem* remove(ListItem* sItem);
+
+private:
+ txList(const txList& aOther); // not implemented
+
+ ListItem* firstItem;
+ ListItem* lastItem;
+ int32_t itemCount;
+
+ nsresult insertAfter(void* objPtr, ListItem* sItem);
+ nsresult insertBefore(void* objPtr, ListItem* sItem);
+};
+
+
+
+/**
+ * An Iterator for the txList Class
+**/
+class txListIterator {
+
+public:
+ /**
+ * Creates a new txListIterator for the given txList
+ * @param list, the txList to create an Iterator for
+ **/
+ explicit txListIterator(txList* list);
+
+ /**
+ * Adds the Object pointer to the txList pointed to by this txListIterator.
+ * The Object pointer is inserted as the next item in the txList
+ * based on the current position within the txList
+ * @param objPtr the Object pointer to add to the list
+ **/
+ nsresult addAfter(void* objPtr);
+
+ /**
+ * Adds the Object pointer to the txList pointed to by this txListIterator.
+ * The Object pointer is inserted as the previous item in the txList
+ * based on the current position within the txList
+ * @param objPtr the Object pointer to add to the list
+ **/
+ nsresult addBefore(void* objPtr);
+
+ /**
+ * Returns true if a successful call to the next() method can be made
+ * @return true if a successful call to the next() method can be made,
+ * otherwise false
+ **/
+ bool hasNext();
+
+ /**
+ * Returns the next Object pointer from the list
+ **/
+ void* next();
+
+ /**
+ * Returns the previous Object pointer from the list
+ **/
+ void* previous();
+
+ /**
+ * Returns the current Object
+ **/
+ void* current();
+
+ /**
+ * Removes the Object last returned by the next() or previous() methods;
+ * @return the removed Object pointer
+ **/
+ void* remove();
+
+ /**
+ * Resets the current location within the txList to the beginning of the txList
+ **/
+ void reset();
+
+ /**
+ * Resets the current location within the txList to the end of the txList
+ **/
+ void resetToEnd();
+
+private:
+
+ //-- points to the current list item
+ txList::ListItem* currentItem;
+
+ //-- points to the list to iterator over
+ txList* list;
+
+ //-- we've moved off the end of the list
+ bool atEndOfList;
+};
+
+typedef txList List;
+
+#endif
diff --git a/dom/xslt/base/txLog.h b/dom/xslt/base/txLog.h
new file mode 100644
index 000000000..0387c9d60
--- /dev/null
+++ b/dom/xslt/base/txLog.h
@@ -0,0 +1,24 @@
+/* -*- 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 txLog_h__
+#define txLog_h__
+
+#include "mozilla/Logging.h"
+
+class txLog
+{
+public:
+ static mozilla::LazyLogModule xpath;
+ static mozilla::LazyLogModule xslt;
+};
+
+#define TX_LG_IMPL \
+ mozilla::LazyLogModule txLog::xpath("xpath"); \
+ mozilla::LazyLogModule txLog::xslt("xslt");
+
+#define TX_LG_CREATE
+
+#endif
diff --git a/dom/xslt/base/txNamespaceMap.cpp b/dom/xslt/base/txNamespaceMap.cpp
new file mode 100644
index 000000000..cb6c4b164
--- /dev/null
+++ b/dom/xslt/base/txNamespaceMap.cpp
@@ -0,0 +1,98 @@
+/* -*- 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 "txNamespaceMap.h"
+#include "nsGkAtoms.h"
+#include "txXPathNode.h"
+
+txNamespaceMap::txNamespaceMap()
+{
+}
+
+txNamespaceMap::txNamespaceMap(const txNamespaceMap& aOther)
+ : mPrefixes(aOther.mPrefixes)
+{
+ mNamespaces = aOther.mNamespaces; //bah! I want a copy-constructor!
+}
+
+nsresult
+txNamespaceMap::mapNamespace(nsIAtom* aPrefix, const nsAString& aNamespaceURI)
+{
+ nsIAtom* prefix = aPrefix == nsGkAtoms::_empty ? nullptr : aPrefix;
+
+ int32_t nsId;
+ if (prefix && aNamespaceURI.IsEmpty()) {
+ // Remove the mapping
+ int32_t index = mPrefixes.IndexOf(prefix);
+ if (index >= 0) {
+ mPrefixes.RemoveObjectAt(index);
+ mNamespaces.RemoveElementAt(index);
+ }
+
+ return NS_OK;
+ }
+
+ if (aNamespaceURI.IsEmpty()) {
+ // Set default to empty namespace
+ nsId = kNameSpaceID_None;
+ }
+ else {
+ nsId = txNamespaceManager::getNamespaceID(aNamespaceURI);
+ NS_ENSURE_FALSE(nsId == kNameSpaceID_Unknown, NS_ERROR_FAILURE);
+ }
+
+ // Check if the mapping already exists
+ int32_t index = mPrefixes.IndexOf(prefix);
+ if (index >= 0) {
+ mNamespaces.ElementAt(index) = nsId;
+
+ return NS_OK;
+ }
+
+ // New mapping
+ if (!mPrefixes.AppendObject(prefix)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (mNamespaces.AppendElement(nsId) == nullptr) {
+ mPrefixes.RemoveObjectAt(mPrefixes.Count() - 1);
+
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ return NS_OK;
+}
+
+int32_t
+txNamespaceMap::lookupNamespace(nsIAtom* aPrefix)
+{
+ if (aPrefix == nsGkAtoms::xml) {
+ return kNameSpaceID_XML;
+ }
+
+ nsIAtom* prefix = aPrefix == nsGkAtoms::_empty ? 0 : aPrefix;
+
+ int32_t index = mPrefixes.IndexOf(prefix);
+ if (index >= 0) {
+ return mNamespaces.SafeElementAt(index, kNameSpaceID_Unknown);
+ }
+
+ if (!prefix) {
+ return kNameSpaceID_None;
+ }
+
+ return kNameSpaceID_Unknown;
+}
+
+int32_t
+txNamespaceMap::lookupNamespaceWithDefault(const nsAString& aPrefix)
+{
+ nsCOMPtr<nsIAtom> prefix = NS_Atomize(aPrefix);
+ if (prefix != nsGkAtoms::_poundDefault) {
+ return lookupNamespace(prefix);
+ }
+
+ return lookupNamespace(nullptr);
+}
diff --git a/dom/xslt/base/txNamespaceMap.h b/dom/xslt/base/txNamespaceMap.h
new file mode 100644
index 000000000..0e6be57c6
--- /dev/null
+++ b/dom/xslt/base/txNamespaceMap.h
@@ -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/. */
+
+#ifndef TRANSFRMX_TXNAMESPACEMAP_H
+#define TRANSFRMX_TXNAMESPACEMAP_H
+
+#include "nsIAtom.h"
+#include "nsCOMArray.h"
+#include "nsTArray.h"
+
+class txNamespaceMap
+{
+public:
+ txNamespaceMap();
+ txNamespaceMap(const txNamespaceMap& aOther);
+
+ nsrefcnt AddRef()
+ {
+ return ++mRefCnt;
+ }
+ nsrefcnt Release()
+ {
+ if (--mRefCnt == 0) {
+ mRefCnt = 1; //stabilize
+ delete this;
+ return 0;
+ }
+ return mRefCnt;
+ }
+
+ nsresult mapNamespace(nsIAtom* aPrefix, const nsAString& aNamespaceURI);
+ int32_t lookupNamespace(nsIAtom* aPrefix);
+ int32_t lookupNamespaceWithDefault(const nsAString& aPrefix);
+
+private:
+ nsAutoRefCnt mRefCnt;
+ nsCOMArray<nsIAtom> mPrefixes;
+ nsTArray<int32_t> mNamespaces;
+};
+
+#endif //TRANSFRMX_TXNAMESPACEMAP_H
diff --git a/dom/xslt/base/txOwningArray.h b/dom/xslt/base/txOwningArray.h
new file mode 100644
index 000000000..6ec7ebd22
--- /dev/null
+++ b/dom/xslt/base/txOwningArray.h
@@ -0,0 +1,32 @@
+/* -*- 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/. */
+
+#ifndef txOwningArray_h__
+#define txOwningArray_h__
+
+// Class acting like a nsTArray except that it deletes its objects
+// on destruction. It does not however delete its objects on operations
+// like RemoveElementsAt or on |array[i] = bar|.
+
+template<class E>
+class txOwningArray : public nsTArray<E*>
+{
+public:
+ typedef nsTArray<E*> base_type;
+ typedef typename base_type::elem_type elem_type;
+
+ ~txOwningArray()
+ {
+ elem_type* iter = base_type::Elements();
+ elem_type* end = iter + base_type::Length();
+ for (; iter < end; ++iter) {
+ delete *iter;
+ }
+ }
+
+};
+
+#endif // txOwningArray_h__
diff --git a/dom/xslt/base/txStack.h b/dom/xslt/base/txStack.h
new file mode 100644
index 000000000..53ce29862
--- /dev/null
+++ b/dom/xslt/base/txStack.h
@@ -0,0 +1,122 @@
+/* -*- 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 txStack_h___
+#define txStack_h___
+
+#include "nsTArray.h"
+
+class txStack : private nsTArray<void*>
+{
+public:
+ /**
+ * Returns the specified object from the top of this stack,
+ * without removing it from the stack.
+ *
+ * @return a pointer to the object that is the top of this stack.
+ */
+ inline void* peek()
+ {
+ NS_ASSERTION(!isEmpty(), "peeking at empty stack");
+ return !isEmpty() ? ElementAt(Length() - 1) : nullptr;
+ }
+
+ /**
+ * Adds the specified object to the top of this stack.
+ *
+ * @param obj a pointer to the object that is to be added to the
+ * top of this stack.
+ */
+ inline nsresult push(void* aObject)
+ {
+ return AppendElement(aObject) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ /**
+ * Removes and returns the specified object from the top of this
+ * stack.
+ *
+ * @return a pointer to the object that was the top of this stack.
+ */
+ inline void* pop()
+ {
+ void* object = nullptr;
+ NS_ASSERTION(!isEmpty(), "popping from empty stack");
+ if (!isEmpty())
+ {
+ const uint32_t count = Length() - 1;
+ object = ElementAt(count);
+ RemoveElementAt(count);
+ }
+ return object;
+ }
+
+ /**
+ * Returns true if there are no objects in the stack.
+ *
+ * @return true if there are no objects in the stack.
+ */
+ inline bool isEmpty()
+ {
+ return IsEmpty();
+ }
+
+ /**
+ * Returns the number of elements in the Stack.
+ *
+ * @return the number of elements in the Stack.
+ */
+ inline int32_t size()
+ {
+ return Length();
+ }
+
+private:
+ friend class txStackIterator;
+};
+
+class txStackIterator
+{
+public:
+ /**
+ * Creates an iterator for the given stack.
+ *
+ * @param aStack the stack to create an iterator for.
+ */
+ inline
+ explicit txStackIterator(txStack* aStack) : mStack(aStack),
+ mPosition(0)
+ {
+ }
+
+ /**
+ * Returns true if there is more objects on the stack.
+ *
+ * @return .
+ */
+ inline bool hasNext()
+ {
+ return (mPosition < mStack->Length());
+ }
+
+ /**
+ * Returns the next object pointer from the stack.
+ *
+ * @return .
+ */
+ inline void* next()
+ {
+ if (mPosition == mStack->Length()) {
+ return nullptr;
+ }
+ return mStack->ElementAt(mPosition++);
+ }
+
+private:
+ txStack* mStack;
+ uint32_t mPosition;
+};
+
+#endif /* txStack_h___ */
diff --git a/dom/xslt/base/txStringUtils.h b/dom/xslt/base/txStringUtils.h
new file mode 100644
index 000000000..6f45fe661
--- /dev/null
+++ b/dom/xslt/base/txStringUtils.h
@@ -0,0 +1,34 @@
+/* -*- 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/. */
+
+#ifndef txStringUtils_h__
+#define txStringUtils_h__
+
+#include "nsAString.h"
+#include "nsIAtom.h"
+#include "nsUnicharUtils.h"
+#include "nsContentUtils.h" // For ASCIIToLower().
+
+typedef nsCaseInsensitiveStringComparator txCaseInsensitiveStringComparator;
+
+/**
+ * Check equality between a string and an atom containing ASCII.
+ */
+inline bool
+TX_StringEqualsAtom(const nsASingleFragmentString& aString, nsIAtom* aAtom)
+{
+ return aAtom->Equals(aString);
+}
+
+inline already_AddRefed<nsIAtom>
+TX_ToLowerCaseAtom(nsIAtom* aAtom)
+{
+ nsAutoString str;
+ aAtom->ToString(str);
+ nsContentUtils::ASCIIToLower(str);
+ return NS_Atomize(str);
+}
+
+#endif // txStringUtils_h__
diff --git a/dom/xslt/base/txURIUtils.cpp b/dom/xslt/base/txURIUtils.cpp
new file mode 100644
index 000000000..3f3556f80
--- /dev/null
+++ b/dom/xslt/base/txURIUtils.cpp
@@ -0,0 +1,78 @@
+/* -*- 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 "txURIUtils.h"
+#include "nsNetUtil.h"
+#include "nsIDocument.h"
+#include "nsIHttpChannelInternal.h"
+#include "nsIPrincipal.h"
+#include "mozilla/LoadInfo.h"
+
+using mozilla::net::LoadInfo;
+
+/**
+ * URIUtils
+ * A set of utilities for handling URIs
+**/
+
+/**
+ * Resolves the given href argument, using the given documentBase
+ * if necessary.
+ * The new resolved href will be appended to the given dest String
+**/
+void URIUtils::resolveHref(const nsAString& href, const nsAString& base,
+ nsAString& dest) {
+ if (base.IsEmpty()) {
+ dest.Append(href);
+ return;
+ }
+ if (href.IsEmpty()) {
+ dest.Append(base);
+ return;
+ }
+ nsCOMPtr<nsIURI> pURL;
+ nsAutoString resultHref;
+ nsresult result = NS_NewURI(getter_AddRefs(pURL), base);
+ if (NS_SUCCEEDED(result)) {
+ NS_MakeAbsoluteURI(resultHref, href, pURL);
+ dest.Append(resultHref);
+ }
+} //-- resolveHref
+
+// static
+void
+URIUtils::ResetWithSource(nsIDocument *aNewDoc, nsINode *aSourceNode)
+{
+ nsCOMPtr<nsIDocument> sourceDoc = aSourceNode->OwnerDoc();
+ nsIPrincipal* sourcePrincipal = sourceDoc->NodePrincipal();
+
+ // Copy the channel and loadgroup from the source document.
+ nsCOMPtr<nsILoadGroup> loadGroup = sourceDoc->GetDocumentLoadGroup();
+ nsCOMPtr<nsIChannel> channel = sourceDoc->GetChannel();
+ if (!channel) {
+ // Need to synthesize one
+ nsresult rv = NS_NewChannel(getter_AddRefs(channel),
+ sourceDoc->GetDocumentURI(),
+ sourceDoc,
+ nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL,
+ nsIContentPolicy::TYPE_OTHER,
+ loadGroup,
+ nullptr, // aCallbacks
+ nsIChannel::LOAD_BYPASS_SERVICE_WORKER);
+
+ if (NS_FAILED(rv)) {
+ return;
+ }
+ }
+
+ aNewDoc->Reset(channel, loadGroup);
+ aNewDoc->SetPrincipal(sourcePrincipal);
+ aNewDoc->SetBaseURI(sourceDoc->GetDocBaseURI());
+
+ // Copy charset
+ aNewDoc->SetDocumentCharacterSetSource(
+ sourceDoc->GetDocumentCharacterSetSource());
+ aNewDoc->SetDocumentCharacterSet(sourceDoc->GetDocumentCharacterSet());
+}
diff --git a/dom/xslt/base/txURIUtils.h b/dom/xslt/base/txURIUtils.h
new file mode 100644
index 000000000..ad182a00a
--- /dev/null
+++ b/dom/xslt/base/txURIUtils.h
@@ -0,0 +1,37 @@
+/* -*- 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_URIUTILS_H
+#define TRANSFRMX_URIUTILS_H
+
+#include "txCore.h"
+
+class nsIDocument;
+class nsINode;
+
+/**
+ * A utility class for URI handling
+ * Not yet finished, only handles file URI at this point
+**/
+
+class URIUtils {
+public:
+
+ /**
+ * Reset the given document with the document of the source node
+ */
+ static void ResetWithSource(nsIDocument *aNewDoc, nsINode *aSourceNode);
+
+ /**
+ * Resolves the given href argument, using the given documentBase
+ * if necessary.
+ * The new resolved href will be appended to the given dest String
+ **/
+ static void resolveHref(const nsAString& href, const nsAString& base,
+ nsAString& dest);
+}; //-- URIUtils
+
+/* */
+#endif
diff --git a/dom/xslt/crashtests/1089049.html b/dom/xslt/crashtests/1089049.html
new file mode 100644
index 000000000..84ef6494c
--- /dev/null
+++ b/dom/xslt/crashtests/1089049.html
@@ -0,0 +1,3 @@
+<script>
+var xpathResult = document.evaluate('', null, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
+</script>
diff --git a/dom/xslt/crashtests/111994.xml b/dom/xslt/crashtests/111994.xml
new file mode 100644
index 000000000..ce7ffad2a
--- /dev/null
+++ b/dom/xslt/crashtests/111994.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" standalone="yes" ?>
+<?xml-stylesheet type="text/xsl" href="111994.xsl" ?>
+<root>
+ <item id="1001" name="name" />
+</root>
diff --git a/dom/xslt/crashtests/111994.xsl b/dom/xslt/crashtests/111994.xsl
new file mode 100644
index 000000000..4cf68179f
--- /dev/null
+++ b/dom/xslt/crashtests/111994.xsl
@@ -0,0 +1,13 @@
+<?xml version="1.0" ?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+ <xsl:template match="root">
+ <html><body><xsl:apply-templates select="item" /></body></html>
+ </xsl:template>
+ <xsl:template match="item">
+ <img>
+ <xsl:attribute name="src">
+ <xsl:value-of select="@name" />
+ </xsl:attribute>
+ </img>
+ </xsl:template>
+</xsl:stylesheet>
diff --git a/dom/xslt/crashtests/1205163.xml b/dom/xslt/crashtests/1205163.xml
new file mode 100644
index 000000000..2d450a1d5
--- /dev/null
+++ b/dom/xslt/crashtests/1205163.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="1205163.xsl"?>
+<result>
+ <Title>Example</Title>
+ <Error>Error</Error>
+</result>
diff --git a/dom/xslt/crashtests/1205163.xsl b/dom/xslt/crashtests/1205163.xsl
new file mode 100644
index 000000000..f0bbb5cb9
--- /dev/null
+++ b/dom/xslt/crashtests/1205163.xsl
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<xsl:stylesheet version="1.0"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+ <xsl:output method="text"/>
+ <xsl:template match="node()|@*">
+ <xsl:copy>
+ <xsl:apply-templates select="node()|@*"/>
+ </xsl:copy>
+ </xsl:template>
+ <xsl:template match="Error"/>
+</xsl:stylesheet>
diff --git a/dom/xslt/crashtests/1243337.xml b/dom/xslt/crashtests/1243337.xml
new file mode 100644
index 000000000..40f5e3a35
--- /dev/null
+++ b/dom/xslt/crashtests/1243337.xml
@@ -0,0 +1,3 @@
+<?xml version="1.0" standalone="yes" ?>
+<?xml-stylesheet type="text/xsl" href="1243337.xsl" ?>
+<root/>
diff --git a/dom/xslt/crashtests/1243337.xsl b/dom/xslt/crashtests/1243337.xsl
new file mode 100644
index 000000000..0e659bf90
--- /dev/null
+++ b/dom/xslt/crashtests/1243337.xsl
@@ -0,0 +1,6 @@
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+ <xsl:template match="/">
+ <xsl:value-of select="generate-id(5)"/>
+ <html><body/></html>
+ </xsl:template>
+</xsl:stylesheet>
diff --git a/dom/xslt/crashtests/182460-select.xml b/dom/xslt/crashtests/182460-select.xml
new file mode 100644
index 000000000..bb9f79b04
--- /dev/null
+++ b/dom/xslt/crashtests/182460-select.xml
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<?xml-stylesheet type="text/xsl" href="182460-selects.xsl"?>
+<fud/> \ No newline at end of file
diff --git a/dom/xslt/crashtests/182460-selects.xsl b/dom/xslt/crashtests/182460-selects.xsl
new file mode 100644
index 000000000..f083ea31f
--- /dev/null
+++ b/dom/xslt/crashtests/182460-selects.xsl
@@ -0,0 +1,5 @@
+<html>
+<body>
+hi
+</body>
+</html>
diff --git a/dom/xslt/crashtests/182460-table.xhtml b/dom/xslt/crashtests/182460-table.xhtml
new file mode 100644
index 000000000..1bf2a23cb
--- /dev/null
+++ b/dom/xslt/crashtests/182460-table.xhtml
@@ -0,0 +1,9 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>XSL Crash</title>
+</head>
+<body>
+<iframe src="182460-select.xml"></iframe>
+<script type="text/javascript" src="data:text/javascript,/*no code*/"/>
+</body>
+</html>
diff --git a/dom/xslt/crashtests/226425.xml b/dom/xslt/crashtests/226425.xml
new file mode 100644
index 000000000..c6662cfe2
--- /dev/null
+++ b/dom/xslt/crashtests/226425.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<?xml-stylesheet type="text/xsl" href="226425.xsl" ?>
+
+<text></text>
diff --git a/dom/xslt/crashtests/226425.xsl b/dom/xslt/crashtests/226425.xsl
new file mode 100644
index 000000000..ddb61ae68
--- /dev/null
+++ b/dom/xslt/crashtests/226425.xsl
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+<xsl:output method="html" indent="yes"/>
+
+<xsl:template match="/">
+<html><body>
+<xsl:apply-templates select="document('dontmatter')"/>
+</body></html>
+</xsl:template>
+</xsl:stylesheet>
diff --git a/dom/xslt/crashtests/406106-1.html b/dom/xslt/crashtests/406106-1.html
new file mode 100644
index 000000000..86ab510ff
--- /dev/null
+++ b/dom/xslt/crashtests/406106-1.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script type="text/javascript">
+
+function boom()
+{
+ var p = new XSLTProcessor();
+ p.setParameter("a", "b", {e: p});
+}
+
+boom();
+
+</script>
+</head>
+<body>
+</body>
+</html>
diff --git a/dom/xslt/crashtests/483444.xml b/dom/xslt/crashtests/483444.xml
new file mode 100644
index 000000000..18fffceba
--- /dev/null
+++ b/dom/xslt/crashtests/483444.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="#stylesheet"?>
+<!DOCTYPE root [
+ <!ATTLIST xsl:stylesheet id ID #IMPLIED>
+]>
+<root>
+<xsl:stylesheet id="stylesheet" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+ <xsl:attribute-set name="a">
+ <xsl:attribute name="b">c</xsl:attribute>
+ </xsl:attribute-set>
+ <xsl:template match="/">
+ <b>Please output something!</b>
+ </xsl:template>
+ <xsl:attribute-set name="a">
+ <xsl:attribute name="b">c</xsl:attribute>
+ </xsl:attribute-set>
+</xsl:stylesheet>
+<doc id="foo">this</doc>
+</root>
diff --git a/dom/xslt/crashtests/485217.xml b/dom/xslt/crashtests/485217.xml
new file mode 100644
index 000000000..93a20e4f7
--- /dev/null
+++ b/dom/xslt/crashtests/485217.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?xml-stylesheet type="text/xsl" href="485217.xsl"?>
+
+
+<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <item1 id="AAAAAAA" />
+ <item2 id="AAAAAAAAA" label="AAAAAAAAAAAA"/>
+</root>
diff --git a/dom/xslt/crashtests/485217.xsl b/dom/xslt/crashtests/485217.xsl
new file mode 100644
index 000000000..2935c5a41
--- /dev/null
+++ b/dom/xslt/crashtests/485217.xsl
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+ <xsl:key name="label" match="item2" use="undeclaredfunc()"/>
+
+ <xsl:template match="root">
+ <xsl:for-each select="//item1">
+ <xsl:call-template name="item1" />
+ </xsl:for-each>
+ </xsl:template>
+
+ <xsl:template name="item1">
+ <xsl:for-each select="key('label', 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA')">
+ </xsl:for-each>
+ </xsl:template>
+
+</xsl:stylesheet>
diff --git a/dom/xslt/crashtests/485286.xml b/dom/xslt/crashtests/485286.xml
new file mode 100644
index 000000000..c87aa940e
--- /dev/null
+++ b/dom/xslt/crashtests/485286.xml
@@ -0,0 +1,12 @@
+<?xml-stylesheet type="application/xml" href="485286.xml"?>
+<transform xmlns="http://www.w3.org/1999/XSL/Transform" version="1.0">
+ <variable name="v">
+ <for-each select="/">
+ <value-of select="count(1)"/>
+ </for-each>
+ </variable>
+ <key name="k" match="/" use="$v"/>
+ <template match="/">
+ <value-of select="key('k', /..)"/>
+ </template>
+</transform>
diff --git a/dom/xslt/crashtests/527558_1.xml b/dom/xslt/crashtests/527558_1.xml
new file mode 100644
index 000000000..ebb6c3d53
--- /dev/null
+++ b/dom/xslt/crashtests/527558_1.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xml" href="#bug"?>
+<!DOCTYPE doc [
+<!ATTLIST xsl:transform
+ id ID #REQUIRED>
+]>
+<doc>
+<xsl:transform id="bug"
+ version="2.0"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+ <xsl:key name="k0" match="e1" use="key('k0', 'foobar')" />
+ <xsl:template id="t1" name="t1" match="key('k0', '1/2/2003')" />
+</xsl:transform>
+
+<e1 a1="foobar" a2="foobar"/>
+
+</doc>
diff --git a/dom/xslt/crashtests/528300.xml b/dom/xslt/crashtests/528300.xml
new file mode 100644
index 000000000..8902bb373
--- /dev/null
+++ b/dom/xslt/crashtests/528300.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xml" href="#bug"?>
+<!DOCTYPE doc [
+<!ATTLIST xsl:transform
+ id ID #REQUIRED>
+]>
+<doc>
+<xsl:transform
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ version="2.0"
+ id="bug">
+ <xsl:variable name="v0">
+ <xsl:for-each select="$v0" />
+ </xsl:variable>
+ <xsl:template name="t2" match="/">
+ <xsl:copy-of select="number($v0)" />
+ </xsl:template>
+</xsl:transform>
+
+<e1 />
+
+</doc>
diff --git a/dom/xslt/crashtests/528488.xml b/dom/xslt/crashtests/528488.xml
new file mode 100644
index 000000000..904b34561
--- /dev/null
+++ b/dom/xslt/crashtests/528488.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xml" href="#bug"?>
+<!DOCTYPE doc [
+<!ATTLIST xsl:transform
+ id ID #REQUIRED>
+]>
+<doc>
+ <xsl:transform
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:exslstrings="http://exslt.org/strings"
+ version="2.0"
+ id="bug">
+ <xsl:variable name="v0" select="$v0" />
+ <xsl:template name="t2" match="/">
+ <xsl:param name="p0" select="exslstrings:tokenize('1234','foobar')" />
+ <xsl:copy-of select="round($v0)" />
+ </xsl:template>
+ </xsl:transform>
+</doc>
diff --git a/dom/xslt/crashtests/528963.xml b/dom/xslt/crashtests/528963.xml
new file mode 100644
index 000000000..d855e7563
--- /dev/null
+++ b/dom/xslt/crashtests/528963.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xml" href="#bug"?>
+<!DOCTYPE doc [
+<!ATTLIST xsl:transform
+ id ID #REQUIRED>
+]>
+<doc>
+ <xsl:transform
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ version="2.0"
+ id="bug">
+ <xsl:key name="k0" match="e2" use="name('foo')" />
+
+ <xsl:template name="t1" match="/">
+ <xsl:element name="e2" namespace="{//doc}" />
+ </xsl:template>
+ <xsl:template name="t2" match="key('k0', 'bar')" />
+ </xsl:transform>
+
+ <e2/>
+
+</doc>
diff --git a/dom/xslt/crashtests/545927.html b/dom/xslt/crashtests/545927.html
new file mode 100644
index 000000000..0a9010e21
--- /dev/null
+++ b/dom/xslt/crashtests/545927.html
@@ -0,0 +1,28 @@
+<html>
+<head>
+<script>
+function main()
+{
+ xml=document.implementation.createDocument('', '', null);
+ xml.appendChild(doc=xml.createElement('root'));
+
+ var p = new DOMParser();
+ text = '<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">';
+ text += '<xsl:template match="/">';
+ text += '<body>';
+ text += '<xsl:number value="2147483648" format="i"/>';
+ text += '</body>';
+ text += '</xsl:template>';
+ text += '</xsl:stylesheet>';
+ xsl=p.parseFromString(text, 'text/xml');
+
+ xsltProcessor=new XSLTProcessor();
+ xsltProcessor.importStylesheet(xsl);
+ d = xsltProcessor.transformToFragment(xml, document);
+}
+</script>
+</head>
+<body onload="main()">
+</body>
+</html>
+
diff --git a/dom/xslt/crashtests/601543.html b/dom/xslt/crashtests/601543.html
new file mode 100644
index 000000000..af025b242
--- /dev/null
+++ b/dom/xslt/crashtests/601543.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<script>
+(new XSLTProcessor).setParameter('', '', [{}, null]);
+</script>
diff --git a/dom/xslt/crashtests/602115.html b/dom/xslt/crashtests/602115.html
new file mode 100644
index 000000000..ad42d76a0
--- /dev/null
+++ b/dom/xslt/crashtests/602115.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<script>
+
+try {
+ var docType = document.implementation.createDocumentType(undefined, '', '');
+ var doc = document.implementation.createDocument('', '', null);
+ var xp = new XSLTProcessor;
+ xp.importStylesheet(doc);
+ xp.transformToDocument(docType);
+}
+catch (ex) {}
+
+try {
+ docType = document.implementation.createDocumentType(undefined, '', '');
+ doc = document.implementation.createDocument('', '', null);
+ xp = new XSLTProcessor;
+ xp.importStylesheet(doc);
+ xp.transformToFragment(docType, document);
+}
+catch (ex) {}
+
+</script>
diff --git a/dom/xslt/crashtests/603844.html b/dom/xslt/crashtests/603844.html
new file mode 100644
index 000000000..f576effdb
--- /dev/null
+++ b/dom/xslt/crashtests/603844.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+<script>
+
+function boom()
+{
+ var frame = document.createElementNS("http://www.w3.org/1999/xhtml", "iframe");
+ frame.onload = y;
+ frame.src = "data:text/plain,0";
+ document.body.appendChild(frame);
+ frameDoc = frame.contentDocument;
+
+ function y()
+ {
+ frameDoc.removeChild(frameDoc.documentElement);
+
+ var xp = new XSLTProcessor;
+ xp.importStylesheet(frameDoc);
+ try {
+ xp.transformToDocument(frameDoc.createTextNode('x'));
+ } catch(e) { }
+
+ document.documentElement.removeAttribute("class");
+ }
+}
+
+</script>
+</head>
+
+<body onload="boom();"></body>
+</html>
diff --git a/dom/xslt/crashtests/667315.xml b/dom/xslt/crashtests/667315.xml
new file mode 100644
index 000000000..7a560ba3b
--- /dev/null
+++ b/dom/xslt/crashtests/667315.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="#stylesheet"?>
+<!DOCTYPE root [
+ <!ATTLIST xsl:stylesheet id ID #IMPLIED>
+]>
+<root>
+<xsl:stylesheet id="stylesheet" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+ <xsl:variable name="var"><p>a</p></xsl:variable>
+ <xsl:template match="/"><xsl:copy-of select="$var" /></xsl:template>
+</xsl:stylesheet>
+</root>
diff --git a/dom/xslt/crashtests/91332.xml b/dom/xslt/crashtests/91332.xml
new file mode 100644
index 000000000..c5a463c8a
--- /dev/null
+++ b/dom/xslt/crashtests/91332.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" standalone="yes" ?>
+<?xml-stylesheet type="text/xsl" href="91332.xsl" ?>
+<root>
+ <category name="Rectangles">
+ <list item="square" />
+ </category>
+ <quad id="square">
+ <desc>A square is ...</desc>
+ </quad>
+</root>
diff --git a/dom/xslt/crashtests/91332.xsl b/dom/xslt/crashtests/91332.xsl
new file mode 100644
index 000000000..7d01df22b
--- /dev/null
+++ b/dom/xslt/crashtests/91332.xsl
@@ -0,0 +1,21 @@
+<?xml version="1.0" ?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+
+ <xsl:key name="polyList" match="quad" use="@id" />
+
+ <xsl:template match="root">
+ <html><body><xsl:apply-templates select="category" /></body></html>
+ </xsl:template>
+
+ <xsl:template match="category">
+ <table><xsl:apply-templates select="list" /></table>
+ </xsl:template>
+
+ <xsl:template match="list">
+ <tr><td><xsl:apply-templates select="key('polyList',@item)" /></td></tr>
+ </xsl:template>
+
+ <xsl:template match="quad">
+ <b>Please output something!</b>
+ </xsl:template>
+</xsl:stylesheet>
diff --git a/dom/xslt/crashtests/crashtests.list b/dom/xslt/crashtests/crashtests.list
new file mode 100644
index 000000000..5958655d6
--- /dev/null
+++ b/dom/xslt/crashtests/crashtests.list
@@ -0,0 +1,20 @@
+load 91332.xml
+load 111994.xml
+load 182460-table.xhtml
+load 226425.xml
+load 406106-1.html
+load 483444.xml
+load 485217.xml
+load 485286.xml
+load 527558_1.xml
+load 528300.xml
+load 528488.xml
+load 528963.xml
+load 545927.html
+load 601543.html
+load 602115.html
+load 603844.html
+load 667315.xml
+load 1089049.html
+load 1205163.xml
+load 1243337.xml
diff --git a/dom/xslt/moz.build b/dom/xslt/moz.build
new file mode 100644
index 000000000..32ece0b53
--- /dev/null
+++ b/dom/xslt/moz.build
@@ -0,0 +1,31 @@
+# -*- 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/.
+
+XPIDL_SOURCES += [
+ 'nsIXSLTProcessor.idl',
+ 'nsIXSLTProcessorPrivate.idl',
+ 'txIEXSLTRegExFunctions.idl',
+ 'txIFunctionEvaluationContext.idl',
+ 'txINodeSet.idl',
+ 'txIXPathObject.idl',
+]
+
+XPIDL_MODULE = 'content_xslt'
+
+EXPORTS += [
+ 'nsIDocumentTransformer.h',
+]
+
+DIRS += [
+ 'base',
+ 'xml',
+ 'xpath',
+ 'xslt',
+]
+
+if CONFIG['ENABLE_TESTS']:
+ JAR_MANIFESTS += ['tests/buster/jar.mn']
+ MOCHITEST_MANIFESTS += ['tests/mochitest/mochitest.ini']
diff --git a/dom/xslt/nsIDocumentTransformer.h b/dom/xslt/nsIDocumentTransformer.h
new file mode 100644
index 000000000..13e2d39e8
--- /dev/null
+++ b/dom/xslt/nsIDocumentTransformer.h
@@ -0,0 +1,65 @@
+/* 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 nsIDocumentTransformer_h__
+#define nsIDocumentTransformer_h__
+
+#include "nsISupports.h"
+
+template<class> class nsCOMPtr;
+class nsIContent;
+class nsIDocument;
+class nsIDOMNode;
+class nsIURI;
+class nsString;
+template<class> class nsTArray;
+
+#define NS_ITRANSFORMOBSERVER_IID \
+{ 0x04b2d17c, 0xe98d, 0x45f5, \
+ { 0x9a, 0x67, 0xb7, 0x01, 0x19, 0x59, 0x7d, 0xe7 } }
+
+class nsITransformObserver : public nsISupports
+{
+public:
+
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_ITRANSFORMOBSERVER_IID)
+
+ NS_IMETHOD OnDocumentCreated(nsIDocument *aResultDocument) = 0;
+
+ NS_IMETHOD OnTransformDone(nsresult aResult,
+ nsIDocument *aResultDocument) = 0;
+
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsITransformObserver, NS_ITRANSFORMOBSERVER_IID)
+
+#define NS_IDOCUMENTTRANSFORMER_IID \
+{ 0xf45e1ff8, 0x50f3, 0x4496, \
+ { 0xb3, 0xa2, 0x0e, 0x03, 0xe8, 0x4a, 0x57, 0x11 } }
+
+class nsIDocumentTransformer : public nsISupports
+{
+public:
+
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_IDOCUMENTTRANSFORMER_IID)
+
+ NS_IMETHOD SetTransformObserver(nsITransformObserver* aObserver) = 0;
+ NS_IMETHOD LoadStyleSheet(nsIURI* aUri, nsIDocument* aLoaderDocument) = 0;
+ NS_IMETHOD SetSourceContentModel(nsIDocument* aDocument,
+ const nsTArray<nsCOMPtr<nsIContent>>& aSource) = 0;
+ NS_IMETHOD CancelLoads() = 0;
+
+ NS_IMETHOD AddXSLTParamNamespace(const nsString& aPrefix,
+ const nsString& aNamespace) = 0;
+ NS_IMETHOD AddXSLTParam(const nsString& aName,
+ const nsString& aNamespace,
+ const nsString& aValue,
+ const nsString& aSelect,
+ nsIDOMNode* aContextNode) = 0;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocumentTransformer,
+ NS_IDOCUMENTTRANSFORMER_IID)
+
+#endif //nsIDocumentTransformer_h__
diff --git a/dom/xslt/nsIXSLTProcessor.idl b/dom/xslt/nsIXSLTProcessor.idl
new file mode 100644
index 000000000..ce379020d
--- /dev/null
+++ b/dom/xslt/nsIXSLTProcessor.idl
@@ -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 "domstubs.idl"
+
+interface nsIVariant;
+
+[scriptable, uuid(4a91aeb3-4100-43ee-a21e-9866268757c5)]
+interface nsIXSLTProcessor : nsISupports
+{
+ /**
+ * Import the stylesheet into this XSLTProcessor for transformations.
+ *
+ * @param style The root-node of a XSLT stylesheet. This can be either
+ * a document node or an element node. If a document node
+ * then the document can contain either a XSLT stylesheet
+ * or a LRE stylesheet.
+ * If the argument is an element node it must be the
+ * xsl:stylesheet (or xsl:transform) element of an XSLT
+ * stylesheet.
+ */
+ void importStylesheet(in nsIDOMNode style);
+
+ /**
+ * Transforms the node source applying the stylesheet given by
+ * the importStylesheet() function. The owner document of the output node
+ * owns the returned document fragment.
+ *
+ * @param source The node to be transformed
+ * @param output This document is used to generate the output
+ * @return DocumentFragment The result of the transformation
+ */
+ nsIDOMDocumentFragment transformToFragment(in nsIDOMNode source,
+ in nsIDOMDocument output);
+
+ /**
+ * Transforms the node source applying the stylesheet given by the
+ * importStylesheet() function.
+ *
+ * @param source The node to be transformed
+ * @return Document The result of the transformation
+ */
+ nsIDOMDocument transformToDocument(in nsIDOMNode source);
+
+ /**
+ * Sets a parameter to be used in subsequent transformations with this
+ * nsIXSLTProcessor. If the parameter doesn't exist in the stylesheet the
+ * parameter will be ignored.
+ *
+ * @param namespaceURI The namespaceURI of the XSLT parameter
+ * @param localName The local name of the XSLT parameter
+ * @param value The new value of the XSLT parameter
+ *
+ * @exception NS_ERROR_ILLEGAL_VALUE The datatype of value is
+ * not supported
+ */
+ void setParameter(in DOMString namespaceURI,
+ in DOMString localName,
+ in nsIVariant value);
+
+ /**
+ * Gets a parameter if previously set by setParameter. Returns null
+ * otherwise.
+ *
+ * @param namespaceURI The namespaceURI of the XSLT parameter
+ * @param localName The local name of the XSLT parameter
+ * @return nsIVariant The value of the XSLT parameter
+ */
+ nsIVariant getParameter(in DOMString namespaceURI,
+ in DOMString localName);
+ /**
+ * Removes a parameter, if set. This will make the processor use the
+ * default-value for the parameter as specified in the stylesheet.
+ *
+ * @param namespaceURI The namespaceURI of the XSLT parameter
+ * @param localName The local name of the XSLT parameter
+ */
+ void removeParameter(in DOMString namespaceURI,
+ in DOMString localName);
+
+ /**
+ * Removes all set parameters from this nsIXSLTProcessor. This will make
+ * the processor use the default-value for all parameters as specified in
+ * the stylesheet.
+ */
+ void clearParameters();
+
+ /**
+ * Remove all parameters and stylesheets from this nsIXSLTProcessor.
+ */
+ void reset();
+};
diff --git a/dom/xslt/nsIXSLTProcessorPrivate.idl b/dom/xslt/nsIXSLTProcessorPrivate.idl
new file mode 100644
index 000000000..d88320cef
--- /dev/null
+++ b/dom/xslt/nsIXSLTProcessorPrivate.idl
@@ -0,0 +1,23 @@
+/* -*- 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 "nsISupports.idl"
+
+[scriptable, uuid(b8d727f7-67f4-4dc1-a318-ec0c87280816)]
+interface nsIXSLTProcessorPrivate : nsISupports
+{
+ /**
+ * Disables all loading of external documents, such as from
+ * <xsl:import> and document()
+ * Defaults to off and is *not* reset by calls to reset()
+ */
+ const unsigned long DISABLE_ALL_LOADS = 1;
+
+ /**
+ * Flags for this processor. Defaults to 0. See individual flags above
+ * for documentation for effect of reset()
+ */
+ attribute unsigned long flags;
+};
diff --git a/dom/xslt/tests/XSLTMark/XSLTMark-static.js b/dom/xslt/tests/XSLTMark/XSLTMark-static.js
new file mode 100644
index 000000000..d3284aac8
--- /dev/null
+++ b/dom/xslt/tests/XSLTMark/XSLTMark-static.js
@@ -0,0 +1,49 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
+
+const enablePrivilege = netscape.security.PrivilegeManager.enablePrivilege;
+const IOSERVICE_CTRID = "@mozilla.org/network/io-service;1";
+const nsIIOService = Components.interfaces.nsIIOService;
+const SIS_CTRID = "@mozilla.org/scriptableinputstream;1";
+const nsISIS = Components.interfaces.nsIScriptableInputStream;
+const nsIFilePicker = Components.interfaces.nsIFilePicker;
+const STDURL_CTRID = "@mozilla.org/network/standard-url;1";
+const nsIURI = Components.interfaces.nsIURI;
+
+Components.utils.import("resource://gre/modules/NetUtil.jsm");
+
+var gStop = false;
+
+function loadFile(aUriSpec)
+{
+ enablePrivilege('UniversalXPConnect');
+ var serv = Components.classes[IOSERVICE_CTRID].
+ getService(nsIIOService);
+ if (!serv) {
+ throw Components.results.ERR_FAILURE;
+ }
+ var chan = NetUtil.newChannel({
+ uri: aUriSpec,
+ loadUsingSystemPrincipal: true
+ });
+ var instream =
+ Components.classes[SIS_CTRID].createInstance(nsISIS);
+ instream.init(chan.open2());
+
+ return instream.read(instream.available());
+}
+
+function dump20(aVal)
+{
+ const pads = ' ';
+ if (typeof(aVal)=='string')
+ out = aVal;
+ else if (typeof(aVal)=='number')
+ out = Number(aVal).toFixed(2);
+ else
+ out = new String(aVal);
+ dump(pads.substring(0, 20 - out.length));
+ dump(out);
+}
diff --git a/dom/xslt/tests/XSLTMark/XSLTMark-test.js b/dom/xslt/tests/XSLTMark/XSLTMark-test.js
new file mode 100644
index 000000000..4037c75e4
--- /dev/null
+++ b/dom/xslt/tests/XSLTMark/XSLTMark-test.js
@@ -0,0 +1,46 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
+
+var gParser = new DOMParser;
+var gTimeout;
+
+function Test(aTitle, aSourceURL, aStyleURL, aNumber, aObserver)
+{
+ this.mTitle = aTitle;
+ this.mObserver = aObserver;
+ this.mTotal = aNumber;
+ this.mDone = 0;
+ var xmlcontent = loadFile(aSourceURL);
+ var xslcontent = loadFile(aStyleURL);
+ this.mSource = gParser.parseFromString(xmlcontent, 'application/xml');
+ this.mStyle = gParser.parseFromString(xslcontent, 'application/xml');
+}
+
+function runTest(aTitle, aSourceURL, aStyleURL, aNumber, aObserver)
+{
+ test = new Test(aTitle, aSourceURL, aStyleURL, aNumber,
+ aObserver);
+ gTimeout = setTimeout(onNextTransform, 100, test, 0);
+}
+
+function onNextTransform(aTest, aNumber)
+{
+ var proc = new XSLTProcessor;
+ var startTime = Date.now();
+ proc.importStylesheet(aTest.mStyle);
+ var res = proc.transformToDocument(aTest.mSource);
+ var endTime = Date.now();
+ aNumber++;
+ var progress = aNumber / aTest.mTotal * 100;
+ if (aTest.mObserver) {
+ aTest.mObserver.progress(aTest.mTitle, endTime - startTime,
+ progress);
+ }
+ if (aNumber < aTest.mTotal) {
+ gTimeout = setTimeout(onNextTransform, 100, aTest, aNumber);
+ } else if (aTest.mObserver) {
+ aTest.mObserver.done(aTest.mTitle);
+ }
+}
diff --git a/dom/xslt/tests/XSLTMark/XSLTMark-view.js b/dom/xslt/tests/XSLTMark/XSLTMark-view.js
new file mode 100644
index 000000000..7342d0d9a
--- /dev/null
+++ b/dom/xslt/tests/XSLTMark/XSLTMark-view.js
@@ -0,0 +1,175 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
+
+var view =
+{
+ configUrl: null,
+ testArray: null,
+ mCurrent: null,
+
+ browseForConfig: function()
+ {
+ enablePrivilege('UniversalXPConnect');
+ var fp = Components.classes["@mozilla.org/filepicker;1"].
+ createInstance(nsIFilePicker);
+ fp.init(window,'XSLTMark Description File',nsIFilePicker.modeOpen);
+ fp.appendFilter('*.conf', '*.conf');
+ fp.appendFilters(nsIFilePicker.filterAll);
+ var res = fp.show();
+
+ if (res == nsIFilePicker.returnOK) {
+ this.configUrl = Components.classes[STDURL_CTRID].createInstance(nsIURI);
+ this.configUrl.spec = fp.fileURL.spec;
+ document.getElementById('config').setAttribute('value', this.configUrl.spec);
+ }
+ this.parseConfig();
+ return true;
+ },
+
+ parseConfig: function()
+ {
+ this.testArray = new Array();
+ var test;
+ if (!this.configUrl) {
+ return;
+ }
+
+ var content = loadFile(this.configUrl.spec);
+
+ var lines = content.split("\n");
+ var line, res;
+ var head = /^\[(.+)\]$/;
+ var instruct = /^(.+)=(.+)$/;
+ while (lines.length) {
+ line = lines.shift();
+ if (head.test(line)) {
+ test = new Object;
+ res = head.exec(line);
+ test['title'] = res[1];
+ this.testArray.push(test);
+ }
+ else if (line == '') {
+ test = undefined;
+ }
+ else {
+ res = instruct.exec(line);
+ test[res[1]] = res[2];
+ }
+ }
+ },
+
+ onLoad: function()
+ {
+ this.mCurrentStatus = document.getElementById('currentStatus');
+ this.mCurrentProgress = document.getElementById('currentProgress');
+ this.mTotalProgress = document.getElementById('totalProgress');
+ this.mOutput = document.getElementById('transformOutput');
+ this.mDetailOutput =
+ document.getElementById('transformDetailedOutput');
+ this.mDetail = true;
+ },
+
+ progress: function(aTitle, aTime, aProgress)
+ {
+ // dump20(aTitle);
+ // dump20(aTime);
+ // dump20(aProgress);
+ this.mCurrentProgress.value = aProgress;
+ this.displayDetailTime(aTime);
+ this.mTimes.push(aTime);
+ // dump("\n");
+ },
+
+ done: function(aTitle)
+ {
+ // dump(aTitle + " is finished.\n");
+ this.mCurrent++;
+ this.mCurrentProgress.value = 0;
+ this.displayTotalTime();
+ if (this.mCurrent >= this.testArray.length) {
+ this.mTotalProgress.value = 0;
+ this.mCurrentStatus.value = "done";
+ return;
+ }
+ this.mTotalProgress.value = this.mCurrent*100/this.testArray.length;
+ var test = this.testArray[this.mCurrent];
+ enablePrivilege('UniversalXPConnect');
+ this.displayTest(test.title);
+ runTest(test.title, this.configUrl.resolve(test.input),
+ this.configUrl.resolve(test.stylesheet),
+ test.iterations, this);
+ },
+
+ onStop: function()
+ {
+ clearTimeout(gTimeout);
+ this.mCurrentProgress.value = 0;
+ this.mTotalProgress.value = 0;
+ this.mCurrentStatus.value = "stopped";
+ },
+
+ displayTest: function(aTitle)
+ {
+ this.mTimes = new Array;
+ aTitle += "\t";
+ this.mCurrentStatus.value = aTitle;
+ this.mOutput.value += aTitle;
+ if (this.mDetail) {
+ this.mDetailOutput.value += aTitle;
+ }
+ },
+
+ displayDetailTime: function(aTime)
+ {
+ if (this.mDetail) {
+ this.mDetailOutput.value += aTime + " ms\t";
+ }
+ },
+
+ displayTotalTime: function()
+ {
+ var sum = 0;
+ for (k = 0; k < this.mTimes.length; k++) {
+ sum += this.mTimes[k];
+ }
+ var mean = sum / this.mTimes.length;
+ this.mOutput.value += Number(mean).toFixed(2) + " ms\t" + sum + " ms\t";
+ var variance = 0;
+ for (k = 0; k < this.mTimes.length; k++) {
+ var n = this.mTimes[k] - mean;
+ variance += n*n;
+ }
+ variance = Math.sqrt(variance/this.mTimes.length);
+ this.mOutput.value += Number(variance).toFixed(2)+"\n";
+ if (this.mDetail) {
+ this.mDetailOutput.value += "\n";
+ }
+ },
+
+ runBenchmark: function()
+ {
+ enablePrivilege('UniversalXPConnect');
+ if (!this.testArray) {
+ if (!this.configUrl) {
+ this.configUrl = Components.classes[STDURL_CTRID].createInstance(nsIURI);
+ this.configUrl.spec = document.getElementById('config').value;
+ }
+ this.parseConfig();
+ }
+
+ this.mCurrent = 0;
+ var test = this.testArray[this.mCurrent];
+ this.mOutput.value = '';
+ if (this.mDetail) {
+ this.mDetailOutput.value = '';
+ }
+ this.displayTest(test.title);
+ runTest(test.title, this.configUrl.resolve(test.input),
+ this.configUrl.resolve(test.stylesheet),
+ test.iterations, this);
+ return true;
+ }
+}
+
diff --git a/dom/xslt/tests/XSLTMark/XSLTMark.css b/dom/xslt/tests/XSLTMark/XSLTMark.css
new file mode 100644
index 000000000..80386ae2d
--- /dev/null
+++ b/dom/xslt/tests/XSLTMark/XSLTMark.css
@@ -0,0 +1,8 @@
+/* -*- Mode: Java; 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/. */
+
+textbox.out {
+ white-space: pre;
+}
diff --git a/dom/xslt/tests/XSLTMark/XSLTMark.xul b/dom/xslt/tests/XSLTMark/XSLTMark.xul
new file mode 100644
index 000000000..bc34a7a03
--- /dev/null
+++ b/dom/xslt/tests/XSLTMark/XSLTMark.xul
@@ -0,0 +1,52 @@
+<?xml version="1.0"?><!-- -*- Mode: xml; tab-width: 2; indent-tabs-mode: nil -*- -->
+<!-- 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/. -->
+
+<?xml-stylesheet href="chrome://communicator/skin/" type="text/css"?>
+<?xml-stylesheet href="XSLTMark.css" type="text/css"?>
+<window id="XSLTMarkHarness"
+ title="XSLTMark"
+ onload="view.onLoad()"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ orient="vertical">
+<script type="application/x-javascript" src="XSLTMark-static.js" />
+<script type="application/x-javascript" src="XSLTMark-test.js" />
+<script type="application/x-javascript" src="XSLTMark-view.js" />
+
+<hbox>
+ <groupbox orient="horizontal">
+ <caption label="test description file" />
+ <label value=""/><!-- needed, otherwise groupbox fucks up :-( -->
+ <textbox id="config" persist="value" readonly="true"/>
+ <button label="browse..." oncommand="view.browseForConfig();" />
+ </groupbox>
+ <groupbox orient="horizontal">
+ <caption label="test control" />
+ <button label="run..."
+ oncommand="setTimeout('view.runBenchmark();', 0);" />
+ <button label="stop" oncommand="view.onStop();" />
+ </groupbox>
+ <groupbox orient="horizontal">
+ <caption label="options" />
+ <label value="responsiveness: "/>
+ <menulist label="sloppy">
+ <menupopup>
+ <menuitem label="sloppy" selected="true"/>
+ <menuitem label="bad"/>
+ </menupopup>
+ </menulist>
+ </groupbox>
+</hbox>
+<hbox>
+ <textbox id="currentStatus" readonly="true" flex="1"/>
+ <progressmeter id="currentProgress" mode="normal" value="0" flex="2"/>
+ <progressmeter id="totalProgress" mode="normal" value="0" flex="2"/>
+</hbox>
+<hbox flex="1">
+ <textbox id="transformOutput" class="out" readonly="true" multiline="true" flex="1"/>
+</hbox>
+<hbox flex="1">
+ <textbox id="transformDetailedOutput" class="out" readonly="true" multiline="true" flex="1"/>
+</hbox>
+</window> \ No newline at end of file
diff --git a/dom/xslt/tests/buster/DiffDOM.js b/dom/xslt/tests/buster/DiffDOM.js
new file mode 100644
index 000000000..587214ff3
--- /dev/null
+++ b/dom/xslt/tests/buster/DiffDOM.js
@@ -0,0 +1,103 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
+
+// ----------------------
+// DiffDOM(node1,node2)
+// ----------------------
+
+var isHTML = false;
+
+function DiffDOM(node1, node2, aIsHTML)
+{
+ isHTML = aIsHTML;
+ return DiffNodeAndChildren(node1, node2);
+}
+
+
+// namespace attributes in the second node are ignored
+const nsreg = /^xmlns[|:\w]/;
+
+// This function does the work of DiffDOM by recursively calling
+// itself to explore the tree
+function DiffNodeAndChildren(node1, node2)
+{
+ if (!node1 && !node2)
+ return true;
+ if (!node1 || !node2)
+ return ErrorUp("One of the nodes is null", node1, node2);
+ if (node1.type!=node2.type)
+ return ErrorUp("Different node types", node1, node2);
+
+ var attributes = node2.attributes;
+ if (attributes && attributes.length) {
+ var item, name, ns, value, otherValue;
+ for (var index = 0; index < attributes.length; index++) {
+ item = attributes.item(index);
+ ns = item.namespaceURI;
+ if (ns) {
+ name = item.localName;
+ otherValue = node2.getAttributeNS(ns, name);
+ }
+ else {
+ name = item.nodeName;
+ otherValue = node2.getAttribute(name);
+ }
+ value = item.nodeValue;
+ if (!nsreg.test(name) && otherValue!=value) {
+ return ErrorUp("Different values for attribute", node1, node2);
+ }
+ }
+ }
+ else if (node1.attributes && node1.attributes.length) {
+ return ErrorUp("Different number of attributes", node1, node2);
+ }
+
+ if (isHTML) {
+ if (node1.nodeName.toLowerCase()!=node2.nodeName.toLowerCase())
+ return ErrorUp("Different node names", node1, node2);
+ }
+ else {
+ if (node1.nodeName!=node2.nodeName)
+ return ErrorUp("Different node names", node1, node2);
+ }
+ if (node1.nodeValue!=node2.nodeValue)
+ return ErrorUp("Different node values", node1, node2);
+ if (!isHTML)
+ if (node1.namespaceURI!=node2.namespaceURI)
+ return ErrorUp("Different namespace", node1, node2);
+ if (node1.hasChildNodes() != node2.hasChildNodes())
+ return ErrorUp("Different children", node1, node2);
+ if (node1.childNodes) {
+ if (node1.childNodes.length != node2.childNodes.length)
+ return ErrorUp("Different number of children", node1, node2);
+ for (var child = 0; child < node1.childNodes.length; child++) {
+ if (!DiffNodeAndChildren(node1.childNodes[child],
+ node2.childNodes[child])) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+function ErrorUp(errMsg, node1, node2)
+{
+ dump("Error: "+errMsg+"\n");
+ if (node1) {
+ dump("Node 1: "+node1+", ");
+ if (node1.nodeType == Node.TEXT_NODE)
+ dump("nodeValue: "+node1.nodeValue+"\n");
+ else
+ dump("nodeName: "+node1.namespaceURI+":"+node1.nodeName+"\n");
+ }
+ if (node2) {
+ dump("Node 2: "+node2+", ");
+ if (node2.nodeType == Node.TEXT_NODE)
+ dump("nodeValue: "+node2.nodeValue+"\n");
+ else
+ dump("nodeName: "+node2.namespaceURI+":"+node2.nodeName+"\n");
+ }
+ return false;
+}
diff --git a/dom/xslt/tests/buster/DumpDOM.js b/dom/xslt/tests/buster/DumpDOM.js
new file mode 100644
index 000000000..0a90ce9d0
--- /dev/null
+++ b/dom/xslt/tests/buster/DumpDOM.js
@@ -0,0 +1,85 @@
+// ----------------------
+// DumpDOM(node)
+//
+// Call this function to dump the contents of the DOM starting at the specified node.
+// Use node = document.documentElement to dump every element of the current document.
+// Use node = top.window.document.documentElement to dump every element.
+//
+// 8-13-99 Updated to dump almost all attributes of every node. There are still some attributes
+// that are purposely skipped to make it more readable.
+// ----------------------
+function DumpDOM(node)
+{
+ dump("--------------------- DumpDOM ---------------------\n");
+
+ DumpNodeAndChildren(node, "");
+
+ dump("------------------- End DumpDOM -------------------\n");
+}
+
+
+// This function does the work of DumpDOM by recursively calling itself to explore the tree
+function DumpNodeAndChildren(node, prefix)
+{
+ dump(prefix + "<" + node.nodeName);
+
+ var attributes = node.attributes;
+
+ if ( attributes && attributes.length )
+ {
+ var item, name, value;
+
+ for ( var index = 0; index < attributes.length; index++ )
+ {
+ item = attributes.item(index);
+ name = item.nodeName;
+ value = item.nodeValue;
+
+ if ( (name == 'lazycontent' && value == 'true') ||
+ (name == 'xulcontentsgenerated' && value == 'true') ||
+ (name == 'id') ||
+ (name == 'instanceOf') )
+ {
+ // ignore these
+ }
+ else
+ {
+ dump(" " + name + "=\"" + value + "\"");
+ }
+ }
+ }
+
+ if ( node.nodeType == 1 )
+ {
+ // id
+ var text = node.getAttribute('id');
+ if ( text && text[0] != '$' )
+ dump(" id=\"" + text + "\"");
+ }
+
+ if ( node.nodeType == Node.TEXT_NODE )
+ dump(" = \"" + node.data + "\"");
+
+ dump(">\n");
+
+ // dump IFRAME && FRAME DOM
+ if ( node.nodeName == "IFRAME" || node.nodeName == "FRAME" )
+ {
+ if ( node.name )
+ {
+ var wind = top.frames[node.name];
+ if ( wind && wind.document && wind.document.documentElement )
+ {
+ dump(prefix + "----------- " + node.nodeName + " -----------\n");
+ DumpNodeAndChildren(wind.document.documentElement, prefix + " ");
+ dump(prefix + "--------- End " + node.nodeName + " ---------\n");
+ }
+ }
+ }
+ // children of nodes (other than frames)
+ else if ( node.childNodes )
+ {
+ for ( var child = 0; child < node.childNodes.length; child++ )
+ DumpNodeAndChildren(node.childNodes[child], prefix + " ");
+ }
+}
diff --git a/dom/xslt/tests/buster/ReadMe b/dom/xslt/tests/buster/ReadMe
new file mode 100644
index 000000000..82ad04d96
--- /dev/null
+++ b/dom/xslt/tests/buster/ReadMe
@@ -0,0 +1,22 @@
+The buster is a XUL interface to the conformance tests shipped as part of
+Xalan. For information about Xalan, please see http://xml.apache.org/.
+For your convenience we provide a packed distribution of all needed files
+in http://www.axel.pike.org/mozilla/xalan.tar.gz. Please see the included
+LICENSE.txt or http://xml.apache.org/dist/LICENSE.txt for terms of
+distributing those files.
+
+To use the buster, open buster.xul with an XSLT enabled Mozilla.
+Open the rdf index file shipped with the test package into the
+"Xalan index", and the available tests will show up as a tree.
+Once you have selected the tests you're interested in, press the button
+"run checked tests", and all the tests will be run.
+You can save the results into an rdf, and load it for comparison and
+regression hunting.
+
+DiffDOM tries to find out, which tests failed, and will DumpDOM both the
+result and the reference solution. Not all reference solutions load
+properly, those need manual love.
+
+Good luck and fun
+
+Axel Hecht <axel@pike.org>
diff --git a/dom/xslt/tests/buster/buster-files.js b/dom/xslt/tests/buster/buster-files.js
new file mode 100644
index 000000000..cbdb8bdd2
--- /dev/null
+++ b/dom/xslt/tests/buster/buster-files.js
@@ -0,0 +1,81 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
+
+const kFileOutStreamCID = "@mozilla.org/network/file-output-stream;1";
+const nsIFileOutputStream = Components.interfaces.nsIFileOutputStream;
+
+var cmdFileController =
+{
+ supportsCommand: function(aCommand)
+ {
+ switch(aCommand) {
+ case 'cmd_fl_save':
+ case 'cmd_fl_import':
+ return true;
+ default:
+ }
+ return false;
+ },
+ isCommandEnabled: function(aCommand)
+ {
+ return this.supportsCommand(aCommand);
+ },
+ doCommand: function(aCommand)
+ {
+ switch(aCommand) {
+ case 'cmd_fl_save':
+ var sink = new Object;
+ sink.write = function(aContent, aCount)
+ {
+ // replace NC:succ with NC:orig_succ,
+ // so the rdf stuff differs
+ var content = aContent.replace(/NC:succ/g,"NC:orig_succ");
+ content = content.replace(/NC:failCount/g,"NC:orig_failCount");
+ this.mSink.write(content, content.length);
+ return aCount;
+ };
+ var fp = doCreateRDFFP('Xalan results',
+ nsIFilePicker.modeSave);
+ var res = fp.show();
+
+ if (res == nsIFilePicker.returnOK ||
+ res == nsIFilePicker.returnReplace) {
+ var serial = doCreate(kRDFXMLSerializerID,
+ nsIRDFXMLSerializer);
+ serial.init(view.mResultDS);
+ serial.QueryInterface(nsIRDFXMLSource);
+ var fl = fp.file;
+ var fstream = doCreate(kFileOutStreamCID,
+ nsIFileOutputStream);
+ fstream.init(fl, 26, 420, 0);
+ sink.mSink = fstream;
+ serial.Serialize(sink);
+ }
+ break;
+ case 'cmd_fl_import':
+ var fp = doCreateRDFFP('Previous Xalan results',
+ nsIFilePicker.modeLoad);
+ var res = fp.show();
+
+ if (res == nsIFilePicker.returnOK) {
+ var fl = fp.file;
+ if (view.mPreviousResultDS) {
+ view.database.RemoveDataSource(view.mPreviousResultDS);
+ view.mPreviousResultDS = null;
+ }
+ view.mPreviousResultDS = kRDFSvc.GetDataSource(fp.fileURL.spec);
+ view.database.AddDataSource(view.mPreviousResultDS);
+ }
+
+ document.getElementById('obs_orig_success')
+ .setAttribute('hidden','false');
+ break;
+ default:
+ alert('Unknown Command'+aCommand);
+ }
+ }
+};
+
+registerController(cmdFileController);
diff --git a/dom/xslt/tests/buster/buster-handlers.js b/dom/xslt/tests/buster/buster-handlers.js
new file mode 100644
index 000000000..4e6f85fcc
--- /dev/null
+++ b/dom/xslt/tests/buster/buster-handlers.js
@@ -0,0 +1,37 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
+
+var xalan_field;
+
+function onLoad()
+{
+ view.tree = document.getElementById('out');
+ view.boxObject = view.tree.boxObject;
+ {
+ view.mIframe = document.getElementById('hiddenHtml');
+ view.mIframe.webNavigation.allowPlugins = false;
+ view.mIframe.webNavigation.allowJavascript = false;
+ view.mIframe.webNavigation.allowMetaRedirects = false;
+ view.mIframe.webNavigation.allowImages = false;
+ }
+ view.database = view.tree.database;
+ view.builder = view.tree.builder.QueryInterface(nsIXULTemplateBuilder);
+ view.builder.QueryInterface(nsIXULTreeBuilder);
+ runItem.prototype.kDatabase = view.database;
+ xalan_field = document.getElementById("xalan_rdf");
+ var persistedUrl = xalan_field.getAttribute('url');
+ if (persistedUrl) {
+ view.xalan_url = persistedUrl;
+ xalan_field.value = persistedUrl;
+ }
+ view.setDataSource();
+ return true;
+}
+
+function onUnload()
+{
+ if (xalan_field)
+ xalan_field.setAttribute('url', xalan_field.value);
+}
diff --git a/dom/xslt/tests/buster/buster-statics.js b/dom/xslt/tests/buster/buster-statics.js
new file mode 100644
index 000000000..b70cef511
--- /dev/null
+++ b/dom/xslt/tests/buster/buster-statics.js
@@ -0,0 +1,87 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
+
+// helper function to shortcut component creation
+function doCreate(aContract, aInterface)
+{
+ return Components.classes[aContract].createInstance(aInterface);
+}
+
+// for the items, loading a text file
+const IOSERVICE_CTRID = "@mozilla.org/network/io-service;1";
+const nsIIOService = Components.interfaces.nsIIOService;
+const SIS_CTRID = "@mozilla.org/scriptableinputstream;1"
+const nsISIS = Components.interfaces.nsIScriptableInputStream;
+
+// rdf foo, onload handler
+const kRDFSvcContractID = "@mozilla.org/rdf/rdf-service;1";
+const kRDFInMemContractID =
+ "@mozilla.org/rdf/datasource;1?name=in-memory-datasource";
+const kRDFContUtilsID = "@mozilla.org/rdf/container-utils;1";
+const kRDFXMLSerializerID = "@mozilla.org/rdf/xml-serializer;1";
+const kIOSvcContractID = "@mozilla.org/network/io-service;1";
+const kStandardURL = Components.classes["@mozilla.org/network/standard-url;1"];
+const nsIURL = Components.interfaces.nsIURL;
+const nsIStandardURL = Components.interfaces.nsIStandardURL;
+const nsIFilePicker = Components.interfaces.nsIFilePicker;
+const nsIXULTreeBuilder = Components.interfaces.nsIXULTreeBuilder;
+const nsIXULTemplateBuilder = Components.interfaces.nsIXULTemplateBuilder;
+const kIOSvc = Components.classes[kIOSvcContractID]
+ .getService(Components.interfaces.nsIIOService);
+const nsIRDFService = Components.interfaces.nsIRDFService;
+const nsIRDFDataSource = Components.interfaces.nsIRDFDataSource;
+const nsIRDFRemoteDataSource = Components.interfaces.nsIRDFRemoteDataSource;
+const nsIRDFPurgeableDataSource =
+ Components.interfaces.nsIRDFPurgeableDataSource;
+const nsIRDFResource = Components.interfaces.nsIRDFResource;
+const nsIRDFLiteral = Components.interfaces.nsIRDFLiteral;
+const nsIRDFInt = Components.interfaces.nsIRDFInt;
+const nsIRDFContainerUtils = Components.interfaces.nsIRDFContainerUtils;
+const nsIRDFXMLSerializer = Components.interfaces.nsIRDFXMLSerializer;
+const nsIRDFXMLSource = Components.interfaces.nsIRDFXMLSource;
+const kRDFSvc =
+ Components.classes[kRDFSvcContractID].getService(nsIRDFService);
+const krTypeCat = kRDFSvc.GetResource("http://home.netscape.com/NC-rdf#category");
+const krTypeFailCount = kRDFSvc.GetResource("http://home.netscape.com/NC-rdf#failCount");
+const krTypeName = kRDFSvc.GetResource("http://home.netscape.com/NC-rdf#name");
+const krTypeSucc = kRDFSvc.GetResource("http://home.netscape.com/NC-rdf#succ");
+const krTypeOrigSucc = kRDFSvc.GetResource("http://home.netscape.com/NC-rdf#orig_succ");
+const krTypeOrigFailCount = kRDFSvc.GetResource("http://home.netscape.com/NC-rdf#orig_failCount");
+const krTypeOrigSuccCount = kRDFSvc.GetResource("http://home.netscape.com/NC-rdf#orig_succCount");
+const krTypePath = kRDFSvc.GetResource("http://home.netscape.com/NC-rdf#path");
+const krTypeParent = kRDFSvc.GetResource("http://home.netscape.com/NC-rdf#parent");
+const krTypePurp = kRDFSvc.GetResource("http://home.netscape.com/NC-rdf#purp");
+const krTypeSuccCount = kRDFSvc.GetResource("http://home.netscape.com/NC-rdf#succCount");
+const kGood = kRDFSvc.GetLiteral("yes");
+const kBad = kRDFSvc.GetLiteral("no");
+const kMixed = kRDFSvc.GetLiteral("+-");
+const kContUtils = doCreate(kRDFContUtilsID, nsIRDFContainerUtils);
+
+function doCreateRDFFP(aTitle, aMode)
+{
+ var fp = doCreate("@mozilla.org/filepicker;1", nsIFilePicker);
+ fp.init(window, aTitle, aMode);
+ fp.appendFilter('*.rdf', '*.rdf');
+ fp.appendFilters(nsIFilePicker.filterAll);
+ return fp;
+}
+
+function goDoCommand(aCommand)
+{
+ try {
+ var controller =
+ top.document.commandDispatcher.getControllerForCommand(aCommand);
+ if (controller && controller.isCommandEnabled(aCommand))
+ controller.doCommand(aCommand);
+ }
+ catch(e) {
+ dump("An error "+e+" occurred executing the "+aCommand+" command\n");
+ }
+}
+
+function registerController(aController)
+{
+ top.controllers.appendController(aController);
+}
diff --git a/dom/xslt/tests/buster/buster-test.js b/dom/xslt/tests/buster/buster-test.js
new file mode 100644
index 000000000..2cc838685
--- /dev/null
+++ b/dom/xslt/tests/buster/buster-test.js
@@ -0,0 +1,356 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
+
+Components.utils.import("resource://gre/modules/NetUtil.jsm");
+
+var parser = new DOMParser();
+var methodExpr = (new XPathEvaluator).createExpression("xsl:output/@method",
+ {
+ lookupNamespaceURI: function(aPrefix)
+ {
+ if (aPrefix == "xsl")
+ return "http://www.w3.org/1999/XSL/Transform";
+ return "";
+ }
+ });
+
+const nsIWebProgListener = Components.interfaces.nsIWebProgressListener;
+
+var runQueue =
+{
+ mArray : new Array(),
+ push : function(aRunItem)
+ {
+ this.mArray.push(aRunItem);
+ },
+ observe : function(aSubject, aTopic, aData)
+ {
+ var item = this.mArray.shift();
+ if (item) {
+ item.run(this);
+ }
+ },
+ run : function()
+ {
+ this.observe(null,'','');
+ }
+}
+
+var itemCache =
+{
+ mArray : new Array(),
+ getItem : function(aResource)
+ {
+ // Directory selected
+ if (kContUtils.IsSeq(runItem.prototype.kDatabase, aResource)) {
+ var aSeq = kContUtils.MakeSeq(runItem.prototype.kDatabase, aResource);
+ dump("sequence: "+aSeq+" with "+aSeq.GetCount()+" elements\n");
+ var child, children = aSeq.GetElements();
+ var m = 0, first;
+ while (children.hasMoreElements()) {
+ m += 1;
+ child = children.getNext();
+ child.QueryInterface(nsIRDFResource);
+ if (!first)
+ first = itemCache.getItem(child);
+ else
+ itemCache.getItem(child);
+ }
+ return first;
+ }
+ if (aResource.Value in this.mArray) {
+ return this.mArray[aResource.Value];
+ }
+ var retItem = new runItem(aResource);
+ this.mArray[aResource.Value] = retItem;
+ runQueue.push(retItem);
+ return retItem;
+ },
+ rerunItem : function(aResource, aObserver)
+ {
+ var anItem = new runItem(aResource);
+ this.mArray[aResource.Value] = anItem;
+ anItem.run(aObserver);
+ },
+ observe : function(aSubject, aTopic, aData)
+ {
+ this.mRun += 1;
+ if (aTopic == "success") {
+ if (aData == "yes") {
+ this.mGood += 1;
+ }
+ else {
+ this.mFalse +=1;
+ }
+ }
+ }
+}
+
+function runItem(aResource)
+{
+ this.mResource = aResource;
+ // Directory selected
+ if (kContUtils.IsSeq(this.kDatabase,this.mResource)) {
+ var aSeq = kContUtils.MakeSeq(this.kDatabase,this.mResource);
+ dump("THIS SHOULDN'T HAPPEN\n");
+ var child, children = aSeq.GetElements();
+ var m = 0;
+ while (children.hasMoreElements()) {
+ m += 1;
+ child = children.getNext();
+ child.QueryInterface(nsIRDFResource);
+ itemCache.getItem(child);
+ }
+ }
+}
+
+runItem.prototype =
+{
+ // RDF resource associated with this test
+ mResource : null,
+ // XML documents for the XSLT transformation
+ mSourceDoc : null,
+ mStyleDoc : null,
+ mResDoc : null,
+ // XML or plaintext document for the reference
+ mRefDoc : null,
+ // bitfield signaling the loaded documents
+ mLoaded : 0,
+ kSource : 1,
+ kStyle : 2,
+ kReference : 4,
+ // a observer, potential argument to run()
+ mObserver : null,
+ mSuccess : null,
+ mMethod : 'xml',
+ // XSLTProcessor, shared by the instances
+ kProcessor : new XSLTProcessor(),
+ kXalan : kStandardURL.createInstance(nsIURL),
+ kDatabase : null,
+ kObservers : new Array(),
+
+ run : function(aObserver)
+ {
+ if (aObserver && typeof(aObserver)=='function' ||
+ (typeof(aObserver)=='object' &&
+ typeof(aObserver.observe)=='function')) {
+ this.mObserver=aObserver;
+ }
+ var name = this.kDatabase.GetTarget(this.mResource, krTypeName, true);
+ if (name) {
+ var cat = this.kDatabase.GetTarget(this.mResource, krTypeCat, true);
+ var path = this.kDatabase.GetTarget(this.mResource, krTypePath, true);
+ cat = cat.QueryInterface(nsIRDFLiteral);
+ name = name.QueryInterface(nsIRDFLiteral);
+ path = path.QueryInterface(nsIRDFLiteral);
+ var xalan_fl = this.kXalan.resolve(cat.Value+"/"+path.Value);
+ var xalan_ref = this.kXalan.resolve(cat.Value+"-gold/"+path.Value);
+ this.mRefURL =
+ this.kXalan.resolve(cat.Value + "-gold/" + path.Value + ".out");
+ dump(name.Value+" links to "+xalan_fl+"\n");
+ }
+ // Directory selected
+ if (kContUtils.IsSeq(this.kDatabase,this.mResource)) {
+ return;
+ var aSeq = kContUtils.MakeSeq(this.kDatabase,this.mResource);
+ dump("sequence: "+aSeq+" with "+aSeq.GetCount()+" elements\n");
+ var child, children = aSeq.GetElements();
+ var m = 0;
+ while (children.hasMoreElements()) {
+ m += 1;
+ child = children.getNext();
+ child.QueryInterface(nsIRDFResource);
+ }
+ }
+ this.mSourceDoc = document.implementation.createDocument('', '', null);
+ this.mSourceDoc.addEventListener("load",this.onload(1),false);
+ this.mSourceDoc.load(xalan_fl+".xml");
+ this.mStyleDoc = document.implementation.createDocument('', '', null);
+ this.mStyleDoc.addEventListener("load",this.styleLoaded(),false);
+ this.mStyleDoc.load(xalan_fl+".xsl");
+ },
+
+ // nsIWebProgressListener
+ QueryInterface: function(aIID)
+ {
+ return this;
+ },
+ onStateChange: function(aProg, aRequest, aFlags, aStatus)
+ {
+ if ((aFlags & nsIWebProgListener.STATE_STOP) &&
+ (aFlags & nsIWebProgListener.STATE_IS_DOCUMENT)) {
+ aProg.removeProgressListener(this);
+ this.mRefDoc = document.getElementById('hiddenHtml').contentDocument;
+ this.fileLoaded(4);
+ }
+ },
+ onProgressChange: function(aProg, b,c,d,e,f)
+ {
+ },
+ onLocationChange: function(aProg, aRequest, aURI, aFlags)
+ {
+ },
+ onStatusChange: function(aProg, aRequest, aStatus, aMessage)
+ {
+ },
+ onSecurityChange: function(aWebProgress, aRequest, aState)
+ {
+ },
+
+ // onload handler helper
+ onload : function(file)
+ {
+ var self = this;
+ return function(e)
+ {
+ return self.fileLoaded(file);
+ };
+ },
+
+ styleLoaded : function()
+ {
+ var self = this;
+ return function(e)
+ {
+ return self.styleLoadedHelper();
+ };
+ },
+ styleLoadedHelper : function()
+ {
+ var method = methodExpr.evaluate(this.mStyleDoc.documentElement, 2,
+ null).stringValue;
+ var refContent;
+ if (!method) {
+ // implicit method, guess from result
+ refContent = this.loadTextFile(this.mRefURL);
+ if (refContent.match(/^\s*<html/gi)) {
+ method = 'html';
+ }
+ else {
+ method = 'xml';
+ }
+ }
+ this.mMethod = method;
+
+ switch (method) {
+ case 'xml':
+ if (!refContent) {
+ refContent = this.loadTextFile(this.mRefURL);
+ }
+ this.mRefDoc = parser.parseFromString(refContent, 'application/xml');
+ this.mLoaded += 4;
+ break;
+ case 'html':
+ view.loadHtml(this.mRefURL, this);
+ break;
+ case 'text':
+ if (!refContent) {
+ refContent = this.loadTextFile(this.mRefURL);
+ }
+ const ns = 'http://www.mozilla.org/TransforMiix';
+ const qn = 'transformiix:result';
+ this.mRefDoc =
+ document.implementation.createDocument(ns, qn, null);
+ var txt = this.mRefDoc.createTextNode(refContent);
+ this.mRefDoc.documentElement.appendChild(txt);
+ this.mLoaded += 4;
+ break;
+ default:
+ throw "unkown XSLT output method";
+ }
+ this.fileLoaded(2)
+ },
+
+ fileLoaded : function(mask)
+ {
+ this.mLoaded += mask;
+ if (this.mLoaded < 7) {
+ return;
+ }
+ this.doTransform();
+ },
+
+ doTransform : function()
+ {
+ this.kProcessor.reset();
+ try {
+ this.kProcessor.importStylesheet(this.mStyleDoc);
+ this.mResDoc =
+ this.kProcessor.transformToDocument(this.mSourceDoc);
+ this.mRefDoc.normalize();
+ isGood = DiffDOM(this.mResDoc.documentElement,
+ this.mRefDoc.documentElement,
+ this.mMethod == 'html');
+ } catch (e) {
+ isGood = false;
+ };
+ dump("This succeeded. "+isGood+"\n");
+ isGood = isGood.toString();
+ for (var i=0; i<this.kObservers.length; i++) {
+ var aObs = this.kObservers[i];
+ if (typeof(aObs)=='object' && typeof(aObs.observe)=='function') {
+ aObs.observe(this.mResource, 'success', isGood);
+ }
+ else if (typeof(aObs)=='function') {
+ aObs(this.mResource, 'success', isGood);
+ }
+ }
+ if (this.mObserver) {
+ if (typeof(this.mObserver)=='object') {
+ this.mObserver.observe(this.mResource, 'success', isGood);
+ }
+ else {
+ this.mObserver(this.mResource, 'success', isGood);
+ }
+ }
+ },
+
+ loadTextFile : function(url)
+ {
+ var chan = NetUtil.newChannel({
+ uri: url,
+ loadUsingSystemPrincipal: true
+ });
+ var instream = doCreate(SIS_CTRID, nsISIS);
+ instream.init(chan.open2());
+
+ return instream.read(instream.available());
+ }
+}
+
+runItem.prototype.kXalan.QueryInterface(nsIStandardURL);
+
+var cmdTestController =
+{
+ supportsCommand: function(aCommand)
+ {
+ switch(aCommand) {
+ case 'cmd_tst_run':
+ case 'cmd_tst_runall':
+ return true;
+ default:
+ }
+ return false;
+ },
+ isCommandEnabled: function(aCommand)
+ {
+ return this.supportsCommand(aCommand);
+ },
+ doCommand: function(aCommand)
+ {
+ switch(aCommand) {
+ case 'cmd_tst_run':
+ dump("cmd_tst_run\n");
+ break;
+ case 'cmd_tst_runall':
+ dump("cmd_tst_runall\n");
+ var tst_run = document.getElementById('cmd_tst_run');
+ tst_run.doCommand();
+ default:
+ }
+ }
+};
+
+registerController(cmdTestController);
diff --git a/dom/xslt/tests/buster/buster-view.js b/dom/xslt/tests/buster/buster-view.js
new file mode 100644
index 000000000..3ee4af28c
--- /dev/null
+++ b/dom/xslt/tests/buster/buster-view.js
@@ -0,0 +1,193 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
+
+var view =
+{
+ onRun : function()
+ {
+ runQueue.mArray = new Array();
+ var sels = this.boxObject.view.selection,a=new Object(),b=new Object(),k;
+ var rowResource, name, path;
+ for (k=0;k<sels.getRangeCount();k++){
+ sels.getRangeAt(k,a,b);
+ for (var l=a.value;l<=b.value;l++) {
+ rowResource = this.builder.getResourceAtIndex(l);
+ itemCache.getItem(rowResource);
+ }
+ }
+ runQueue.run();
+ },
+ displayTest : function()
+ {
+ var current = this.boxObject.view.selection.currentIndex;
+ var rowResource = this.builder.getResourceAtIndex(current);
+ var item = itemCache.getItem(rowResource);
+ },
+ browseForRDF : function()
+ {
+ var fp = doCreateRDFFP('Xalan Description File',
+ nsIFilePicker.modeOpen);
+ var res = fp.show();
+
+ if (res == nsIFilePicker.returnOK) {
+ var furl = fp.fileURL;
+ this.setDataSource(fp.fileURL.spec);
+ }
+ },
+ dump_Good : function()
+ {
+ var enumi = this.mResultDS.GetSources(krTypeSucc, kGood, true);
+ var k = 0;
+ while (enumi.hasMoreElements()) {
+ k += 1;
+ dump(enumi.getNext().QueryInterface(nsIRDFResource).Value+"\n");
+ }
+ dump("found "+k+" good tests\n");
+ },
+ prune_ds : function()
+ {
+ if (this.mResultDS) {
+ this.mResultDS.QueryInterface(nsIRDFPurgeableDataSource).Sweep();
+ }
+ regressionStats.init()
+ itemCache.mArray = new Array();
+ },
+ setDataSource : function(aSpec)
+ {
+ var baseSpec;
+ if (aSpec) {
+ baseSpec = aSpec;
+ }
+ else {
+ baseSpec = document.getElementById("xalan_rdf").value;
+ }
+ if (this.mXalanDS && this.mXalanDS.URI == baseSpec) {
+ this.mXalanDS.QueryInterface(nsIRDFRemoteDataSource);
+ this.mXalanDS.Refresh(true);
+ }
+ else {
+ if (this.mXalanDS) {
+ this.database.RemoveDataSource(view.mXalanDS);
+ }
+ this.mXalanDS = kRDFSvc.GetDataSourceBlocking(baseSpec);
+ if (!this.mXalanDS) {
+ alert("Unable do load DataSource: "+baseSpec);
+ return;
+ }
+ this.database.AddDataSource(this.mXalanDS);
+ }
+ regressionStats.init();
+ if (!this.mResultDS) {
+ this.mResultDS = doCreate(kRDFInMemContractID,
+ nsIRDFDataSource);
+ this.database.AddDataSource(view.mResultDS);
+ if (!this.mResultDS) {
+ alert("Unable to create result InMemDatasource");
+ return;
+ }
+ }
+
+ this.builder.rebuild();
+ document.getElementById("xalan_rdf").value = baseSpec;
+ runItem.prototype.kXalan.init(runItem.prototype.kXalan.URLTYPE_STANDARD,
+ 0, baseSpec, null, null);
+ },
+ loadHtml : function(aUrl, aListener)
+ {
+ const nsIIRequestor = Components.interfaces.nsIInterfaceRequestor;
+ const nsIWebProgress = Components.interfaces.nsIWebProgress;
+ var req = this.mIframe.webNavigation.QueryInterface(nsIIRequestor);
+ var prog = req.getInterface(nsIWebProgress);
+ prog.addProgressListener(aListener, nsIWebProgress.NOTIFY_ALL);
+ this.mIframe.webNavigation.loadURI(aUrl, 0,null,null,null);
+ },
+ fillItemContext : function()
+ {
+ var index = view.boxObject.view.selection.currentIndex;
+ var res = view.builder.getResourceAtIndex(index);
+ var purp = view.mXalanDS.GetTarget(res, krTypePurp, true);
+ return (purp != null);
+ }
+}
+
+regressionStats =
+{
+ observe: function(aSubject, aTopic, aData)
+ {
+ if (aTopic != 'success') {
+ return;
+ }
+ var arc = (aData == "true") ? krTypeSuccCount : krTypeFailCount;
+ this.assertNewCount(aSubject, arc, 1);
+ },
+ init: function()
+ {
+ if (this.mRegressionDS) {
+ this.mRegressionDS.QueryInterface(nsIRDFPurgeableDataSource).Sweep();
+ }
+ else {
+ this.mRegressionDS =
+ doCreate(kRDFInMemContractID, nsIRDFDataSource);
+ view.database.AddDataSource(this.mRegressionDS);
+ }
+ },
+ getParent: function(aDS, aSource)
+ {
+ // parent chached?
+ var parent = this.mRegressionDS.GetTarget(aSource, krTypeParent, true);
+ if (!parent) {
+ var labels = view.mXalanDS.ArcLabelsIn(aSource);
+ while (labels.hasMoreElements()) {
+ var arc = labels.getNext().QueryInterface(nsIRDFResource);
+ if (arc.Value.match(this.mChildRE)) {
+ parent = view.mXalanDS.GetSource(arc, aSource, true);
+ // cache the parent
+ this.mRegressionDS.Assert(aSource, krTypeParent,
+ parent, true);
+ }
+ }
+ }
+ return parent;
+ },
+ assertNewCount: function(aSource, aArc, aIncrement)
+ {
+ var root = kRDFSvc.GetResource('urn:root');
+ var count = 0;
+ // parent chached?
+ var parent = this.getParent(view.XalanDS, aSource);
+ while (parent && !parent.EqualsNode(root)) {
+ var countRes = view.mResultDS.GetTarget(parent, aArc, true);
+ if (countRes) {
+ count = countRes.QueryInterface(nsIRDFInt).Value;
+ }
+ var newCountRes = kRDFSvc.GetIntLiteral(count + aIncrement);
+ if (!newCountRes) {
+ return;
+ }
+
+ if (countRes) {
+ view.mResultDS.Change(parent, aArc, countRes, newCountRes);
+ }
+ else {
+ view.mResultDS.Assert(parent, aArc, newCountRes, true);
+ }
+ parent = this.getParent(view.XalanDS, parent);
+ }
+ },
+ mRegressionDS: 0,
+ mChildRE: /http:\/\/www\.w3\.org\/1999\/02\/22-rdf-syntax-ns#_/
+}
+
+function rdfObserve(aSubject, aTopic, aData)
+{
+ if (aTopic == "success") {
+ var target = (aData == "true") ? kGood : kBad;
+ view.mResultDS.Assert(aSubject, krTypeSucc, target, true);
+
+ regressionStats.observe(aSubject, aTopic, aData);
+ }
+}
+
+runItem.prototype.kObservers.push(rdfObserve);
diff --git a/dom/xslt/tests/buster/buster.css b/dom/xslt/tests/buster/buster.css
new file mode 100644
index 000000000..fe11c10d9
--- /dev/null
+++ b/dom/xslt/tests/buster/buster.css
@@ -0,0 +1,20 @@
+/* -*- Mode: Java; 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/. */
+
+label.head {
+ padding: 5px;
+ font-size: medium;
+ font-weight: bold;
+}
+
+treechildren::-moz-tree-cell(success yes)
+{
+ background-color: green ;
+}
+
+treechildren::-moz-tree-cell(success no)
+{
+ background-color: red ;
+}
diff --git a/dom/xslt/tests/buster/buster.xul b/dom/xslt/tests/buster/buster.xul
new file mode 100644
index 000000000..7191dff4e
--- /dev/null
+++ b/dom/xslt/tests/buster/buster.xul
@@ -0,0 +1,196 @@
+<?xml version="1.0"?><!-- -*- Mode: xml; tab-width: 2; indent-tabs-mode: nil -*- -->
+<!-- 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/. -->
+
+<?xml-stylesheet href="chrome://communicator/skin/" type="text/css"?>
+<?xml-stylesheet href="buster.css" type="text/css"?>
+
+<?xul-overlay href="chrome://global/content/globalOverlay.xul"?>
+<?xul-overlay href="chrome://communicator/content/utilityOverlay.xul"?>
+
+<window id="XalanBuster"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="onLoad()" onunload="onUnload()"
+ title="Xalan testcase harness"
+ persist="width,height"
+ width="800"
+ height="600"
+ orient="vertical">
+<script type="application/x-javascript" src="buster-statics.js" />
+<script type="application/x-javascript" src="buster-test.js" />
+<script type="application/x-javascript" src="buster-view.js" />
+<script type="application/x-javascript" src="buster-handlers.js" />
+<script type="application/x-javascript" src="result-view.js" />
+<script type="application/x-javascript" src="buster-files.js" />
+<script type="application/x-javascript" src="DumpDOM.js" />
+<script type="application/x-javascript" src="DiffDOM.js" />
+
+<commands id="busterKing">
+ <commandset id="buster_file_cmds">
+ <command id="cmd_fl_save" oncommand="goDoCommand('cmd_fl_save')" />
+ <command id="cmd_fl_import" oncommand="goDoCommand('cmd_fl_import')"/>
+ </commandset>
+ <commandset id="buster_test_cmds">
+ <command id="cmd_tst_run" oncommand="goDoCommand('cmd_tst_run')" />
+ <command id="cmd_tst_runall" oncommand="goDoCommand('cmd_tst_runall')" />
+ </commandset>
+ <commandset id="commands">
+ <command id="cmd_quit"/>
+ <command id="cmd_close" oncommand="window.close();"/>
+ </commandset>
+</commands>
+
+<keyset>
+ <key id="key_quit"/>
+ <key id="key_close"/>
+</keyset>
+
+<broadcasterset>
+ <broadcaster id="obs_orig_success" hidden="true"/>
+ <broadcaster id="not_yet" disabled="true"/>
+</broadcasterset>
+
+
+<menubar>
+ <menu id="menu_File" label="File" accesskey="f">
+ <menupopup id="menu_FilePopup">
+ <menuitem label="Save results ..." accesskey="s"
+ observes="cmd_fl_save"/>
+ <menuitem label="Import results ..." accesskey="i"
+ observes="cmd_fl_import"/>
+ <menuitem id="menu_close"/>
+ </menupopup>
+ </menu>
+ <menu id="busterTests" label="Tests" accesskey="t">
+ <menupopup id="tests-popup">
+ <menuitem label="run a test" accesskey="r"
+ observes="cmd_tst_run"/>
+ <menuitem label="run all tests" accesskey="a"
+ observes="cmd_tst_runall"/>
+ </menupopup>
+ </menu>
+</menubar>
+
+<popupset>
+ <popup id="itemcontext" onpopupshowing="return view.fillItemContext();">
+ <menuitem label="View Test" oncommand="onNewResultView(event)"/>
+ </popup>
+</popupset>
+
+<hbox>
+ <button label="check all" oncommand="check(true)" observes="not_yet"/>
+ <button label="uncheck all" oncommand="check(false)" observes="not_yet"/>
+ <button label="reset success" oncommand="view.prune_ds()" />
+ <button label="run checked tests" oncommand="view.onRun()" />
+</hbox>
+<hbox>
+ <label value="Xalan index: " class="head"/>
+ <textbox id="xalan_rdf" persist="url" crop="end" size="40"/>
+ <button label="browse..." oncommand="view.browseForRDF()" />
+</hbox>
+<hbox>
+<groupbox orient="horizontal"><caption label="search" />
+ <button label="Search for " oncommand="select()" observes="not_yet"/>
+ <textbox style="width: 10em;" id="search-name" persist="value" /><label value=" in " />
+ <menulist id="search-field" persist="data" observes="not_yet">
+ <menupopup>
+ <menuitem value="1" label="Name" />
+ <menuitem value="2" label="Purpose" />
+ <menuitem value="3" label="Comment" />
+ </menupopup>
+ </menulist>
+</groupbox>
+<spacer flex="1" /></hbox>
+
+<tree id="out" flex="1" flags="dont-build-content" hidecolumnpicker="true"
+ datasources="rdf:null" ref="urn:root" context="itemcontext">
+ <treecols>
+ <treecol id="NameColumn" flex="1" label="Name" sort="?name"
+ primary="true" />
+ <splitter class="tree-splitter" />
+ <treecol id="PurpsColumn" flex="2" label="Purpose" sort="?purp" />
+ <splitter class="tree-splitter" />
+ <treecol id="SuccessColumn" flex="0" label="Success" />
+ <splitter class="tree-splitter" observes="obs_orig_success" />
+ <treecol id="OrigSuccessColumn" flex="0" label="Previously"
+ observes="obs_orig_success" />
+ </treecols>
+ <template>
+ <rule>
+ <conditions>
+ <content uri="?uri" />
+ <member container="?uri" child="?subheading" />
+ <triple subject="?subheading"
+ predicate="http://home.netscape.com/NC-rdf#purp"
+ object="?purp" />
+ </conditions>
+
+ <bindings>
+ <binding subject="?subheading"
+ predicate="http://home.netscape.com/NC-rdf#name"
+ object="?name" />
+ <binding subject="?subheading"
+ predicate="http://home.netscape.com/NC-rdf#succ"
+ object="?succ" />
+ <binding subject="?subheading"
+ predicate="http://home.netscape.com/NC-rdf#orig_succ"
+ object="?orig_succ" />
+ </bindings>
+
+ <action>
+ <treechildren>
+ <treeitem uri="?subheading">
+ <treerow>
+ <treecell ref="NameColumn" label="?name" />
+ <treecell ref="PurpsColumn" label="?purp" />
+ <treecell ref="SuccessColumn" label="?succ"
+ properties="success ?succ"/>
+ <treecell ref="OrigSuccessColumn" label="?orig_succ"
+ properties="success ?orig_succ" />
+ </treerow>
+ </treeitem>
+ </treechildren>
+ </action>
+ </rule>
+ <rule>
+ <conditions>
+ <content uri="?uri" />
+ <member container="?uri" child="?subheading" />
+ </conditions>
+
+ <bindings>
+ <binding subject="?subheading"
+ predicate="http://home.netscape.com/NC-rdf#dir"
+ object="?dir" />
+ <binding subject="?subheading"
+ predicate="http://home.netscape.com/NC-rdf#succCount"
+ object="?succ" />
+ <binding subject="?subheading"
+ predicate="http://home.netscape.com/NC-rdf#failCount"
+ object="?fail" />
+ <binding subject="?subheading"
+ predicate="http://home.netscape.com/NC-rdf#orig_succCount"
+ object="?orig_succ" />
+ <binding subject="?subheading"
+ predicate="http://home.netscape.com/NC-rdf#orig_failCount"
+ object="?orig_fail" />
+ </bindings>
+
+ <action>
+ <treechildren>
+ <treeitem uri="?subheading">
+ <treerow>
+ <treecell ref="NameColumn" label="?dir" />
+ <treecell ref="PurpsColumn" label="" />
+ <treecell ref="SuccessColumn" label="?succ / ?fail" />
+ <treecell ref="OrigSuccessColumn" label="?orig_succ / ?orig_fail" />
+ </treerow>
+ </treeitem>
+ </treechildren>
+ </action>
+ </rule>
+ </template>
+</tree>
+<iframe style="visibility:hidden; height:0px;" id="hiddenHtml" />
+</window>
diff --git a/dom/xslt/tests/buster/helper/generate-rdf.pl b/dom/xslt/tests/buster/helper/generate-rdf.pl
new file mode 100644
index 000000000..c2bdc7b92
--- /dev/null
+++ b/dom/xslt/tests/buster/helper/generate-rdf.pl
@@ -0,0 +1,95 @@
+use File::Spec;
+
+my(@chunks, @list, $entry, $main_cats, $spacing);
+@list = ('conf', 'perf');
+foreach $entry (@list) {
+ $main_cats .= " <rdf:li><rdf:Description about=\"urn:x-buster:$entry\" nc:dir=\"$entry\" /></rdf:li>\n";
+ go_in($entry, '', $entry);
+}
+if ($ARGV[0]) {
+ open OUTPUT, ">$ARGV[0]";
+}
+else {
+ open OUTPUT, ">xalan.rdf";
+};
+select(OUTPUT);
+print '<?xml version="1.0"?>
+
+<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:nc="http://home.netscape.com/NC-rdf#">
+ <rdf:Seq about="urn:root">
+' . $main_cats . ' </rdf:Seq>
+';
+print join('',@chunks);
+print '</rdf:RDF>
+';
+exit 0;
+
+sub go_in {
+ my($current, $about, $cat) = @_;
+ my (@list, $entry, @subdirs, @files, @purps, $rdf);
+ chdir $current;
+ @list = <*>;
+
+ LOOP: foreach $entry (@list) {
+ next LOOP if $entry=~/^CVS$/;
+ if (! -d $entry) {
+ if ($entry=~/^($current.*)\.xsl$/) {
+ local $source = $entry;
+ $source=~s/xsl$/xml/;
+ next LOOP if ! -f $source;
+ $entry=~/^($current.*)\.xsl$/;
+ push(@files, $1);
+ local ($purp, $purp_open);
+ open STYLE, $entry;
+ $purp_open = 0;
+ while (<STYLE>) {
+ if (/<!--\s+purpose: (.+)\s*-->/i) {
+ $purp .= $1;
+ }
+ elsif (/<!--\s+purpose: (.+)\s*$/i) {
+ $purp_open = 1;
+ $purp .= $1;
+ }
+ elsif ($purp_open) {
+ if (/\s*(\s.+)\s*-->/) {
+ $purp_open = 0;
+ $purp .= $1;
+ }
+ elsif (/\s*(\s.+)\s*$/) {
+ $purp .= $1;
+ }
+ }
+ }
+ $purp=~s/"/'/g; $purp=~s/&/&amp;/g; $purp=~s/</&lt;/g;
+ $purp=~s/\r/ /g; $purp=~s/\s\s/ /g; $purp=~s/\s$//g;
+ push(@purps, $purp);
+ }
+ }
+ else {
+ push(@subdirs, $entry);
+ }
+ }
+
+ if (@subdirs > 0 || @files > 0) {
+ my $topic = $about.$current; $topic=~s/\///g;
+ $rdf = ' <rdf:Seq about="urn:x-buster:'.$topic."\">\n";
+ foreach $entry (@subdirs) {
+ if (go_in($entry, $about.$current.'/', $cat)) {
+ my $id = 'urn:x-buster:'.$about.$current.$entry; $id=~s/\///g;
+ $rdf .= " <rdf:li><rdf:Description about=\"$id\" nc:dir=\"$entry\" /></rdf:li>\n";
+ }
+ }
+ for (my $i=0; $i < @files; $i++) {
+ my $uri = $about.$current.'/'.$files[$i];
+ $uri=~s/[^\/]+\///;
+ my $id = $uri; $id=~s/\///g;
+ $rdf .= " <rdf:li><rdf:Description about=\"urn:x-buster:$files[$i]\" nc:name=\"$files[$i]\" nc:purp=\"$purps[$i]\" nc:path=\"$uri\" nc:category=\"$cat\" /></rdf:li>\n";
+ }
+ $rdf .= " </rdf:Seq>\n";
+ push(@chunks, $rdf);
+ }
+
+ chdir File::Spec->updir;
+ return (@subdirs > 0 || @files > 0);
+}
diff --git a/dom/xslt/tests/buster/install.js b/dom/xslt/tests/buster/install.js
new file mode 100644
index 000000000..0306db692
--- /dev/null
+++ b/dom/xslt/tests/buster/install.js
@@ -0,0 +1,17 @@
+const X_APP = "Buster";
+const X_VER = "2.0"
+const X_JAR_FILE = "xslt-qa.jar";
+
+var err = initInstall("Install " + X_APP, X_APP, X_VER);
+logComment("initInstall: " + err);
+logComment( "Installation started ..." );
+addFile("We're on our way ...", X_JAR_FILE, getFolder("chrome"), "");
+registerChrome(CONTENT|DELAYED_CHROME, getFolder("chrome", X_JAR_FILE), "content/xslt-qa/");
+err = getLastError();
+if (err == SUCCESS) {
+ performInstall();
+ alert("Please restart Mozilla");
+}
+else {
+ cancelInstall();
+}
diff --git a/dom/xslt/tests/buster/jar.mn b/dom/xslt/tests/buster/jar.mn
new file mode 100644
index 000000000..af7e395f0
--- /dev/null
+++ b/dom/xslt/tests/buster/jar.mn
@@ -0,0 +1,18 @@
+xslt-qa.jar:
+% content xslt-qa %content/xslt-qa/
+% overlay chrome://communicator/content/tasksOverlay.xul chrome://xslt-qa/content/xslt-qa-overlay.xul
+ content/xslt-qa/xslt-qa-overlay.xul
+ content/xslt-qa/xslt-qa-overlay.js
+ content/xslt-qa/buster/buster.xul
+ content/xslt-qa/buster/buster.css
+ content/xslt-qa/buster/buster-statics.js
+ content/xslt-qa/buster/buster-handlers.js
+ content/xslt-qa/buster/buster-files.js
+ content/xslt-qa/buster/buster-test.js
+ content/xslt-qa/buster/buster-view.js
+ content/xslt-qa/buster/result-view.xul
+ content/xslt-qa/buster/result-inspector.xul
+ content/xslt-qa/buster/result-view.css
+ content/xslt-qa/buster/result-view.js
+ content/xslt-qa/buster/DumpDOM.js
+ content/xslt-qa/buster/DiffDOM.js
diff --git a/dom/xslt/tests/buster/result-inspector.xul b/dom/xslt/tests/buster/result-inspector.xul
new file mode 100644
index 000000000..77c3ce180
--- /dev/null
+++ b/dom/xslt/tests/buster/result-inspector.xul
@@ -0,0 +1,37 @@
+<?xml version="1.0"?><!-- -*- Mode: xml; tab-width: 2; indent-tabs-mode: nil -*- -->
+<!-- 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/. -->
+
+<!DOCTYPE window [
+ <!ENTITY % dtd1 SYSTEM "chrome://inspector/locale/inspector.dtd"> %dtd1;
+ <!ENTITY % dtd2 SYSTEM "chrome://inspector/content/util.dtd"> %dtd2;
+]>
+
+<?xul-overlay href="chrome://inspector/content/commandOverlay.xul"?>
+<?xul-overlay href="chrome://inspector/content/keysetOverlay.xul"?>
+<?xul-overlay href="chrome://inspector/content/popupOverlay.xul"?>
+
+<?xml-stylesheet href="chrome://inspector/skin/inspectorWindow.css"?>
+
+<window class="color-dialog"
+ title="&Inspector.title;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script type="application/x-javascript" src="chrome://inspector/content/ViewerRegistry.js"/>
+ <script type="application/x-javascript" src="chrome://inspector/content/utils.js"/>
+ <script type="application/x-javascript" src="chrome://inspector/content/jsutil/xpcom/XPCU.js"/>
+ <script type="application/x-javascript" src="chrome://inspector/content/jsutil/rdf/RDFU.js"/>
+ <script type="application/x-javascript" src="chrome://inspector/content/jsutil/rdf/RDFArray.js"/>
+ <script type="application/x-javascript" src="chrome://inspector/content/jsutil/events/ObserverManager.js"/>
+ <script type="application/x-javascript" src="chrome://inspector/content/jsutil/xul/FrameExchange.js"/>
+
+ <commandset id="cmdsGlobalCommands"/>
+ <keyset id="ksGlobalKeyset"/>
+ <popupset id="ppsViewerPopupset"/>
+
+ <domi-panelset id="bxPanelSet" flex="1" viewercommandset="cmdsGlobalCommands">
+ <domi-panel title="&bxDocPanel.title;" flex="1"/>
+ </domi-panelset>
+
+</window>
diff --git a/dom/xslt/tests/buster/result-view.css b/dom/xslt/tests/buster/result-view.css
new file mode 100644
index 000000000..b869cc12b
--- /dev/null
+++ b/dom/xslt/tests/buster/result-view.css
@@ -0,0 +1,16 @@
+label.heading {
+ font-size: medium;
+ font-weight: bold;
+}
+
+button.close {
+ font-size: small;
+}
+
+iframe {
+ padding-left: 10px;
+}
+
+vbox.hidden {
+ display: none;
+}
diff --git a/dom/xslt/tests/buster/result-view.js b/dom/xslt/tests/buster/result-view.js
new file mode 100644
index 000000000..de1b8c881
--- /dev/null
+++ b/dom/xslt/tests/buster/result-view.js
@@ -0,0 +1,105 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
+
+function onNewResultView(event)
+{
+ dump("onNewResultView\n");
+ const db = runItem.prototype.kDatabase;
+ const kXalan = runItem.prototype.kXalan;
+ var index = view.boxObject.view.selection.currentIndex;
+ var res = view.builder.getResourceAtIndex(index);
+ var name = db.GetTarget(res, krTypeName, true);
+ if (!name) {
+ return false;
+ }
+ var cat = db.GetTarget(res, krTypeCat, true);
+ var path = db.GetTarget(res, krTypePath, true);
+ cat = cat.QueryInterface(nsIRDFLiteral);
+ name = name.QueryInterface(nsIRDFLiteral);
+ path = path.QueryInterface(nsIRDFLiteral);
+ xalan_fl = kXalan.resolve(cat.Value+"/"+path.Value);
+ xalan_ref = kXalan.resolve(cat.Value+"-gold/"+path.Value);
+ var currentResultItem = new Object();
+ currentResultItem.testpath = xalan_fl;
+ currentResultItem.refpath = xalan_ref;
+ var currentRunItem = itemCache.getItem(res);
+ // XXX todo, keep a list of these windows, so that we can close them.
+ resultWin = window.openDialog('result-view.xul','_blank',
+ 'chrome,resizable,dialog=no',
+ currentResultItem, currentRunItem);
+ return true;
+}
+
+var refInspector;
+var resInspector;
+
+function onResultViewLoad(event)
+{
+ dump("onResultViewLoad\n");
+ aResultItem = window.arguments[0];
+ aRunItem = window.arguments[1];
+ var loadFlags = Components.interfaces.nsIWebNavigation.LOAD_FLAGS_NONE;
+ document.getElementById('src').webNavigation.loadURI('view-source:'+
+ aResultItem.testpath+'.xml', loadFlags, null, null, null);
+ document.getElementById('style').webNavigation.loadURI('view-source:'+
+ aResultItem.testpath+'.xsl', loadFlags, null, null, null);
+
+ if (aRunItem && aRunItem.mRefDoc && aRunItem.mResDoc) {
+ document.getElementById("refSourceBox").setAttribute("class", "hidden");
+ refInspector = new ObjectApp();
+ refInspector.initialize("refInsp", aRunItem.mRefDoc);
+ resInspector = new ObjectApp();
+ resInspector.initialize("resInsp", aRunItem.mResDoc);
+ }
+ else {
+ document.getElementById("inspectorBox").setAttribute("class", "hidden");
+ document.getElementById('ref').webNavigation.loadURI('view-source:'+
+ aResultItem.refpath+'.out', loadFlags, null, null, null);
+ }
+ return true;
+}
+
+function onResultViewUnload(event)
+{
+ dump("onResultUnload\n");
+}
+
+function ObjectApp()
+{
+}
+
+ObjectApp.prototype =
+{
+ mDoc: null,
+ mPanelSet: null,
+
+ initialize: function(aId, aDoc)
+ {
+ this.mDoc = aDoc;
+ this.mPanelSet = document.getElementById(aId).contentDocument.getElementById("bxPanelSet");
+ this.mPanelSet.addObserver("panelsetready", this, false);
+ this.mPanelSet.initialize();
+ },
+
+ doViewerCommand: function(aCommand)
+ {
+ this.mPanelSet.execCommand(aCommand);
+ },
+
+ getViewer: function(aUID)
+ {
+ return this.mPanelSet.registry.getViewerByUID(aUID);
+ },
+
+ onEvent: function(aEvent)
+ {
+ switch (aEvent.type) {
+ case "panelsetready":
+ {
+ this.mPanelSet.getPanel(0).subject = this.mDoc;
+ }
+ }
+ }
+};
diff --git a/dom/xslt/tests/buster/result-view.xul b/dom/xslt/tests/buster/result-view.xul
new file mode 100644
index 000000000..e402067aa
--- /dev/null
+++ b/dom/xslt/tests/buster/result-view.xul
@@ -0,0 +1,46 @@
+<?xml version="1.0"?><!-- -*- Mode: xml; tab-width: 2; indent-tabs-mode: nil -*- -->
+<!-- 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/. -->
+
+<?xml-stylesheet href="chrome://communicator/skin/" type="text/css"?>
+<?xml-stylesheet href="result-view.css" type="text/css"?>
+
+<window id="buster-result-view" title="Xalan testcase details"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ orient="vertical" persist="width height"
+ onload="onResultViewLoad()" onunload="onResultViewUnload()">
+ <script type="application/x-javascript" src="DumpDOM.js" />
+ <script type="application/x-javascript" src="buster-statics.js" />
+ <script type="application/x-javascript" src="buster-test.js" />
+ <script type="application/x-javascript" src="result-view.js" />
+
+ <hbox>
+ <button class="close" label="close this window"
+ oncommand="window.close()" />
+ </hbox>
+ <vbox flex="1">
+ <label class="heading" value="XML Source:" />
+ <iframe flex="1" id="src" />
+ </vbox>
+ <vbox flex="1">
+ <label class="heading" value="XSL Source:" />
+ <iframe flex="1" id="style" />
+ </vbox>
+ <vbox flex="1" id="refSourceBox">
+ <label class="heading" value="Reference Source:" />
+ <iframe flex="1" id="ref" />
+ </vbox>
+ <vbox flex="2" id="inspectorBox">
+ <hbox flex="1">
+ <vbox flex="1">
+ <label class="heading" value="Reference" />
+ <iframe flex="1" id="refInsp" src="result-inspector.xul" />
+ </vbox>
+ <vbox flex="1">
+ <label class="heading" value="Result" />
+ <iframe flex="1" id="resInsp" src="result-inspector.xul" />
+ </vbox>
+ </hbox>
+ </vbox>
+</window>
diff --git a/dom/xslt/tests/buster/xslt-qa-overlay.js b/dom/xslt/tests/buster/xslt-qa-overlay.js
new file mode 100644
index 000000000..41bd764a5
--- /dev/null
+++ b/dom/xslt/tests/buster/xslt-qa-overlay.js
@@ -0,0 +1,10 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
+
+function onStartBuster()
+{
+ window.open('chrome://xslt-qa/content/buster/buster.xul',
+ 'buster', 'chrome,resizable');
+}
diff --git a/dom/xslt/tests/buster/xslt-qa-overlay.xul b/dom/xslt/tests/buster/xslt-qa-overlay.xul
new file mode 100644
index 000000000..294d27f92
--- /dev/null
+++ b/dom/xslt/tests/buster/xslt-qa-overlay.xul
@@ -0,0 +1,12 @@
+<?xml version="1.0"?><!-- -*- Mode: xml; tab-width: 2; indent-tabs-mode: nil -*- -->
+<!-- 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/. -->
+
+<overlay id="xsltToolsMenuID"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="xslt-qa-overlay.js" />
+ <menupopup id="toolsPopup">
+ <menuitem label="Xalan Tests" oncommand="onStartBuster()"/>
+ </menupopup>
+</overlay>
diff --git a/dom/xslt/tests/mochitest/file_bug1135764.xml b/dom/xslt/tests/mochitest/file_bug1135764.xml
new file mode 100644
index 000000000..b9da87e5e
--- /dev/null
+++ b/dom/xslt/tests/mochitest/file_bug1135764.xml
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?xml-stylesheet type="text/xsl" href="file_bug1135764.xsl"?>
+<root/>
diff --git a/dom/xslt/tests/mochitest/file_bug1135764.xsl b/dom/xslt/tests/mochitest/file_bug1135764.xsl
new file mode 100644
index 000000000..e739086cb
--- /dev/null
+++ b/dom/xslt/tests/mochitest/file_bug1135764.xsl
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+<xsl:output method="html"
+ indent="yes"
+ version="5.0"
+ doctype-system="about:legacy-compat"/>
+
+<xsl:template match="/">
+<html>
+<head>
+</head>
+ <body>
+ Some text
+ </body>
+</html>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/dom/xslt/tests/mochitest/mochitest.ini b/dom/xslt/tests/mochitest/mochitest.ini
new file mode 100644
index 000000000..53a6d001c
--- /dev/null
+++ b/dom/xslt/tests/mochitest/mochitest.ini
@@ -0,0 +1,20 @@
+[DEFAULT]
+
+[test_bug1072116.html]
+[test_bug319374.xhtml]
+[test_bug427060.html]
+[test_bug440974.html]
+[test_bug453441.html]
+[test_bug468208.html]
+[test_bug511487.html]
+[test_bug551412.html]
+[test_bug551654.html]
+[test_bug566629.html]
+[test_bug566629.xhtml]
+[test_bug603159.html]
+[test_bug616774.html]
+[test_bug667315.html]
+[test_bug1135764.html]
+support-files = file_bug1135764.xml file_bug1135764.xsl
+[test_exslt_regex.html]
+[test_parameter.html]
diff --git a/dom/xslt/tests/mochitest/test_bug1072116.html b/dom/xslt/tests/mochitest/test_bug1072116.html
new file mode 100644
index 000000000..2d0624725
--- /dev/null
+++ b/dom/xslt/tests/mochitest/test_bug1072116.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1072116
+-->
+<head>
+ <title>Test for Bug 1072116</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1072116">Mozilla Bug 1072116</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 1072116 **/
+var attr = document.createAttribute("c");
+
+var xpathExpr = document.createExpression('a', null);
+
+var status = false;
+try {
+ xpathExpr.evaluate(attr, null, null, null, null);
+} catch(e) {
+ status = true;
+}
+
+ok(status, "Still alive \\o/");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/xslt/tests/mochitest/test_bug1135764.html b/dom/xslt/tests/mochitest/test_bug1135764.html
new file mode 100644
index 000000000..5a7410e37
--- /dev/null
+++ b/dom/xslt/tests/mochitest/test_bug1135764.html
@@ -0,0 +1,53 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1135764
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1135764</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 1135764 **/
+ SimpleTest.waitForExplicitFinish();
+ var counter = 0;
+ var startTimelineValue;
+
+ function waitATick() {
+ ++counter;
+ if (counter == 1) {
+ frames[0].requestAnimationFrame(waitATick);
+ return;
+ }
+ ok(frames[0].document.timeline.currentTime !== startTimelineValue,
+ "The timeline in an XSLT-transformed document should still advance");
+ SimpleTest.finish();
+ }
+ addLoadEvent(function() {
+ SpecialPowers.pushPrefEnv(
+ { "set": [[ "dom.animations-api.core.enabled", true]] },
+ function() {
+ var ifr = document.querySelector("iframe");
+ ifr.onload = function() {
+ startTimelineValue = frames[0].document.timeline.currentTime;
+ frames[0].requestAnimationFrame(waitATick);
+ }
+ ifr.src = "file_bug1135764.xml";
+ })
+ })
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1135764">Mozilla Bug 1135764</a>
+<p id="display">
+ <iframe></iframe>
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/xslt/tests/mochitest/test_bug319374.xhtml b/dom/xslt/tests/mochitest/test_bug319374.xhtml
new file mode 100644
index 000000000..2342b3447
--- /dev/null
+++ b/dom/xslt/tests/mochitest/test_bug319374.xhtml
@@ -0,0 +1,109 @@
+<?xml version="1.0"?>
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xbl="http://www.mozilla.org/xbl">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=319374
+-->
+<head>
+ <title>Test for Bug 319374</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <xbl:bindings>
+ <xbl:binding id="test">
+ <xbl:content>
+ <span attr="attribute"><span></span></span><span> anon text </span><br/>
+ </xbl:content>
+ </xbl:binding>
+ </xbl:bindings>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=319374">Mozilla Bug 319374</a>
+<p id="display"></p>
+<div id="content"><span style="-moz-binding: url(#test)"/><span style="-moz-binding: url(#test)"/><span style="-moz-binding: url(#test)"/></div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+<![CDATA[
+
+/** Test for Bug 319374 **/
+
+ function testChangesInAnonymousTree() {
+ // Test 1: Make sure that modifying anonymous content doesn't
+ // cause non-anonymous XPath result to throw exceptions..
+ var counter = 0;
+ var error = null;
+ function getAnonymousNodes(e) {
+ return SpecialPowers.wrap(document).getAnonymousNodes(e);
+ }
+ try {
+ var xp = new XPathEvaluator();
+ var result = xp.evaluate("*",
+ document.getElementById('content'),
+ null,
+ XPathResult.UNORDERED_NODE_ITERATOR_TYPE,
+ null);
+ var res = null;
+ while (res = result.iterateNext()) {
+ ++counter;
+ var anon = getAnonymousNodes(res);
+ anon[0].removeChild(anon[0].firstChild); // Removing a child node
+ anon[0].removeAttribute("attr1"); // Removing an attribute
+ anon[1].firstChild.data = "anon text changed" // Modifying text data
+ }
+ } catch (e) {
+ error = e;
+ }
+ ok(!error, error);
+ ok(counter == 3, "XPathEvaluator should have found 3 elements.")
+
+ // Test 2: If the context node is in anonymous content, changing some
+ // other anonymous tree shouldn't cause XPath result to throw.
+ var anonAttr1 =
+ getAnonymousNodes(document.getElementById('content').
+ firstChild)[0].getAttributeNode('attr');
+ var anonAttr2 =
+ getAnonymousNodes(document.getElementById('content').
+ lastChild)[0].getAttributeNode('attr');
+ var resultAttr = null;
+ try {
+ var xp2 = SpecialPowers.wrap(xp).evaluate(".",
+ anonAttr1,
+ null,
+ XPathResult.UNORDERED_NODE_ITERATOR_TYPE,
+ null);
+ // Attribute changing in a different anonymous tree.
+ anonAttr2.value = "foo";
+ resultAttr = xp2.iterateNext();
+ ok(SpecialPowers.compare(resultAttr, anonAttr1), "XPathEvaluator returned wrong attribute!")
+ } catch (e) {
+ ok(false, e);
+ }
+
+ // Test 3: If the anonymous tree in which context node is in is modified,
+ // XPath result should throw when iterateNext() is called.
+ resultAttr = null;
+ try {
+ var xp3 = xp.evaluate(".",
+ anonAttr1,
+ null,
+ XPathResult.UNORDERED_NODE_ITERATOR_TYPE,
+ null);
+ // Attribute changing in the same anonymous tree.
+ anonAttr1.ownerElement.setAttribute("foo", "bar");
+ resultAttr = xp3.iterateNext();
+ ok(resultAttr == anonAttr1,
+ "XPathEvaluator should have thrown an exception!")
+ } catch (e) {
+ ok(true, e);
+ }
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addLoadEvent(testChangesInAnonymousTree);
+]]>
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/xslt/tests/mochitest/test_bug427060.html b/dom/xslt/tests/mochitest/test_bug427060.html
new file mode 100644
index 000000000..89bcb5255
--- /dev/null
+++ b/dom/xslt/tests/mochitest/test_bug427060.html
@@ -0,0 +1,49 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=427060
+-->
+<head>
+ <title>Test for Bug 427060</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=427060">Mozilla Bug 427060</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 427060 **/
+
+var xmldoc, xsltdoc;
+
+xmldoc = new DOMParser().parseFromString('<opml version="1.0"><body></body></opml>', "text/xml");
+xsltdoc = new DOMParser().parseFromString('<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">\n\
+ <xsl:template match="/opml">\n\
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\n\
+ <head>\n\
+ <base target="_blank"></base>\n\
+ </head>\n\
+ <body></body>\n\
+ </html>\n\
+ </xsl:template>\n\
+ </xsl:stylesheet>', "text/xml");
+
+var processor = new XSLTProcessor;
+processor.importStylesheet(xsltdoc);
+try
+{
+ var result = processor.transformToDocument(xmldoc);
+}
+catch (e)
+{
+}
+ok(result && result instanceof Document, "XSLT transform should have created a document");
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/xslt/tests/mochitest/test_bug440974.html b/dom/xslt/tests/mochitest/test_bug440974.html
new file mode 100644
index 000000000..33b73e6de
--- /dev/null
+++ b/dom/xslt/tests/mochitest/test_bug440974.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=440974
+-->
+<head>
+ <title>Test for Bug 440974</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=440974">Mozilla Bug 440974</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 440974 **/
+
+function isTxResult(node)
+{
+ return node.namespaceURI == "http://www.mozilla.org/TransforMiix" &&
+ node.localName == "result";
+}
+
+var xmldoc, xsltdoc;
+
+xmldoc = new DOMParser().parseFromString('<items><item><id>1</id></item><item><id>2</id></item><item><id>3</id></item></items>', "text/xml");
+xsltdoc = new DOMParser().parseFromString('<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">\n\
+ <xsl:output method="xml" />\n\
+ <xsl:template match="item"><foo id="{id}"/></xsl:template>\n\
+ </xsl:stylesheet>', "text/xml");
+
+var processor = new XSLTProcessor;
+processor.importStylesheet(xsltdoc);
+var result = processor.transformToDocument(xmldoc);
+var resultElements = Array.prototype.filter.call(result.getElementsByTagName('*'), isTxResult);
+is(resultElements.length, 1, "there should be only one 'transformiix:result' element");
+is(resultElements[0], result.documentElement, "the 'transformiix:result' element should be the document element");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/xslt/tests/mochitest/test_bug453441.html b/dom/xslt/tests/mochitest/test_bug453441.html
new file mode 100644
index 000000000..4decae56c
--- /dev/null
+++ b/dom/xslt/tests/mochitest/test_bug453441.html
@@ -0,0 +1,57 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=453441
+-->
+<head>
+ <title>Test for Bug 453441</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=453441">Mozilla Bug 453441</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 453441 **/
+
+function tryImportStylesheet(xml, valid)
+{
+ var processor = new XSLTProcessor;
+
+ var xsltdoc = new DOMParser().parseFromString(xml, "text/xml");
+ try
+ {
+ processor.importStylesheet(xsltdoc);
+ ok(valid, "should be able to parse this XSLT stylesheet");
+ }
+ catch (e)
+ {
+ ok(!valid, "should not be able to parse this XSLT stylesheet");
+ }
+}
+
+tryImportStylesheet(
+ '<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">\n\
+ <xsl:template match="/">\n\
+ <html xmlns="http://www.w3.org/1999/xhtml" xsl:version="1.0" />\n\
+ </xsl:template>\n\
+ </xsl:stylesheet>'
+, true);
+
+tryImportStylesheet(
+ '<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" />'
+, false);
+
+tryImportStylesheet(
+ '<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" />'
+, false);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/xslt/tests/mochitest/test_bug468208.html b/dom/xslt/tests/mochitest/test_bug468208.html
new file mode 100644
index 000000000..3da0349be
--- /dev/null
+++ b/dom/xslt/tests/mochitest/test_bug468208.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=468208
+-->
+<head>
+ <title>Test for Bug 468208</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=468208">Mozilla Bug 468208</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 468208 **/
+var xslt =
+ '<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">\n\
+ <xsl:strip-space elements="color"/>\n\
+ </xsl:stylesheet>'
+;
+var xsltdoc = new DOMParser().parseFromString(xslt, "text/xml");
+
+var processor = new XSLTProcessor;
+processor.importStylesheet(xsltdoc);
+ok(true, "XSLT shouldn't leak");
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/xslt/tests/mochitest/test_bug511487.html b/dom/xslt/tests/mochitest/test_bug511487.html
new file mode 100644
index 000000000..b5cd21cf6
--- /dev/null
+++ b/dom/xslt/tests/mochitest/test_bug511487.html
@@ -0,0 +1,59 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=511487
+-->
+<head>
+ <title>Test for Bug 511487</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=511487">Mozilla Bug 511487</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 511487 **/
+
+ var didTransform = false;
+ var processor = new XSLTProcessor();
+ var style =
+ '<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns="http://www.w3.org/1999/xhtml">' +
+ '<xsl:output method="xml" version="1.0" encoding="UTF-8" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN" />' +
+ '<xsl:template match="wml">' +
+ '<html xmlns="http://www.w3.org/1999/xhtml">' +
+ '<head>' +
+ '<title>XSLT test</title>' +
+ '</head>' +
+ '<body onload="window.alert(this)">' +
+ '</body>' +
+ '</html>' +
+ '</xsl:template>' +
+ '</xsl:stylesheet>';
+ var styleDoc = new DOMParser().parseFromString (style, "text/xml");
+
+ var data =
+ '<?xml version="1.0"?>' +
+ '<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml">' +
+ '<wml><card><p>paragraph</p></card></wml>';
+ var originalDoc = new DOMParser().parseFromString(data, "text/xml");
+
+ processor.importStylesheet(styleDoc);
+ try {
+ var transformedDocument = processor.transformToDocument(originalDoc);
+ didTransform = true;
+ } catch (e) {
+ ok(false, e);
+ }
+
+ ok(didTransform, "transformToDocument didn't succeed!");
+
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/xslt/tests/mochitest/test_bug551412.html b/dom/xslt/tests/mochitest/test_bug551412.html
new file mode 100644
index 000000000..c6f5e13e8
--- /dev/null
+++ b/dom/xslt/tests/mochitest/test_bug551412.html
@@ -0,0 +1,48 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=551412
+-->
+<head>
+ <title>Test for Bug 551412</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=551412">Mozilla Bug 551412</a>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 551412 **/
+
+ var processor = new XSLTProcessor();
+ var style =
+ '<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" ' +
+ 'xmlns:exsl="http://exslt.org/common" ' +
+ 'version="1.0">' +
+ '<xsl:output method="html"/>' +
+ '<xsl:variable name="rtf">1 <b>2</b> 3</xsl:variable>' +
+ '<xsl:template match="/">' +
+ '<xsl:copy-of select="exsl:node-set($rtf)"/>' +
+ '</xsl:template>' +
+ '</xsl:stylesheet>';
+ var styleDoc = new DOMParser().parseFromString (style, "text/xml");
+
+ var data =
+ '<root/>';
+ var originalDoc = new DOMParser().parseFromString(data, "text/xml");
+
+ processor.importStylesheet(styleDoc);
+
+ var fragment = processor.transformToFragment(originalDoc, document);
+ var content = document.getElementById("content");
+ content.appendChild(fragment);
+ is(content.innerHTML, "1 <b>2</b> 3",
+ "Result of transform should be '1 <b>2</b> 3'");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/xslt/tests/mochitest/test_bug551654.html b/dom/xslt/tests/mochitest/test_bug551654.html
new file mode 100644
index 000000000..a72fa41fa
--- /dev/null
+++ b/dom/xslt/tests/mochitest/test_bug551654.html
@@ -0,0 +1,49 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=551654
+-->
+<head>
+ <title>Test for Bug 551654</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=551654">Mozilla Bug 551654</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 551654 **/
+
+ var didTransform = false;
+ var processor = new XSLTProcessor();
+ var style =
+ '<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" ' +
+ 'xmlns:exsl="http://exslt.org/common" ' +
+ 'version="1.0">' +
+ '<xsl:output method="html"/>' +
+ '<xsl:template match="/">' +
+ '<xsl:copy-of select="exsl:node-set(42)"/>' +
+ '</xsl:template>' +
+ '</xsl:stylesheet>';
+ var styleDoc = new DOMParser().parseFromString (style, "text/xml");
+
+ var data =
+ '<root/>';
+ var originalDoc = new DOMParser().parseFromString(data, "text/xml");
+
+ processor.importStylesheet(styleDoc);
+ var fragment = processor.transformToFragment(originalDoc, document);
+ is(fragment.firstChild.nodeType, Node.TEXT_NODE,
+ "Result of transform should be a textnode");
+ is(fragment.firstChild.nodeValue, "42",
+ "Result of transform should be a textnode with value '42'");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/xslt/tests/mochitest/test_bug566629.html b/dom/xslt/tests/mochitest/test_bug566629.html
new file mode 100644
index 000000000..7d66d212b
--- /dev/null
+++ b/dom/xslt/tests/mochitest/test_bug566629.html
@@ -0,0 +1,70 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=566629
+-->
+<head>
+ <title>Test for Bug 566629</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=566629">Mozilla Bug 566629</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 566629 **/
+
+var xsltdoc = new DOMParser().parseFromString(
+ '<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"\
+ xmlns:xhtml="http://www.w3.org/1999/xhtml">\
+ <xsl:template match="/">\
+ <xsl:value-of select="count(//body)"/>\
+ <xsl:text>,</xsl:text>\
+ <xsl:value-of select="count(//xhtml:body)"/>\
+ <xsl:text>,</xsl:text>\
+ <xsl:value-of select="count(//xsl:body)"/>\
+ <xsl:text>,</xsl:text>\
+ <xsl:value-of select="name(//body)"/>\
+ <xsl:text>,</xsl:text>\
+ <xsl:value-of select="local-name(//body)"/>\
+ </xsl:template>\
+ </xsl:stylesheet>',
+ "text/xml");
+
+var processor = new XSLTProcessor;
+processor.importStylesheet(xsltdoc);
+var result = processor.transformToFragment(document, document);
+ok(result instanceof DocumentFragment, "returned a docfragment");
+is(result.firstChild.nodeValue, "1,1,0,BODY,body",
+ "correct treatment of HTML elements in XSLT");
+
+is(document.evaluate("count(//body)", document, null, XPathResult.ANY_TYPE, null).numberValue,
+ 1, "namespace-less node-test");
+is(document.evaluate("count(//a:body)", document,
+ function() { return "http://www.w3.org/1999/xhtml" },
+ XPathResult.ANY_TYPE, null).numberValue,
+ 1, "with-namespace node-test");
+is(document.evaluate("count(//a:body)", document,
+ function() { return "foo" },
+ XPathResult.ANY_TYPE, null).numberValue,
+ 0, "wrong-namespace node-test");
+is(document.evaluate("//bODy", document, null, XPathResult.ANY_TYPE, null).iterateNext(),
+ document.body, "case insensitive matching");
+is(document.evaluate("count(//a:bODy)", document,
+ function() { return "http://www.w3.org/1999/xhtml" },
+ XPathResult.ANY_TYPE, null).numberValue,
+ 0, "with-namespace but wrong casing node-test");
+is(document.evaluate("name(//body)", document, null, XPathResult.ANY_TYPE, null).stringValue,
+ "BODY", "uppercase name() function");
+is(document.evaluate("local-name(//body)", document, null, XPathResult.ANY_TYPE, null).stringValue,
+ "body", "lowercase local-name() function");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/xslt/tests/mochitest/test_bug566629.xhtml b/dom/xslt/tests/mochitest/test_bug566629.xhtml
new file mode 100644
index 000000000..e5fca2b58
--- /dev/null
+++ b/dom/xslt/tests/mochitest/test_bug566629.xhtml
@@ -0,0 +1,73 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=566629
+-->
+<head>
+ <title>Test for Bug 566629</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=566629">Mozilla Bug 566629</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+<![CDATA[
+/** Test for Bug 566629 **/
+
+var xsltdoc = new DOMParser().parseFromString(
+ '<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"\
+ xmlns:xhtml="http://www.w3.org/1999/xhtml">\
+ <xsl:template match="/">\
+ <xsl:value-of select="count(//body)"/>\
+ <xsl:text>,</xsl:text>\
+ <xsl:value-of select="count(//xhtml:body)"/>\
+ <xsl:text>,</xsl:text>\
+ <xsl:value-of select="count(//xsl:body)"/>\
+ <xsl:text>,</xsl:text>\
+ <xsl:value-of select="name(//xhtml:body)"/>\
+ <xsl:text>,</xsl:text>\
+ <xsl:value-of select="local-name(//xhtml:body)"/>\
+ </xsl:template>\
+ </xsl:stylesheet>',
+ "text/xml");
+
+var processor = new XSLTProcessor;
+processor.importStylesheet(xsltdoc);
+var result = processor.transformToFragment(document, document);
+ok(result instanceof DocumentFragment, "returned a docfragment");
+is(result.firstChild.nodeValue, "0,1,0,body,body",
+ "correct treatment of HTML elements in XSLT");
+
+is(document.evaluate("count(//body)", document, null, XPathResult.ANY_TYPE, null).numberValue,
+ 0, "namespace-less node-test");
+is(document.evaluate("count(//a:body)", document,
+ function() { return "http://www.w3.org/1999/xhtml" },
+ XPathResult.ANY_TYPE, null).numberValue,
+ 1, "with-namespace node-test");
+is(document.evaluate("count(//a:body)", document,
+ function() { return "foo" },
+ XPathResult.ANY_TYPE, null).numberValue,
+ 0, "wrong-namespace node-test");
+is(document.evaluate("count(//a:bODy)", document,
+ function() { return "http://www.w3.org/1999/xhtml" },
+ XPathResult.ANY_TYPE, null).numberValue,
+ 0, "with-namespace wrong-casing node-test");
+is(document.evaluate("count(//bODy)", document, null, XPathResult.ANY_TYPE, null).numberValue,
+ 0, "without-namespace wrong-casing node-test");
+is(document.evaluate("name(//a:body)", document,
+ function() { return "http://www.w3.org/1999/xhtml" },
+ XPathResult.ANY_TYPE, null).stringValue,
+ "body", "name()");
+is(document.evaluate("local-name(//a:body)", document,
+ function() { return "http://www.w3.org/1999/xhtml" },
+ XPathResult.ANY_TYPE, null).stringValue,
+ "body", "local-name()");
+]]>
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/xslt/tests/mochitest/test_bug603159.html b/dom/xslt/tests/mochitest/test_bug603159.html
new file mode 100644
index 000000000..ac0191bd3
--- /dev/null
+++ b/dom/xslt/tests/mochitest/test_bug603159.html
@@ -0,0 +1,54 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=603159
+-->
+<head>
+ <title>Test for Bug 603159</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=603159">Mozilla Bug 603159</a>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 603159 **/
+
+ var style =
+ '<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" ' +
+ 'xmlns:date="http://exslt.org/dates-and-times" '+
+ 'version="1.0">' +
+ '<xsl:output method="html"/>' +
+ '<xsl:template match="/">' +
+ '<xsl:value-of select="date:date-time()" /> ' +
+ '</xsl:template>' +
+ '</xsl:stylesheet>';
+ var styleDoc = new DOMParser().parseFromString (style, "text/xml");
+
+ var data = '<root/>';
+ var originalDoc = new DOMParser().parseFromString(data, "text/xml");
+
+ var processor = new XSLTProcessor();
+ processor.importStylesheet(styleDoc);
+
+ var fragment = processor.transformToFragment(originalDoc, document);
+ var content = document.getElementById("content");
+ content.appendChild(fragment);
+
+ // use Gecko's Date.parse to parse, then compare the milliseconds since epoch
+ var xslt_ms = Date.parse(content.innerHTML);
+ var now_ms = new Date().getTime();
+ var accepted_diff = 30 * 60 * 1000; // 30 minutes
+ var diff = Math.abs(now_ms - xslt_ms);
+
+ ok(diff < accepted_diff, "generated timestamp should be not more than "
+ + accepted_diff + " ms before 'now', but the difference was: " + diff);
+
+ content.innerHTML = '';
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/xslt/tests/mochitest/test_bug616774.html b/dom/xslt/tests/mochitest/test_bug616774.html
new file mode 100644
index 000000000..3578d1704
--- /dev/null
+++ b/dom/xslt/tests/mochitest/test_bug616774.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=616774-->
+<head>
+ <title>Test for Bug 616774</title>
+ <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=616774">Mozilla Bug 616774</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ 42
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 616774 **/
+is(document.evaluate('- "8"', document, null, XPathResult.ANY_TYPE, null).numberValue, -8, "Negated string literal should evaluate to itself negated");
+is(document.evaluate('- - "999"', document, null, XPathResult.ANY_TYPE, null).numberValue, 999, "String literal should evaluate to itself");
+is(document.evaluate('- - id("content")', document, null, XPathResult.ANY_TYPE, null).numberValue, 42, "DOM element should evaluate to itself coerced to a number");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/xslt/tests/mochitest/test_bug667315.html b/dom/xslt/tests/mochitest/test_bug667315.html
new file mode 100644
index 000000000..fc5baff77
--- /dev/null
+++ b/dom/xslt/tests/mochitest/test_bug667315.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=667315
+-->
+<head>
+ <title>Test for Bug 667315</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=667315">Mozilla Bug 667315</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 667315 **/
+
+var style =
+ '<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" ' +
+ 'version="1.0">' +
+ '<xsl:variable name="var">' +
+ '<html><p>a</p></html>' +
+ '</xsl:variable>' +
+ '<xsl:template match="/">' +
+ '<xsl:copy-of select="$var" />' +
+ '</xsl:template>' +
+ '</xsl:stylesheet>';
+var styleDoc = new DOMParser().parseFromString (style, "text/xml");
+
+var data = '<root/>';
+var originalDoc = new DOMParser().parseFromString(data, "text/xml");
+
+var processor = new XSLTProcessor();
+processor.importStylesheet(styleDoc);
+
+var doc = processor.transformToDocument(originalDoc);
+ok(doc instanceof HTMLDocument, "should have switched to html output method");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/xslt/tests/mochitest/test_exslt_regex.html b/dom/xslt/tests/mochitest/test_exslt_regex.html
new file mode 100644
index 000000000..f4e00f9d3
--- /dev/null
+++ b/dom/xslt/tests/mochitest/test_exslt_regex.html
@@ -0,0 +1,60 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Test EXSLT Regular Expression Extension
+http://www.exslt.org/regexp/index.html
+-->
+<head>
+ <title>Test for EXSLT Regular Expression Extensions</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test">
+<script type="application/javascript">
+ var tests = [
+ { descr: "Testing regexp:test",
+ expr: "regexp:test('XSLT is great', 'XSLT', '')",
+ expResult: "true" },
+
+ { descr: "Testing regexp:match",
+ expr: "regexp:match('XSLT is great', 'XSL.', '')[1]",
+ expResult: "XSLT" },
+
+ { descr: "Testing regexp:replace",
+ expr: "regexp:replace('Food is great', 'Fo.d', '', 'XSLT')",
+ expResult: "XSLT is great" }
+ ];
+
+ for (test of tests) {
+ var style =
+ '<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" ' +
+ 'xmlns:regexp="http://exslt.org/regular-expressions" '+
+ 'version="1.0">' +
+ '<xsl:output method="html"/>' +
+ '<xsl:template match="/">' +
+ '<xsl:value-of select="'+test.expr+'" /> ' +
+ '</xsl:template>' +
+ '</xsl:stylesheet>';
+ var styleDoc = new DOMParser().parseFromString (style, "text/xml");
+
+ var data = '<root/>';
+ var originalDoc = new DOMParser().parseFromString(data, "text/xml");
+
+ var processor = new XSLTProcessor();
+ processor.importStylesheet(styleDoc);
+
+ var fragment = processor.transformToFragment(originalDoc, document);
+ var content = document.getElementById("content");
+ content.appendChild(fragment);
+ is(content.innerHTML, test.expResult, test.descr);
+
+ content.innerHTML = '';
+ }
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/xslt/tests/mochitest/test_parameter.html b/dom/xslt/tests/mochitest/test_parameter.html
new file mode 100644
index 000000000..2430f75ec
--- /dev/null
+++ b/dom/xslt/tests/mochitest/test_parameter.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for setParameter/getParameter</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test">
+<script>
+ var processor = new XSLTProcessor();
+
+ processor.setParameter(null, "test", "hello");
+ is(processor.getParameter(null, "test"), "hello", "null namespace works");
+
+ processor.setParameter("foo", "bar", "foobar");
+ is(processor.getParameter("foo", "bar"), "foobar", "non-null namespace works");
+
+ processor.setParameter(null, "test", 123);
+ is(processor.getParameter(null, "test"), 123, "number value works");
+
+ processor.removeParameter(null, "test");
+ is(processor.getParameter(null, "test"), null, "removeParameter works");
+
+ is(processor.getParameter(null, "not-here"), null, "nonexistant parameter");
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/xslt/txIEXSLTRegExFunctions.idl b/dom/xslt/txIEXSLTRegExFunctions.idl
new file mode 100644
index 000000000..28f248133
--- /dev/null
+++ b/dom/xslt/txIEXSLTRegExFunctions.idl
@@ -0,0 +1,21 @@
+/* -*- Mode: IDL; 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 "nsISupports.idl"
+
+interface txIFunctionEvaluationContext;
+interface txINodeSet;
+
+[scriptable, uuid(c180e993-aced-4839-95a0-ecd5ff138be9)]
+interface txIEXSLTRegExFunctions : nsISupports
+{
+ txINodeSet match(in txIFunctionEvaluationContext aContext,
+ in DOMString aString, in DOMString aRegEx,
+ in DOMString aFlags);
+ DOMString replace(in DOMString aString, in DOMString aRegEx,
+ in DOMString aFlags, in DOMString aReplace);
+ boolean test(in DOMString aString, in DOMString aRegEx,
+ in DOMString aFlags);
+};
diff --git a/dom/xslt/txIFunctionEvaluationContext.idl b/dom/xslt/txIFunctionEvaluationContext.idl
new file mode 100644
index 000000000..a762b42d1
--- /dev/null
+++ b/dom/xslt/txIFunctionEvaluationContext.idl
@@ -0,0 +1,18 @@
+/* -*- Mode: IDL; 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 "nsISupports.idl"
+
+interface nsIDOMNode;
+interface txINodeSet;
+
+[scriptable, uuid(0ecbb00c-6a78-11d9-9791-000a95dc234c)]
+interface txIFunctionEvaluationContext : nsISupports
+{
+ readonly attribute uint32_t position;
+ readonly attribute uint32_t size;
+ readonly attribute nsIDOMNode contextNode;
+ readonly attribute nsISupports state;
+};
diff --git a/dom/xslt/txINodeSet.idl b/dom/xslt/txINodeSet.idl
new file mode 100644
index 000000000..5222c47a9
--- /dev/null
+++ b/dom/xslt/txINodeSet.idl
@@ -0,0 +1,16 @@
+/* -*- Mode: IDL; 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 "domstubs.idl"
+
+[scriptable, uuid(15d424c0-6b47-11d9-9791-000a95dc234c)]
+interface txINodeSet : nsISupports
+{
+ nsIDOMNode item(in unsigned long index);
+ double itemAsNumber(in unsigned long index);
+ DOMString itemAsString(in unsigned long index);
+ readonly attribute unsigned long length;
+ void add(in nsIDOMNode node);
+};
diff --git a/dom/xslt/txIXPathObject.idl b/dom/xslt/txIXPathObject.idl
new file mode 100644
index 000000000..23729d6c8
--- /dev/null
+++ b/dom/xslt/txIXPathObject.idl
@@ -0,0 +1,18 @@
+/* -*- Mode: IDL; 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 "nsISupports.idl"
+
+%{ C++
+class txAExprResult;
+%}
+
+[ptr] native txAExprResultPtr(txAExprResult);
+
+[scriptable, uuid(67706346-dece-4c9b-9fc2-57cf19071014)]
+interface txIXPathObject : nsISupports
+{
+ [noscript, notxpcom] txAExprResultPtr getResult();
+};
diff --git a/dom/xslt/xml/moz.build b/dom/xslt/xml/moz.build
new file mode 100644
index 000000000..f20a27bba
--- /dev/null
+++ b/dom/xslt/xml/moz.build
@@ -0,0 +1,19 @@
+# -*- 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/.
+
+UNIFIED_SOURCES += [
+ 'txXMLParser.cpp',
+ 'txXMLUtils.cpp',
+]
+
+LOCAL_INCLUDES += [
+ '../base',
+ '../xpath',
+ '../xslt',
+ '/dom/base',
+]
+
+FINAL_LIBRARY = 'xul'
diff --git a/dom/xslt/xml/txXMLParser.cpp b/dom/xslt/xml/txXMLParser.cpp
new file mode 100644
index 000000000..0cf281f2c
--- /dev/null
+++ b/dom/xslt/xml/txXMLParser.cpp
@@ -0,0 +1,62 @@
+/* -*- 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 "txXMLParser.h"
+#include "txURIUtils.h"
+#include "txXPathTreeWalker.h"
+
+#include "nsIDocument.h"
+#include "nsIDOMDocument.h"
+#include "nsSyncLoadService.h"
+#include "nsNetUtil.h"
+#include "nsIURI.h"
+#include "nsIPrincipal.h"
+
+nsresult
+txParseDocumentFromURI(const nsAString& aHref,
+ const txXPathNode& aLoader,
+ nsAString& aErrMsg,
+ txXPathNode** aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+ *aResult = nullptr;
+ nsCOMPtr<nsIURI> documentURI;
+ nsresult rv = NS_NewURI(getter_AddRefs(documentURI), aHref);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsIDocument* loaderDocument = txXPathNativeNode::getDocument(aLoader);
+
+ nsCOMPtr<nsILoadGroup> loadGroup = loaderDocument->GetDocumentLoadGroup();
+
+ // For the system principal loaderUri will be null here, which is good
+ // since that means that chrome documents can load any uri.
+
+ // Raw pointer, we want the resulting txXPathNode to hold a reference to
+ // the document.
+ nsIDOMDocument* theDocument = nullptr;
+ nsAutoSyncOperation sync(loaderDocument);
+ rv = nsSyncLoadService::LoadDocument(documentURI,
+ nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST,
+ loaderDocument->NodePrincipal(),
+ nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS,
+ loadGroup, true,
+ loaderDocument->GetReferrerPolicy(),
+ &theDocument);
+
+ if (NS_FAILED(rv)) {
+ aErrMsg.AppendLiteral("Document load of ");
+ aErrMsg.Append(aHref);
+ aErrMsg.AppendLiteral(" failed.");
+ return NS_FAILED(rv) ? rv : NS_ERROR_FAILURE;
+ }
+
+ *aResult = txXPathNativeNode::createXPathNode(theDocument);
+ if (!*aResult) {
+ NS_RELEASE(theDocument);
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
diff --git a/dom/xslt/xml/txXMLParser.h b/dom/xslt/xml/txXMLParser.h
new file mode 100644
index 000000000..fea9defe3
--- /dev/null
+++ b/dom/xslt/xml/txXMLParser.h
@@ -0,0 +1,26 @@
+/* -*- 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 MITRE_XMLPARSER_H
+#define MITRE_XMLPARSER_H
+
+#include "txCore.h"
+
+class txXPathNode;
+
+/**
+ * API to load XML files into DOM datastructures.
+ * Parsing is either done by expat, or by expat via the syncloaderservice
+ */
+
+/**
+ * Parse a document from the aHref location, with referrer URI on behalf
+ * of the document aLoader.
+ */
+extern "C" nsresult
+txParseDocumentFromURI(const nsAString& aHref, const txXPathNode& aLoader,
+ nsAString& aErrMsg, txXPathNode** aResult);
+
+#endif
diff --git a/dom/xslt/xml/txXMLUtils.cpp b/dom/xslt/xml/txXMLUtils.cpp
new file mode 100644
index 000000000..58687568d
--- /dev/null
+++ b/dom/xslt/xml/txXMLUtils.cpp
@@ -0,0 +1,181 @@
+/* -*- 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/. */
+
+/*
+ * XML utility classes
+ */
+
+#include "txXMLUtils.h"
+#include "nsString.h"
+#include "nsReadableUtils.h"
+#include "nsGkAtoms.h"
+#include "txStringUtils.h"
+#include "txNamespaceMap.h"
+#include "txXPathTreeWalker.h"
+#include "nsContentUtils.h"
+
+//------------------------------/
+//- Implementation of XMLUtils -/
+//------------------------------/
+
+// static
+nsresult
+XMLUtils::splitExpatName(const char16_t *aExpatName, nsIAtom **aPrefix,
+ nsIAtom **aLocalName, int32_t* aNameSpaceID)
+{
+ /**
+ * Expat can send the following:
+ * localName
+ * namespaceURI<separator>localName
+ * namespaceURI<separator>localName<separator>prefix
+ */
+
+ const char16_t *uriEnd = nullptr;
+ const char16_t *nameEnd = nullptr;
+ const char16_t *pos;
+ for (pos = aExpatName; *pos; ++pos) {
+ if (*pos == kExpatSeparatorChar) {
+ if (uriEnd) {
+ nameEnd = pos;
+ }
+ else {
+ uriEnd = pos;
+ }
+ }
+ }
+
+ const char16_t *nameStart;
+ if (uriEnd) {
+ *aNameSpaceID =
+ txNamespaceManager::getNamespaceID(nsDependentSubstring(aExpatName,
+ uriEnd));
+ if (*aNameSpaceID == kNameSpaceID_Unknown) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nameStart = (uriEnd + 1);
+ if (nameEnd) {
+ const char16_t *prefixStart = nameEnd + 1;
+ *aPrefix = NS_Atomize(Substring(prefixStart, pos)).take();
+ if (!*aPrefix) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+ else {
+ nameEnd = pos;
+ *aPrefix = nullptr;
+ }
+ }
+ else {
+ *aNameSpaceID = kNameSpaceID_None;
+ nameStart = aExpatName;
+ nameEnd = pos;
+ *aPrefix = nullptr;
+ }
+
+ *aLocalName = NS_Atomize(Substring(nameStart, nameEnd)).take();
+
+ return *aLocalName ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+}
+
+nsresult
+XMLUtils::splitQName(const nsAString& aName, nsIAtom** aPrefix,
+ nsIAtom** aLocalName)
+{
+ const nsAFlatString& qName = PromiseFlatString(aName);
+ const char16_t* colon;
+ bool valid = XMLUtils::isValidQName(qName, &colon);
+ if (!valid) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (colon) {
+ const char16_t *end;
+ qName.EndReading(end);
+
+ *aPrefix = NS_Atomize(Substring(qName.get(), colon)).take();
+ *aLocalName = NS_Atomize(Substring(colon + 1, end)).take();
+ }
+ else {
+ *aPrefix = nullptr;
+ *aLocalName = NS_Atomize(aName).take();
+ }
+
+ return NS_OK;
+}
+
+/**
+ * Returns true if the given string has only whitespace characters
+ */
+bool XMLUtils::isWhitespace(const nsAFlatString& aText)
+{
+ nsAFlatString::const_char_iterator start, end;
+ aText.BeginReading(start);
+ aText.EndReading(end);
+ for ( ; start != end; ++start) {
+ if (!isWhitespace(*start)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+/**
+ * Normalizes the value of a XML processing instruction
+**/
+void XMLUtils::normalizePIValue(nsAString& piValue)
+{
+ nsAutoString origValue(piValue);
+ uint32_t origLength = origValue.Length();
+ uint32_t conversionLoop = 0;
+ char16_t prevCh = 0;
+ piValue.Truncate();
+
+ while (conversionLoop < origLength) {
+ char16_t ch = origValue.CharAt(conversionLoop);
+ switch (ch) {
+ case '>':
+ {
+ if (prevCh == '?') {
+ piValue.Append(char16_t(' '));
+ }
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ piValue.Append(ch);
+ prevCh = ch;
+ ++conversionLoop;
+ }
+}
+
+//static
+bool XMLUtils::isValidQName(const nsAFlatString& aQName,
+ const char16_t** aColon)
+{
+ return NS_SUCCEEDED(nsContentUtils::CheckQName(aQName, true, aColon));
+}
+
+//static
+bool XMLUtils::getXMLSpacePreserve(const txXPathNode& aNode)
+{
+ nsAutoString value;
+ txXPathTreeWalker walker(aNode);
+ do {
+ if (walker.getAttr(nsGkAtoms::space, kNameSpaceID_XML, value)) {
+ if (TX_StringEqualsAtom(value, nsGkAtoms::preserve)) {
+ return true;
+ }
+ if (TX_StringEqualsAtom(value, nsGkAtoms::_default)) {
+ return false;
+ }
+ }
+ } while (walker.moveToParent());
+
+ return false;
+}
diff --git a/dom/xslt/xml/txXMLUtils.h b/dom/xslt/xml/txXMLUtils.h
new file mode 100644
index 000000000..755d7410d
--- /dev/null
+++ b/dom/xslt/xml/txXMLUtils.h
@@ -0,0 +1,82 @@
+/* -*- 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/. */
+
+/**
+ * An XML Utility class
+**/
+
+#ifndef MITRE_XMLUTILS_H
+#define MITRE_XMLUTILS_H
+
+#include "txCore.h"
+#include "nsDependentSubstring.h"
+#include "txXPathNode.h"
+
+#define kExpatSeparatorChar 0xFFFF
+
+extern "C" int MOZ_XMLIsLetter(const char* ptr);
+extern "C" int MOZ_XMLIsNCNameChar(const char* ptr);
+
+class nsIAtom;
+
+class XMLUtils {
+
+public:
+ static nsresult splitExpatName(const char16_t *aExpatName,
+ nsIAtom **aPrefix, nsIAtom **aLocalName,
+ int32_t* aNameSpaceID);
+ static nsresult splitQName(const nsAString& aName, nsIAtom** aPrefix,
+ nsIAtom** aLocalName);
+
+ /*
+ * Returns true if the given character is whitespace.
+ */
+ static bool isWhitespace(const char16_t& aChar)
+ {
+ return (aChar <= ' ' &&
+ (aChar == ' ' || aChar == '\r' ||
+ aChar == '\n'|| aChar == '\t'));
+ }
+
+ /**
+ * Returns true if the given string has only whitespace characters
+ */
+ static bool isWhitespace(const nsAFlatString& aText);
+
+ /**
+ * Normalizes the value of a XML processingInstruction
+ **/
+ static void normalizePIValue(nsAString& attValue);
+
+ /**
+ * Returns true if the given string is a valid XML QName
+ */
+ static bool isValidQName(const nsAFlatString& aQName,
+ const char16_t** aColon);
+
+ /**
+ * Returns true if the given character represents an Alpha letter
+ */
+ static bool isLetter(char16_t aChar)
+ {
+ return !!MOZ_XMLIsLetter(reinterpret_cast<const char*>(&aChar));
+ }
+
+ /**
+ * Returns true if the given character is an allowable NCName character
+ */
+ static bool isNCNameChar(char16_t aChar)
+ {
+ return !!MOZ_XMLIsNCNameChar(reinterpret_cast<const char*>(&aChar));
+ }
+
+ /*
+ * Walks up the document tree and returns true if the closest xml:space
+ * attribute is "preserve"
+ */
+ static bool getXMLSpacePreserve(const txXPathNode& aNode);
+};
+
+#endif
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__ */
diff --git a/dom/xslt/xslt/moz.build b/dom/xslt/xslt/moz.build
new file mode 100644
index 000000000..d629e1d3e
--- /dev/null
+++ b/dom/xslt/xslt/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 += [
+ 'txMozillaXSLTProcessor.h'
+]
+
+UNIFIED_SOURCES += [
+ 'txBufferingHandler.cpp',
+ 'txCurrentFunctionCall.cpp',
+ 'txDocumentFunctionCall.cpp',
+ 'txExecutionState.cpp',
+ 'txEXSLTFunctions.cpp',
+ 'txFormatNumberFunctionCall.cpp',
+ 'txGenerateIdFunctionCall.cpp',
+ 'txInstructions.cpp',
+ 'txKeyFunctionCall.cpp',
+ 'txMozillaStylesheetCompiler.cpp',
+ 'txMozillaTextOutput.cpp',
+ 'txMozillaXMLOutput.cpp',
+ 'txMozillaXSLTProcessor.cpp',
+ 'txNodeSorter.cpp',
+ 'txOutputFormat.cpp',
+ 'txPatternOptimizer.cpp',
+ 'txPatternParser.cpp',
+ 'txRtfHandler.cpp',
+ 'txStylesheet.cpp',
+ 'txStylesheetCompileHandlers.cpp',
+ 'txStylesheetCompiler.cpp',
+ 'txTextHandler.cpp',
+ 'txToplevelItems.cpp',
+ 'txUnknownHandler.cpp',
+ 'txXPathResultComparator.cpp',
+ 'txXSLTEnvironmentFunctionCall.cpp',
+ 'txXSLTNumber.cpp',
+ 'txXSLTNumberCounters.cpp',
+ 'txXSLTPatterns.cpp',
+ 'txXSLTProcessor.cpp',
+]
+
+EXTRA_COMPONENTS += [
+ 'txEXSLTRegExFunctions.js',
+ 'txEXSLTRegExFunctions.manifest',
+]
+
+# For nsAutoJSString
+LOCAL_INCLUDES += ["/dom/base"]
+
+LOCAL_INCLUDES += [
+ '../base',
+ '../xml',
+ '../xpath',
+ '/dom/base',
+]
+
+FINAL_LIBRARY = 'xul'
+
+if CONFIG['GNU_CXX']:
+ CXXFLAGS += ['-Wno-error=shadow']
diff --git a/dom/xslt/xslt/txBufferingHandler.cpp b/dom/xslt/xslt/txBufferingHandler.cpp
new file mode 100644
index 000000000..e1ad24d56
--- /dev/null
+++ b/dom/xslt/xslt/txBufferingHandler.cpp
@@ -0,0 +1,464 @@
+/* -*- 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 "txBufferingHandler.h"
+
+class txOutputTransaction
+{
+public:
+ enum txTransactionType {
+ eAttributeTransaction,
+ eAttributeAtomTransaction,
+ eCharacterTransaction,
+ eCharacterNoOETransaction,
+ eCommentTransaction,
+ eEndDocumentTransaction,
+ eEndElementTransaction,
+ ePITransaction,
+ eStartDocumentTransaction,
+ eStartElementAtomTransaction,
+ eStartElementTransaction
+ };
+ explicit txOutputTransaction(txTransactionType aType)
+ : mType(aType)
+ {
+ MOZ_COUNT_CTOR(txOutputTransaction);
+ }
+ virtual ~txOutputTransaction()
+ {
+ MOZ_COUNT_DTOR(txOutputTransaction);
+ }
+ txTransactionType mType;
+};
+
+class txCharacterTransaction : public txOutputTransaction
+{
+public:
+ txCharacterTransaction(txTransactionType aType, uint32_t aLength)
+ : txOutputTransaction(aType),
+ mLength(aLength)
+ {
+ MOZ_COUNT_CTOR_INHERITED(txCharacterTransaction, txOutputTransaction);
+ }
+ virtual ~txCharacterTransaction()
+ {
+ MOZ_COUNT_DTOR_INHERITED(txCharacterTransaction, txOutputTransaction);
+ }
+ uint32_t mLength;
+};
+
+class txCommentTransaction : public txOutputTransaction
+{
+public:
+ explicit txCommentTransaction(const nsAString& aValue)
+ : txOutputTransaction(eCommentTransaction),
+ mValue(aValue)
+ {
+ MOZ_COUNT_CTOR_INHERITED(txCommentTransaction, txOutputTransaction);
+ }
+ virtual ~txCommentTransaction()
+ {
+ MOZ_COUNT_DTOR_INHERITED(txCommentTransaction, txOutputTransaction);
+ }
+ nsString mValue;
+};
+
+class txPITransaction : public txOutputTransaction
+{
+public:
+ txPITransaction(const nsAString& aTarget, const nsAString& aData)
+ : txOutputTransaction(ePITransaction),
+ mTarget(aTarget),
+ mData(aData)
+ {
+ MOZ_COUNT_CTOR_INHERITED(txPITransaction, txOutputTransaction);
+ }
+ virtual ~txPITransaction()
+ {
+ MOZ_COUNT_DTOR_INHERITED(txPITransaction, txOutputTransaction);
+ }
+ nsString mTarget;
+ nsString mData;
+};
+
+class txStartElementAtomTransaction : public txOutputTransaction
+{
+public:
+ txStartElementAtomTransaction(nsIAtom* aPrefix, nsIAtom* aLocalName,
+ nsIAtom* aLowercaseLocalName, int32_t aNsID)
+ : txOutputTransaction(eStartElementAtomTransaction),
+ mPrefix(aPrefix),
+ mLocalName(aLocalName),
+ mLowercaseLocalName(aLowercaseLocalName),
+ mNsID(aNsID)
+ {
+ MOZ_COUNT_CTOR_INHERITED(txStartElementAtomTransaction, txOutputTransaction);
+ }
+ virtual ~txStartElementAtomTransaction()
+ {
+ MOZ_COUNT_DTOR_INHERITED(txStartElementAtomTransaction, txOutputTransaction);
+ }
+ nsCOMPtr<nsIAtom> mPrefix;
+ nsCOMPtr<nsIAtom> mLocalName;
+ nsCOMPtr<nsIAtom> mLowercaseLocalName;
+ int32_t mNsID;
+};
+
+class txStartElementTransaction : public txOutputTransaction
+{
+public:
+ txStartElementTransaction(nsIAtom* aPrefix,
+ const nsSubstring& aLocalName, int32_t aNsID)
+ : txOutputTransaction(eStartElementTransaction),
+ mPrefix(aPrefix),
+ mLocalName(aLocalName),
+ mNsID(aNsID)
+ {
+ MOZ_COUNT_CTOR_INHERITED(txStartElementTransaction, txOutputTransaction);
+ }
+ virtual ~txStartElementTransaction()
+ {
+ MOZ_COUNT_DTOR_INHERITED(txStartElementTransaction, txOutputTransaction);
+ }
+ nsCOMPtr<nsIAtom> mPrefix;
+ nsString mLocalName;
+ int32_t mNsID;
+};
+
+class txAttributeTransaction : public txOutputTransaction
+{
+public:
+ txAttributeTransaction(nsIAtom* aPrefix,
+ const nsSubstring& aLocalName, int32_t aNsID,
+ const nsString& aValue)
+ : txOutputTransaction(eAttributeTransaction),
+ mPrefix(aPrefix),
+ mLocalName(aLocalName),
+ mNsID(aNsID),
+ mValue(aValue)
+ {
+ MOZ_COUNT_CTOR_INHERITED(txAttributeTransaction, txOutputTransaction);
+ }
+ virtual ~txAttributeTransaction()
+ {
+ MOZ_COUNT_DTOR_INHERITED(txAttributeTransaction, txOutputTransaction);
+ }
+ nsCOMPtr<nsIAtom> mPrefix;
+ nsString mLocalName;
+ int32_t mNsID;
+ nsString mValue;
+};
+
+class txAttributeAtomTransaction : public txOutputTransaction
+{
+public:
+ txAttributeAtomTransaction(nsIAtom* aPrefix, nsIAtom* aLocalName,
+ nsIAtom* aLowercaseLocalName,
+ int32_t aNsID, const nsString& aValue)
+ : txOutputTransaction(eAttributeAtomTransaction),
+ mPrefix(aPrefix),
+ mLocalName(aLocalName),
+ mLowercaseLocalName(aLowercaseLocalName),
+ mNsID(aNsID),
+ mValue(aValue)
+ {
+ MOZ_COUNT_CTOR_INHERITED(txAttributeAtomTransaction, txOutputTransaction);
+ }
+ virtual ~txAttributeAtomTransaction()
+ {
+ MOZ_COUNT_DTOR_INHERITED(txAttributeAtomTransaction, txOutputTransaction);
+ }
+ nsCOMPtr<nsIAtom> mPrefix;
+ nsCOMPtr<nsIAtom> mLocalName;
+ nsCOMPtr<nsIAtom> mLowercaseLocalName;
+ int32_t mNsID;
+ nsString mValue;
+};
+
+txBufferingHandler::txBufferingHandler() : mCanAddAttribute(false)
+{
+ MOZ_COUNT_CTOR(txBufferingHandler);
+ mBuffer = new txResultBuffer();
+}
+
+txBufferingHandler::~txBufferingHandler()
+{
+ MOZ_COUNT_DTOR(txBufferingHandler);
+}
+
+nsresult
+txBufferingHandler::attribute(nsIAtom* aPrefix, nsIAtom* aLocalName,
+ nsIAtom* aLowercaseLocalName, int32_t aNsID,
+ const nsString& aValue)
+{
+ NS_ENSURE_TRUE(mBuffer, NS_ERROR_OUT_OF_MEMORY);
+
+ if (!mCanAddAttribute) {
+ // XXX ErrorReport: Can't add attributes without element
+ return NS_OK;
+ }
+
+ txOutputTransaction* transaction =
+ new txAttributeAtomTransaction(aPrefix, aLocalName,
+ aLowercaseLocalName, aNsID,
+ aValue);
+ return mBuffer->addTransaction(transaction);
+}
+
+nsresult
+txBufferingHandler::attribute(nsIAtom* aPrefix, const nsSubstring& aLocalName,
+ const int32_t aNsID, const nsString& aValue)
+{
+ NS_ENSURE_TRUE(mBuffer, NS_ERROR_OUT_OF_MEMORY);
+
+ if (!mCanAddAttribute) {
+ // XXX ErrorReport: Can't add attributes without element
+ return NS_OK;
+ }
+
+ txOutputTransaction* transaction =
+ new txAttributeTransaction(aPrefix, aLocalName, aNsID, aValue);
+ return mBuffer->addTransaction(transaction);
+}
+
+nsresult
+txBufferingHandler::characters(const nsSubstring& aData, bool aDOE)
+{
+ NS_ENSURE_TRUE(mBuffer, NS_ERROR_OUT_OF_MEMORY);
+
+ mCanAddAttribute = false;
+
+ txOutputTransaction::txTransactionType type =
+ aDOE ? txOutputTransaction::eCharacterNoOETransaction
+ : txOutputTransaction::eCharacterTransaction;
+
+ txOutputTransaction* transaction = mBuffer->getLastTransaction();
+ if (transaction && transaction->mType == type) {
+ mBuffer->mStringValue.Append(aData);
+ static_cast<txCharacterTransaction*>(transaction)->mLength +=
+ aData.Length();
+ return NS_OK;
+ }
+
+ transaction = new txCharacterTransaction(type, aData.Length());
+ mBuffer->mStringValue.Append(aData);
+ return mBuffer->addTransaction(transaction);
+}
+
+nsresult
+txBufferingHandler::comment(const nsString& aData)
+{
+ NS_ENSURE_TRUE(mBuffer, NS_ERROR_OUT_OF_MEMORY);
+
+ mCanAddAttribute = false;
+
+ txOutputTransaction* transaction = new txCommentTransaction(aData);
+ return mBuffer->addTransaction(transaction);
+}
+
+nsresult
+txBufferingHandler::endDocument(nsresult aResult)
+{
+ NS_ENSURE_TRUE(mBuffer, NS_ERROR_OUT_OF_MEMORY);
+
+ txOutputTransaction* transaction =
+ new txOutputTransaction(txOutputTransaction::eEndDocumentTransaction);
+ return mBuffer->addTransaction(transaction);
+}
+
+nsresult
+txBufferingHandler::endElement()
+{
+ NS_ENSURE_TRUE(mBuffer, NS_ERROR_OUT_OF_MEMORY);
+
+ mCanAddAttribute = false;
+
+ txOutputTransaction* transaction =
+ new txOutputTransaction(txOutputTransaction::eEndElementTransaction);
+ return mBuffer->addTransaction(transaction);
+}
+
+nsresult
+txBufferingHandler::processingInstruction(const nsString& aTarget,
+ const nsString& aData)
+{
+ NS_ENSURE_TRUE(mBuffer, NS_ERROR_OUT_OF_MEMORY);
+
+ mCanAddAttribute = false;
+
+ txOutputTransaction* transaction =
+ new txPITransaction(aTarget, aData);
+ return mBuffer->addTransaction(transaction);
+}
+
+nsresult
+txBufferingHandler::startDocument()
+{
+ NS_ENSURE_TRUE(mBuffer, NS_ERROR_OUT_OF_MEMORY);
+
+ txOutputTransaction* transaction =
+ new txOutputTransaction(txOutputTransaction::eStartDocumentTransaction);
+ return mBuffer->addTransaction(transaction);
+}
+
+nsresult
+txBufferingHandler::startElement(nsIAtom* aPrefix, nsIAtom* aLocalName,
+ nsIAtom* aLowercaseLocalName,
+ int32_t aNsID)
+{
+ NS_ENSURE_TRUE(mBuffer, NS_ERROR_OUT_OF_MEMORY);
+
+ mCanAddAttribute = true;
+
+ txOutputTransaction* transaction =
+ new txStartElementAtomTransaction(aPrefix, aLocalName,
+ aLowercaseLocalName, aNsID);
+ return mBuffer->addTransaction(transaction);
+}
+
+nsresult
+txBufferingHandler::startElement(nsIAtom* aPrefix,
+ const nsSubstring& aLocalName,
+ const int32_t aNsID)
+{
+ NS_ENSURE_TRUE(mBuffer, NS_ERROR_OUT_OF_MEMORY);
+
+ mCanAddAttribute = true;
+
+ txOutputTransaction* transaction =
+ new txStartElementTransaction(aPrefix, aLocalName, aNsID);
+ return mBuffer->addTransaction(transaction);
+}
+
+txResultBuffer::txResultBuffer()
+{
+ MOZ_COUNT_CTOR(txResultBuffer);
+}
+
+txResultBuffer::~txResultBuffer()
+{
+ MOZ_COUNT_DTOR(txResultBuffer);
+ for (uint32_t i = 0, len = mTransactions.Length(); i < len; ++i) {
+ delete mTransactions[i];
+ }
+}
+
+nsresult
+txResultBuffer::addTransaction(txOutputTransaction* aTransaction)
+{
+ if (mTransactions.AppendElement(aTransaction) == nullptr) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ return NS_OK;
+}
+
+static nsresult
+flushTransaction(txOutputTransaction* aTransaction,
+ txAXMLEventHandler* aHandler,
+ nsAFlatString::const_char_iterator& aIter)
+{
+ switch (aTransaction->mType) {
+ case txOutputTransaction::eAttributeAtomTransaction:
+ {
+ txAttributeAtomTransaction* transaction =
+ static_cast<txAttributeAtomTransaction*>(aTransaction);
+ return aHandler->attribute(transaction->mPrefix,
+ transaction->mLocalName,
+ transaction->mLowercaseLocalName,
+ transaction->mNsID,
+ transaction->mValue);
+ }
+ case txOutputTransaction::eAttributeTransaction:
+ {
+ txAttributeTransaction* attrTransaction =
+ static_cast<txAttributeTransaction*>(aTransaction);
+ return aHandler->attribute(attrTransaction->mPrefix,
+ attrTransaction->mLocalName,
+ attrTransaction->mNsID,
+ attrTransaction->mValue);
+ }
+ case txOutputTransaction::eCharacterTransaction:
+ case txOutputTransaction::eCharacterNoOETransaction:
+ {
+ txCharacterTransaction* charTransaction =
+ static_cast<txCharacterTransaction*>(aTransaction);
+ nsAFlatString::const_char_iterator start = aIter;
+ nsAFlatString::const_char_iterator end =
+ start + charTransaction->mLength;
+ aIter = end;
+ return aHandler->characters(Substring(start, end),
+ aTransaction->mType ==
+ txOutputTransaction::eCharacterNoOETransaction);
+ }
+ case txOutputTransaction::eCommentTransaction:
+ {
+ txCommentTransaction* commentTransaction =
+ static_cast<txCommentTransaction*>(aTransaction);
+ return aHandler->comment(commentTransaction->mValue);
+ }
+ case txOutputTransaction::eEndElementTransaction:
+ {
+ return aHandler->endElement();
+ }
+ case txOutputTransaction::ePITransaction:
+ {
+ txPITransaction* piTransaction =
+ static_cast<txPITransaction*>(aTransaction);
+ return aHandler->processingInstruction(piTransaction->mTarget,
+ piTransaction->mData);
+ }
+ case txOutputTransaction::eStartDocumentTransaction:
+ {
+ return aHandler->startDocument();
+ }
+ case txOutputTransaction::eStartElementAtomTransaction:
+ {
+ txStartElementAtomTransaction* transaction =
+ static_cast<txStartElementAtomTransaction*>(aTransaction);
+ return aHandler->startElement(transaction->mPrefix,
+ transaction->mLocalName,
+ transaction->mLowercaseLocalName,
+ transaction->mNsID);
+ }
+ case txOutputTransaction::eStartElementTransaction:
+ {
+ txStartElementTransaction* transaction =
+ static_cast<txStartElementTransaction*>(aTransaction);
+ return aHandler->startElement(transaction->mPrefix,
+ transaction->mLocalName,
+ transaction->mNsID);
+ }
+ default:
+ {
+ NS_NOTREACHED("Unexpected transaction type");
+ }
+ }
+
+ return NS_ERROR_UNEXPECTED;
+}
+
+nsresult
+txResultBuffer::flushToHandler(txAXMLEventHandler* aHandler)
+{
+ nsAFlatString::const_char_iterator iter;
+ mStringValue.BeginReading(iter);
+
+ for (uint32_t i = 0, len = mTransactions.Length(); i < len; ++i) {
+ nsresult rv = flushTransaction(mTransactions[i], aHandler, iter);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return NS_OK;
+}
+
+txOutputTransaction*
+txResultBuffer::getLastTransaction()
+{
+ int32_t last = mTransactions.Length() - 1;
+ if (last < 0) {
+ return nullptr;
+ }
+ return mTransactions[last];
+}
diff --git a/dom/xslt/xslt/txBufferingHandler.h b/dom/xslt/xslt/txBufferingHandler.h
new file mode 100644
index 000000000..4d38f3c6f
--- /dev/null
+++ b/dom/xslt/xslt/txBufferingHandler.h
@@ -0,0 +1,47 @@
+/* -*- 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 txBufferingHandler_h__
+#define txBufferingHandler_h__
+
+#include "txXMLEventHandler.h"
+#include "nsString.h"
+#include "nsTArray.h"
+#include "nsAutoPtr.h"
+
+class txOutputTransaction;
+
+class txResultBuffer
+{
+public:
+ txResultBuffer();
+ ~txResultBuffer();
+
+ nsresult addTransaction(txOutputTransaction* aTransaction);
+
+ nsresult flushToHandler(txAXMLEventHandler* aHandler);
+
+ txOutputTransaction* getLastTransaction();
+
+ nsString mStringValue;
+
+private:
+ nsTArray<txOutputTransaction*> mTransactions;
+};
+
+class txBufferingHandler : public txAXMLEventHandler
+{
+public:
+ txBufferingHandler();
+ virtual ~txBufferingHandler();
+
+ TX_DECL_TXAXMLEVENTHANDLER
+
+protected:
+ nsAutoPtr<txResultBuffer> mBuffer;
+ bool mCanAddAttribute;
+};
+
+#endif /* txBufferingHandler_h__ */
diff --git a/dom/xslt/xslt/txCurrentFunctionCall.cpp b/dom/xslt/xslt/txCurrentFunctionCall.cpp
new file mode 100644
index 000000000..a758fd670
--- /dev/null
+++ b/dom/xslt/xslt/txCurrentFunctionCall.cpp
@@ -0,0 +1,65 @@
+/* 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 "nsGkAtoms.h"
+#include "txXSLTFunctions.h"
+#include "txExecutionState.h"
+
+/*
+ Implementation of XSLT 1.0 extension function: current
+*/
+
+/**
+ * Creates a new current function call
+**/
+CurrentFunctionCall::CurrentFunctionCall()
+{
+}
+
+/*
+ * Evaluates this Expr
+ *
+ * @return NodeSet containing the context node used for the complete
+ * Expr or Pattern.
+ */
+nsresult
+CurrentFunctionCall::evaluate(txIEvalContext* aContext, txAExprResult** aResult)
+{
+ *aResult = nullptr;
+
+ if (!requireParams(0, 0, aContext))
+ return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT;
+
+ txExecutionState* es =
+ static_cast<txExecutionState*>(aContext->getPrivateContext());
+ if (!es) {
+ NS_ERROR(
+ "called xslt extension function \"current\" with wrong context");
+ return NS_ERROR_UNEXPECTED;
+ }
+ return aContext->recycler()->getNodeSet(
+ es->getEvalContext()->getContextNode(), aResult);
+}
+
+Expr::ResultType
+CurrentFunctionCall::getReturnType()
+{
+ return NODESET_RESULT;
+}
+
+bool
+CurrentFunctionCall::isSensitiveTo(ContextSensitivity aContext)
+{
+ return !!(aContext & PRIVATE_CONTEXT);
+}
+
+#ifdef TX_TO_STRING
+nsresult
+CurrentFunctionCall::getNameAtom(nsIAtom** aAtom)
+{
+ *aAtom = nsGkAtoms::current;
+ NS_ADDREF(*aAtom);
+ return NS_OK;
+}
+#endif
diff --git a/dom/xslt/xslt/txDocumentFunctionCall.cpp b/dom/xslt/xslt/txDocumentFunctionCall.cpp
new file mode 100644
index 000000000..f1a0a6d0c
--- /dev/null
+++ b/dom/xslt/xslt/txDocumentFunctionCall.cpp
@@ -0,0 +1,167 @@
+/* -*- 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/. */
+
+/*
+ * DocumentFunctionCall
+ * A representation of the XSLT additional function: document()
+ */
+
+#include "nsGkAtoms.h"
+#include "txIXPathContext.h"
+#include "txXSLTFunctions.h"
+#include "txExecutionState.h"
+#include "txURIUtils.h"
+
+/*
+ * Creates a new DocumentFunctionCall.
+ */
+DocumentFunctionCall::DocumentFunctionCall(const nsAString& aBaseURI)
+ : mBaseURI(aBaseURI)
+{
+}
+
+static void
+retrieveNode(txExecutionState* aExecutionState, const nsAString& aUri,
+ const nsAString& aBaseUri, txNodeSet* aNodeSet)
+{
+ nsAutoString absUrl;
+ URIUtils::resolveHref(aUri, aBaseUri, absUrl);
+
+ int32_t hash = absUrl.RFindChar(char16_t('#'));
+ uint32_t urlEnd, fragStart, fragEnd;
+ if (hash == kNotFound) {
+ urlEnd = absUrl.Length();
+ fragStart = 0;
+ fragEnd = 0;
+ }
+ else {
+ urlEnd = hash;
+ fragStart = hash + 1;
+ fragEnd = absUrl.Length();
+ }
+
+ nsDependentSubstring docUrl(absUrl, 0, urlEnd);
+ nsDependentSubstring frag(absUrl, fragStart, fragEnd);
+
+ const txXPathNode* loadNode = aExecutionState->retrieveDocument(docUrl);
+ if (loadNode) {
+ if (frag.IsEmpty()) {
+ aNodeSet->add(*loadNode);
+ }
+ else {
+ txXPathTreeWalker walker(*loadNode);
+ if (walker.moveToElementById(frag)) {
+ aNodeSet->add(walker.getCurrentPosition());
+ }
+ }
+ }
+}
+
+/*
+ * Evaluates this Expr based on the given context node and processor state
+ * NOTE: the implementation is incomplete since it does not make use of the
+ * second argument (base URI)
+ * @param context the context node for evaluation of this Expr
+ * @return the result of the evaluation
+ */
+nsresult
+DocumentFunctionCall::evaluate(txIEvalContext* aContext,
+ txAExprResult** aResult)
+{
+ *aResult = nullptr;
+ txExecutionState* es =
+ static_cast<txExecutionState*>(aContext->getPrivateContext());
+
+ RefPtr<txNodeSet> nodeSet;
+ nsresult rv = aContext->recycler()->getNodeSet(getter_AddRefs(nodeSet));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // document(object, node-set?)
+ if (!requireParams(1, 2, aContext)) {
+ return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT;
+ }
+
+ RefPtr<txAExprResult> exprResult1;
+ rv = mParams[0]->evaluate(aContext, getter_AddRefs(exprResult1));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString baseURI;
+ bool baseURISet = false;
+
+ if (mParams.Length() == 2) {
+ // We have 2 arguments, get baseURI from the first node
+ // in the resulting nodeset
+ RefPtr<txNodeSet> nodeSet2;
+ rv = evaluateToNodeSet(mParams[1],
+ aContext, getter_AddRefs(nodeSet2));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Make this true, even if nodeSet2 is empty. For relative URLs,
+ // we'll fail to load the document with an empty base URI, and for
+ // absolute URLs, the base URI doesn't matter
+ baseURISet = true;
+
+ if (!nodeSet2->isEmpty()) {
+ rv = txXPathNodeUtils::getBaseURI(nodeSet2->get(0), baseURI);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ }
+
+ if (exprResult1->getResultType() == txAExprResult::NODESET) {
+ // The first argument is a NodeSet, iterate on its nodes
+ txNodeSet* nodeSet1 = static_cast<txNodeSet*>
+ (static_cast<txAExprResult*>
+ (exprResult1));
+ int32_t i;
+ for (i = 0; i < nodeSet1->size(); ++i) {
+ const txXPathNode& node = nodeSet1->get(i);
+ nsAutoString uriStr;
+ txXPathNodeUtils::appendNodeValue(node, uriStr);
+ if (!baseURISet) {
+ // if the second argument wasn't specified, use
+ // the baseUri of node itself
+ rv = txXPathNodeUtils::getBaseURI(node, baseURI);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ retrieveNode(es, uriStr, baseURI, nodeSet);
+ }
+
+ NS_ADDREF(*aResult = nodeSet);
+
+ return NS_OK;
+ }
+
+ // The first argument is not a NodeSet
+ nsAutoString uriStr;
+ exprResult1->stringValue(uriStr);
+ const nsAString* base = baseURISet ? &baseURI : &mBaseURI;
+ retrieveNode(es, uriStr, *base, nodeSet);
+
+ NS_ADDREF(*aResult = nodeSet);
+
+ return NS_OK;
+}
+
+Expr::ResultType
+DocumentFunctionCall::getReturnType()
+{
+ return NODESET_RESULT;
+}
+
+bool
+DocumentFunctionCall::isSensitiveTo(ContextSensitivity aContext)
+{
+ return (aContext & PRIVATE_CONTEXT) || argsSensitiveTo(aContext);
+}
+
+#ifdef TX_TO_STRING
+nsresult
+DocumentFunctionCall::getNameAtom(nsIAtom** aAtom)
+{
+ *aAtom = nsGkAtoms::document;
+ NS_ADDREF(*aAtom);
+ return NS_OK;
+}
+#endif
diff --git a/dom/xslt/xslt/txEXSLTFunctions.cpp b/dom/xslt/xslt/txEXSLTFunctions.cpp
new file mode 100644
index 000000000..b226d9088
--- /dev/null
+++ b/dom/xslt/xslt/txEXSLTFunctions.cpp
@@ -0,0 +1,734 @@
+/* -*- 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 "nsIAtom.h"
+#include "nsGkAtoms.h"
+#include "txExecutionState.h"
+#include "txExpr.h"
+#include "txIXPathContext.h"
+#include "txNodeSet.h"
+#include "txOutputFormat.h"
+#include "txRtfHandler.h"
+#include "txXPathTreeWalker.h"
+#include "nsPrintfCString.h"
+#include "nsComponentManagerUtils.h"
+#include "nsContentCID.h"
+#include "nsContentCreatorFunctions.h"
+#include "nsIContent.h"
+#include "nsIDOMDocumentFragment.h"
+#include "txMozillaXMLOutput.h"
+#include "nsTextNode.h"
+#include "mozilla/dom/DocumentFragment.h"
+#include "prtime.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+class txStylesheetCompilerState;
+
+// ------------------------------------------------------------------
+// Utility functions
+// ------------------------------------------------------------------
+
+static nsresult
+convertRtfToNode(txIEvalContext *aContext, txResultTreeFragment *aRtf)
+{
+ txExecutionState* es =
+ static_cast<txExecutionState*>(aContext->getPrivateContext());
+ if (!es) {
+ NS_ERROR("Need txExecutionState!");
+
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ const txXPathNode& document = es->getSourceDocument();
+
+ nsIDocument *doc = txXPathNativeNode::getDocument(document);
+ nsCOMPtr<nsIDOMDocumentFragment> domFragment =
+ new DocumentFragment(doc->NodeInfoManager());
+
+ txOutputFormat format;
+ txMozillaXMLOutput mozHandler(&format, domFragment, true);
+
+ nsresult rv = aRtf->flushToHandler(&mozHandler);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mozHandler.closePrevious(true);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // The txResultTreeFragment will own this.
+ const txXPathNode* node = txXPathNativeNode::createXPathNode(domFragment,
+ true);
+ NS_ENSURE_TRUE(node, NS_ERROR_OUT_OF_MEMORY);
+
+ aRtf->setNode(node);
+
+ return NS_OK;
+}
+
+static nsresult
+createTextNode(txIEvalContext *aContext, nsString& aValue,
+ txXPathNode* *aResult)
+{
+ txExecutionState* es =
+ static_cast<txExecutionState*>(aContext->getPrivateContext());
+ if (!es) {
+ NS_ERROR("Need txExecutionState!");
+
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ const txXPathNode& document = es->getSourceDocument();
+
+ nsIDocument *doc = txXPathNativeNode::getDocument(document);
+ nsCOMPtr<nsIContent> text = new nsTextNode(doc->NodeInfoManager());
+
+ nsresult rv = text->SetText(aValue, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ *aResult = txXPathNativeNode::createXPathNode(text, true);
+ NS_ENSURE_TRUE(*aResult, NS_ERROR_OUT_OF_MEMORY);
+
+ return NS_OK;
+}
+
+static already_AddRefed<DocumentFragment>
+createDocFragment(txIEvalContext *aContext)
+{
+ txExecutionState* es =
+ static_cast<txExecutionState*>(aContext->getPrivateContext());
+ if (!es) {
+ NS_ERROR("Need txExecutionState!");
+
+ return nullptr;
+ }
+
+ const txXPathNode& document = es->getSourceDocument();
+ nsIDocument *doc = txXPathNativeNode::getDocument(document);
+ RefPtr<DocumentFragment> fragment =
+ new DocumentFragment(doc->NodeInfoManager());
+
+ return fragment.forget();
+}
+
+static nsresult
+createAndAddToResult(nsIAtom* aName, const nsSubstring& aValue,
+ txNodeSet* aResultSet, nsIContent* aResultHolder)
+{
+ NS_ASSERTION(aResultHolder->IsNodeOfType(nsINode::eDOCUMENT_FRAGMENT) &&
+ aResultHolder->OwnerDoc(),
+ "invalid result-holder");
+
+ nsIDocument* doc = aResultHolder->OwnerDoc();
+ nsCOMPtr<Element> elem = doc->CreateElem(nsDependentAtomString(aName),
+ nullptr, kNameSpaceID_None);
+ NS_ENSURE_TRUE(elem, NS_ERROR_NULL_POINTER);
+
+ RefPtr<nsTextNode> text = new nsTextNode(doc->NodeInfoManager());
+
+ nsresult rv = text->SetText(aValue, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = elem->AppendChildTo(text, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = aResultHolder->AppendChildTo(elem, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoPtr<txXPathNode> xpathNode(
+ txXPathNativeNode::createXPathNode(elem, true));
+ NS_ENSURE_TRUE(xpathNode, NS_ERROR_OUT_OF_MEMORY);
+
+ aResultSet->append(*xpathNode);
+
+ return NS_OK;
+}
+
+// Need to update this array if types are added to the ResultType enum in
+// txAExprResult.
+static const char * const sTypes[] = {
+ "node-set",
+ "boolean",
+ "number",
+ "string",
+ "RTF"
+};
+
+// ------------------------------------------------------------------
+// Function implementations
+// ------------------------------------------------------------------
+
+struct txEXSLTFunctionDescriptor
+{
+ int8_t mMinParams;
+ int8_t mMaxParams;
+ Expr::ResultType mReturnType;
+ int32_t mNamespaceID;
+ nsIAtom** mName;
+ const char* mNamespaceURI;
+};
+
+static const char kEXSLTCommonNS[] = "http://exslt.org/common";
+static const char kEXSLTSetsNS[] = "http://exslt.org/sets";
+static const char kEXSLTStringsNS[] = "http://exslt.org/strings";
+static const char kEXSLTMathNS[] = "http://exslt.org/math";
+static const char kEXSLTDatesAndTimesNS[] = "http://exslt.org/dates-and-times";
+
+// The order of this table must be the same as the
+// txEXSLTFunctionCall::eType enum
+static txEXSLTFunctionDescriptor descriptTable[] =
+{
+ { 1, 1, Expr::NODESET_RESULT, 0, &nsGkAtoms::nodeSet, kEXSLTCommonNS }, // NODE_SET
+ { 1, 1, Expr::STRING_RESULT, 0, &nsGkAtoms::objectType, kEXSLTCommonNS }, // OBJECT_TYPE
+ { 2, 2, Expr::NODESET_RESULT, 0, &nsGkAtoms::difference, kEXSLTSetsNS }, // DIFFERENCE
+ { 1, 1, Expr::NODESET_RESULT, 0, &nsGkAtoms::distinct, kEXSLTSetsNS }, // DISTINCT
+ { 2, 2, Expr::BOOLEAN_RESULT, 0, &nsGkAtoms::hasSameNode, kEXSLTSetsNS }, // HAS_SAME_NODE
+ { 2, 2, Expr::NODESET_RESULT, 0, &nsGkAtoms::intersection, kEXSLTSetsNS }, // INTERSECTION
+ { 2, 2, Expr::NODESET_RESULT, 0, &nsGkAtoms::leading, kEXSLTSetsNS }, // LEADING
+ { 2, 2, Expr::NODESET_RESULT, 0, &nsGkAtoms::trailing, kEXSLTSetsNS }, // TRAILING
+ { 1, 1, Expr::STRING_RESULT, 0, &nsGkAtoms::concat, kEXSLTStringsNS }, // CONCAT
+ { 1, 2, Expr::STRING_RESULT, 0, &nsGkAtoms::split, kEXSLTStringsNS }, // SPLIT
+ { 1, 2, Expr::STRING_RESULT, 0, &nsGkAtoms::tokenize, kEXSLTStringsNS }, // TOKENIZE
+ { 1, 1, Expr::NUMBER_RESULT, 0, &nsGkAtoms::max, kEXSLTMathNS }, // MAX
+ { 1, 1, Expr::NUMBER_RESULT, 0, &nsGkAtoms::min, kEXSLTMathNS }, // MIN
+ { 1, 1, Expr::NODESET_RESULT, 0, &nsGkAtoms::highest, kEXSLTMathNS }, // HIGHEST
+ { 1, 1, Expr::NODESET_RESULT, 0, &nsGkAtoms::lowest, kEXSLTMathNS }, // LOWEST
+ { 0, 0, Expr::STRING_RESULT, 0, &nsGkAtoms::dateTime, kEXSLTDatesAndTimesNS }, // DATE_TIME
+
+};
+
+class txEXSLTFunctionCall : public FunctionCall
+{
+public:
+ // The order of this enum must be the same as the descriptTable
+ // table above
+ enum eType {
+ // Set functions
+ NODE_SET,
+ OBJECT_TYPE,
+ DIFFERENCE,
+ DISTINCT,
+ HAS_SAME_NODE,
+ INTERSECTION,
+ LEADING,
+ TRAILING,
+ CONCAT,
+ SPLIT,
+ TOKENIZE,
+ MAX,
+ MIN,
+ HIGHEST,
+ LOWEST,
+ DATE_TIME
+ };
+
+ explicit txEXSLTFunctionCall(eType aType)
+ : mType(aType)
+ {
+ }
+
+ TX_DECL_FUNCTION
+
+private:
+ eType mType;
+};
+
+nsresult
+txEXSLTFunctionCall::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 NODE_SET:
+ {
+ RefPtr<txAExprResult> exprResult;
+ rv = mParams[0]->evaluate(aContext, getter_AddRefs(exprResult));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (exprResult->getResultType() == txAExprResult::NODESET) {
+ exprResult.forget(aResult);
+ }
+ else {
+ RefPtr<txNodeSet> resultSet;
+ rv = aContext->recycler()->
+ getNodeSet(getter_AddRefs(resultSet));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (exprResult->getResultType() ==
+ txAExprResult::RESULT_TREE_FRAGMENT) {
+ txResultTreeFragment *rtf =
+ static_cast<txResultTreeFragment*>
+ (exprResult.get());
+
+ const txXPathNode *node = rtf->getNode();
+ if (!node) {
+ rv = convertRtfToNode(aContext, rtf);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ node = rtf->getNode();
+ }
+
+ resultSet->append(*node);
+ }
+ else {
+ nsAutoString value;
+ exprResult->stringValue(value);
+
+ nsAutoPtr<txXPathNode> node;
+ rv = createTextNode(aContext, value,
+ getter_Transfers(node));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ resultSet->append(*node);
+ }
+
+ NS_ADDREF(*aResult = resultSet);
+ }
+
+ return NS_OK;
+ }
+ case OBJECT_TYPE:
+ {
+ RefPtr<txAExprResult> exprResult;
+ nsresult rv = mParams[0]->evaluate(aContext,
+ getter_AddRefs(exprResult));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ RefPtr<StringResult> strRes;
+ rv = aContext->recycler()->getStringResult(getter_AddRefs(strRes));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ AppendASCIItoUTF16(sTypes[exprResult->getResultType()],
+ strRes->mValue);
+
+ NS_ADDREF(*aResult = strRes);
+
+ return NS_OK;
+ }
+ case DIFFERENCE:
+ case INTERSECTION:
+ {
+ RefPtr<txNodeSet> nodes1;
+ rv = evaluateToNodeSet(mParams[0], aContext,
+ getter_AddRefs(nodes1));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ RefPtr<txNodeSet> nodes2;
+ rv = evaluateToNodeSet(mParams[1], aContext,
+ getter_AddRefs(nodes2));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ RefPtr<txNodeSet> resultSet;
+ rv = aContext->recycler()->getNodeSet(getter_AddRefs(resultSet));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool insertOnFound = mType == INTERSECTION;
+
+ int32_t searchPos = 0;
+ int32_t i, len = nodes1->size();
+ for (i = 0; i < len; ++i) {
+ const txXPathNode& node = nodes1->get(i);
+ int32_t foundPos = nodes2->indexOf(node, searchPos);
+ if (foundPos >= 0) {
+ searchPos = foundPos + 1;
+ }
+
+ if ((foundPos >= 0) == insertOnFound) {
+ rv = resultSet->append(node);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ }
+
+ NS_ADDREF(*aResult = resultSet);
+
+ return NS_OK;
+ }
+ case DISTINCT:
+ {
+ RefPtr<txNodeSet> nodes;
+ rv = evaluateToNodeSet(mParams[0], aContext,
+ getter_AddRefs(nodes));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ RefPtr<txNodeSet> resultSet;
+ rv = aContext->recycler()->getNodeSet(getter_AddRefs(resultSet));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsTHashtable<nsStringHashKey> hash;
+
+ int32_t i, len = nodes->size();
+ for (i = 0; i < len; ++i) {
+ nsAutoString str;
+ const txXPathNode& node = nodes->get(i);
+ txXPathNodeUtils::appendNodeValue(node, str);
+ if (!hash.GetEntry(str)) {
+ hash.PutEntry(str);
+ rv = resultSet->append(node);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ }
+
+ NS_ADDREF(*aResult = resultSet);
+
+ return NS_OK;
+ }
+ case HAS_SAME_NODE:
+ {
+ RefPtr<txNodeSet> nodes1;
+ rv = evaluateToNodeSet(mParams[0], aContext,
+ getter_AddRefs(nodes1));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ RefPtr<txNodeSet> nodes2;
+ rv = evaluateToNodeSet(mParams[1], aContext,
+ getter_AddRefs(nodes2));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool found = false;
+ int32_t i, len = nodes1->size();
+ for (i = 0; i < len; ++i) {
+ if (nodes2->contains(nodes1->get(i))) {
+ found = true;
+ break;
+ }
+ }
+
+ aContext->recycler()->getBoolResult(found, aResult);
+
+ return NS_OK;
+ }
+ case LEADING:
+ case TRAILING:
+ {
+ RefPtr<txNodeSet> nodes1;
+ rv = evaluateToNodeSet(mParams[0], aContext,
+ getter_AddRefs(nodes1));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ RefPtr<txNodeSet> nodes2;
+ rv = evaluateToNodeSet(mParams[1], aContext,
+ getter_AddRefs(nodes2));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (nodes2->isEmpty()) {
+ *aResult = nodes1;
+ NS_ADDREF(*aResult);
+
+ return NS_OK;
+ }
+
+ RefPtr<txNodeSet> resultSet;
+ rv = aContext->recycler()->getNodeSet(getter_AddRefs(resultSet));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ int32_t end = nodes1->indexOf(nodes2->get(0));
+ if (end >= 0) {
+ int32_t i = 0;
+ if (mType == TRAILING) {
+ i = end + 1;
+ end = nodes1->size();
+ }
+ for (; i < end; ++i) {
+ rv = resultSet->append(nodes1->get(i));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ }
+
+ NS_ADDREF(*aResult = resultSet);
+
+ return NS_OK;
+ }
+ case CONCAT:
+ {
+ RefPtr<txNodeSet> nodes;
+ rv = evaluateToNodeSet(mParams[0], aContext,
+ getter_AddRefs(nodes));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString str;
+ int32_t i, len = nodes->size();
+ for (i = 0; i < len; ++i) {
+ txXPathNodeUtils::appendNodeValue(nodes->get(i), str);
+ }
+
+ return aContext->recycler()->getStringResult(str, aResult);
+ }
+ case SPLIT:
+ case TOKENIZE:
+ {
+ // Evaluate parameters
+ nsAutoString string;
+ rv = mParams[0]->evaluateToString(aContext, string);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString pattern;
+ if (mParams.Length() == 2) {
+ rv = mParams[1]->evaluateToString(aContext, pattern);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ else if (mType == SPLIT) {
+ pattern.Assign(' ');
+ }
+ else {
+ pattern.AssignLiteral("\t\r\n ");
+ }
+
+ // Set up holders for the result
+ RefPtr<DocumentFragment> docFrag = createDocFragment(aContext);
+ NS_ENSURE_STATE(docFrag);
+
+ RefPtr<txNodeSet> resultSet;
+ rv = aContext->recycler()->getNodeSet(getter_AddRefs(resultSet));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ uint32_t tailIndex;
+
+ // Start splitting
+ if (pattern.IsEmpty()) {
+ nsString::const_char_iterator start = string.BeginReading();
+ nsString::const_char_iterator end = string.EndReading();
+ for (; start < end; ++start) {
+ rv = createAndAddToResult(nsGkAtoms::token,
+ Substring(start, start + 1),
+ resultSet, docFrag);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ tailIndex = string.Length();
+ }
+ else if (mType == SPLIT) {
+ nsAString::const_iterator strStart, strEnd;
+ string.BeginReading(strStart);
+ string.EndReading(strEnd);
+ nsAString::const_iterator start = strStart, end = strEnd;
+
+ while (FindInReadable(pattern, start, end)) {
+ if (start != strStart) {
+ rv = createAndAddToResult(nsGkAtoms::token,
+ Substring(strStart, start),
+ resultSet, docFrag);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ strStart = start = end;
+ end = strEnd;
+ }
+
+ tailIndex = strStart.get() - string.get();
+ }
+ else {
+ int32_t found, start = 0;
+ while ((found = string.FindCharInSet(pattern, start)) !=
+ kNotFound) {
+ if (found != start) {
+ rv = createAndAddToResult(nsGkAtoms::token,
+ Substring(string, start,
+ found - start),
+ resultSet, docFrag);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ start = found + 1;
+ }
+
+ tailIndex = start;
+ }
+
+ // Add tail if needed
+ if (tailIndex != (uint32_t)string.Length()) {
+ rv = createAndAddToResult(nsGkAtoms::token,
+ Substring(string, tailIndex),
+ resultSet, docFrag);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ NS_ADDREF(*aResult = resultSet);
+
+ return NS_OK;
+ }
+ case MAX:
+ case MIN:
+ {
+ RefPtr<txNodeSet> nodes;
+ rv = evaluateToNodeSet(mParams[0], aContext,
+ getter_AddRefs(nodes));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (nodes->isEmpty()) {
+ return aContext->recycler()->
+ getNumberResult(UnspecifiedNaN<double>(), aResult);
+ }
+
+ bool findMax = mType == MAX;
+
+ double res = findMax ? mozilla::NegativeInfinity<double>() :
+ mozilla::PositiveInfinity<double>();
+ int32_t i, len = nodes->size();
+ for (i = 0; i < len; ++i) {
+ nsAutoString str;
+ txXPathNodeUtils::appendNodeValue(nodes->get(i), str);
+ double val = txDouble::toDouble(str);
+ if (mozilla::IsNaN(val)) {
+ res = UnspecifiedNaN<double>();
+ break;
+ }
+
+ if (findMax ? (val > res) : (val < res)) {
+ res = val;
+ }
+ }
+
+ return aContext->recycler()->getNumberResult(res, aResult);
+ }
+ case HIGHEST:
+ case LOWEST:
+ {
+ RefPtr<txNodeSet> nodes;
+ rv = evaluateToNodeSet(mParams[0], aContext,
+ getter_AddRefs(nodes));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (nodes->isEmpty()) {
+ NS_ADDREF(*aResult = nodes);
+
+ return NS_OK;
+ }
+
+ RefPtr<txNodeSet> resultSet;
+ rv = aContext->recycler()->getNodeSet(getter_AddRefs(resultSet));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool findMax = mType == HIGHEST;
+ double res = findMax ? mozilla::NegativeInfinity<double>() :
+ mozilla::PositiveInfinity<double>();
+ int32_t i, len = nodes->size();
+ for (i = 0; i < len; ++i) {
+ nsAutoString str;
+ const txXPathNode& node = nodes->get(i);
+ txXPathNodeUtils::appendNodeValue(node, str);
+ double val = txDouble::toDouble(str);
+ if (mozilla::IsNaN(val)) {
+ resultSet->clear();
+ break;
+ }
+ if (findMax ? (val > res) : (val < res)) {
+ resultSet->clear();
+ res = val;
+ }
+
+ if (res == val) {
+ rv = resultSet->append(node);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ }
+
+ NS_ADDREF(*aResult = resultSet);
+
+ return NS_OK;
+ }
+ case DATE_TIME:
+ {
+ // http://exslt.org/date/functions/date-time/
+ // format: YYYY-MM-DDTTHH:MM:SS.sss+00:00
+ char formatstr[] = "%04hd-%02ld-%02ldT%02ld:%02ld:%02ld.%03ld%c%02ld:%02ld";
+
+ PRExplodedTime prtime;
+ PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &prtime);
+
+ int32_t offset = (prtime.tm_params.tp_gmt_offset +
+ prtime.tm_params.tp_dst_offset) / 60;
+
+ bool isneg = offset < 0;
+ if (isneg) offset = -offset;
+
+ StringResult* strRes;
+ rv = aContext->recycler()->getStringResult(&strRes);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ CopyASCIItoUTF16(nsPrintfCString(formatstr,
+ prtime.tm_year, prtime.tm_month + 1, prtime.tm_mday,
+ prtime.tm_hour, prtime.tm_min, prtime.tm_sec,
+ prtime.tm_usec / 10000,
+ isneg ? '-' : '+', offset / 60, offset % 60), strRes->mValue);
+
+ *aResult = strRes;
+
+ return NS_OK;
+ }
+ }
+
+ aContext->receiveError(NS_LITERAL_STRING("Internal error"),
+ NS_ERROR_UNEXPECTED);
+ return NS_ERROR_UNEXPECTED;
+}
+
+Expr::ResultType
+txEXSLTFunctionCall::getReturnType()
+{
+ return descriptTable[mType].mReturnType;
+}
+
+bool
+txEXSLTFunctionCall::isSensitiveTo(ContextSensitivity aContext)
+{
+ if (mType == NODE_SET || mType == SPLIT || mType == TOKENIZE) {
+ return (aContext & PRIVATE_CONTEXT) || argsSensitiveTo(aContext);
+ }
+ return argsSensitiveTo(aContext);
+}
+
+#ifdef TX_TO_STRING
+nsresult
+txEXSLTFunctionCall::getNameAtom(nsIAtom **aAtom)
+{
+ NS_ADDREF(*aAtom = *descriptTable[mType].mName);
+ return NS_OK;
+}
+#endif
+
+extern nsresult
+TX_ConstructEXSLTFunction(nsIAtom *aName,
+ int32_t aNamespaceID,
+ txStylesheetCompilerState* aState,
+ FunctionCall **aResult)
+{
+ uint32_t i;
+ for (i = 0; i < ArrayLength(descriptTable); ++i) {
+ txEXSLTFunctionDescriptor& desc = descriptTable[i];
+ if (aName == *desc.mName && aNamespaceID == desc.mNamespaceID) {
+ *aResult = new txEXSLTFunctionCall(
+ static_cast<txEXSLTFunctionCall::eType>(i));
+ return NS_OK;
+ }
+ }
+
+ return NS_ERROR_XPATH_UNKNOWN_FUNCTION;
+}
+
+extern bool
+TX_InitEXSLTFunction()
+{
+ uint32_t i;
+ for (i = 0; i < ArrayLength(descriptTable); ++i) {
+ txEXSLTFunctionDescriptor& desc = descriptTable[i];
+ NS_ConvertASCIItoUTF16 namespaceURI(desc.mNamespaceURI);
+ desc.mNamespaceID =
+ txNamespaceManager::getNamespaceID(namespaceURI);
+
+ if (desc.mNamespaceID == kNameSpaceID_Unknown) {
+ return false;
+ }
+ }
+
+ return true;
+}
diff --git a/dom/xslt/xslt/txEXSLTRegExFunctions.js b/dom/xslt/xslt/txEXSLTRegExFunctions.js
new file mode 100644
index 000000000..610060dfe
--- /dev/null
+++ b/dom/xslt/xslt/txEXSLTRegExFunctions.js
@@ -0,0 +1,69 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
+
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+const EXSLT_REGEXP_CID = Components.ID("{18a03189-067b-4978-b4f1-bafe35292ed6}");
+const EXSLT_REGEXP_CONTRACTID = "@mozilla.org/exslt/regexp;1";
+
+const NODESET_CONTRACTID = "@mozilla.org/transformiix-nodeset;1";
+
+const Ci = Components.interfaces;
+
+function txEXSLTRegExFunctions()
+{
+}
+
+var SingletonInstance = null;
+
+txEXSLTRegExFunctions.prototype = {
+ classID: EXSLT_REGEXP_CID,
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.txIEXSLTRegExFunctions]),
+
+ classInfo: XPCOMUtils.generateCI({classID: EXSLT_REGEXP_CID,
+ contractID: EXSLT_REGEXP_CONTRACTID,
+ interfaces: [Ci.txIEXSLTRegExFunctions]}),
+
+ // txIEXSLTRegExFunctions
+ match: function(context, str, regex, flags) {
+ var nodeset = Components.classes[NODESET_CONTRACTID]
+ .createInstance(Ci.txINodeSet);
+
+ var re = new RegExp(regex, flags);
+ var matches = str.match(re);
+ if (matches != null && matches.length > 0) {
+ var contextNode = context.contextNode;
+ var doc = contextNode.nodeType == Ci.nsIDOMNode.DOCUMENT_NODE ?
+ contextNode : contextNode.ownerDocument;
+ var docFrag = doc.createDocumentFragment();
+
+ for (var i = 0; i < matches.length; ++i) {
+ var match = matches[i];
+ var elem = doc.createElementNS(null, "match");
+ var text = doc.createTextNode(match ? match : '');
+ elem.appendChild(text);
+ docFrag.appendChild(elem);
+ nodeset.add(elem);
+ }
+ }
+
+ return nodeset;
+ },
+
+ replace: function(str, regex, flags, replace) {
+ var re = new RegExp(regex, flags);
+
+ return str.replace(re, replace);
+ },
+
+ test: function(str, regex, flags) {
+ var re = new RegExp(regex, flags);
+
+ return re.test(str);
+ }
+}
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([txEXSLTRegExFunctions]);
diff --git a/dom/xslt/xslt/txEXSLTRegExFunctions.manifest b/dom/xslt/xslt/txEXSLTRegExFunctions.manifest
new file mode 100644
index 000000000..0d2d17571
--- /dev/null
+++ b/dom/xslt/xslt/txEXSLTRegExFunctions.manifest
@@ -0,0 +1,3 @@
+component {18a03189-067b-4978-b4f1-bafe35292ed6} txEXSLTRegExFunctions.js
+contract @mozilla.org/exslt/regexp;1 {18a03189-067b-4978-b4f1-bafe35292ed6}
+category XSLT-extension-functions http://exslt.org/regular-expressions @mozilla.org/exslt/regexp;1
diff --git a/dom/xslt/xslt/txExecutionState.cpp b/dom/xslt/xslt/txExecutionState.cpp
new file mode 100644
index 000000000..869c3968b
--- /dev/null
+++ b/dom/xslt/xslt/txExecutionState.cpp
@@ -0,0 +1,561 @@
+/* -*- 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 "txExecutionState.h"
+#include "txSingleNodeContext.h"
+#include "txInstructions.h"
+#include "txStylesheet.h"
+#include "txVariableMap.h"
+#include "txRtfHandler.h"
+#include "txXSLTProcessor.h"
+#include "txLog.h"
+#include "txURIUtils.h"
+#include "txXMLParser.h"
+
+const int32_t txExecutionState::kMaxRecursionDepth = 20000;
+
+nsresult
+txLoadedDocumentsHash::init(const txXPathNode& aSource)
+{
+ mSourceDocument = txXPathNodeUtils::getOwnerDocument(aSource);
+
+ nsAutoString baseURI;
+ nsresult rv = txXPathNodeUtils::getBaseURI(*mSourceDocument, baseURI);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Technically the hash holds documents, but we allow any node that we're transforming
+ // from. In particular, the document() function uses this hash and it can return the
+ // source document, but if we're transforming from a document fragment (through
+ // txMozillaXSLTProcessor::SetSourceContentModel/txMozillaXSLTProcessor::DoTransform)
+ // or from another type of node (through txMozillaXSLTProcessor::TransformToDocument
+ // or txMozillaXSLTProcessor::TransformToFragment) it makes more sense to return the
+ // real root of the source tree, which is the node where the transform started.
+ PutEntry(baseURI)->mDocument = txXPathNativeNode::createXPathNode(txXPathNativeNode::getNode(aSource));
+ return NS_OK;
+}
+
+txLoadedDocumentsHash::~txLoadedDocumentsHash()
+{
+ if (mSourceDocument) {
+ nsAutoString baseURI;
+ nsresult rv = txXPathNodeUtils::getBaseURI(*mSourceDocument, baseURI);
+ if (NS_SUCCEEDED(rv)) {
+ txLoadedDocumentEntry* entry = GetEntry(baseURI);
+ if (entry) {
+ delete entry->mDocument.forget();
+ }
+ }
+ }
+}
+
+txExecutionState::txExecutionState(txStylesheet* aStylesheet,
+ bool aDisableLoads)
+ : mOutputHandler(nullptr),
+ mResultHandler(nullptr),
+ mStylesheet(aStylesheet),
+ mNextInstruction(nullptr),
+ mLocalVariables(nullptr),
+ mRecursionDepth(0),
+ mEvalContext(nullptr),
+ mInitialEvalContext(nullptr),
+ mGlobalParams(nullptr),
+ mKeyHash(aStylesheet->getKeyMap()),
+ mDisableLoads(aDisableLoads)
+{
+ MOZ_COUNT_CTOR(txExecutionState);
+}
+
+txExecutionState::~txExecutionState()
+{
+ MOZ_COUNT_DTOR(txExecutionState);
+
+ delete mResultHandler;
+ delete mLocalVariables;
+ if (mEvalContext != mInitialEvalContext) {
+ delete mEvalContext;
+ }
+
+ txStackIterator varsIter(&mLocalVarsStack);
+ while (varsIter.hasNext()) {
+ delete (txVariableMap*)varsIter.next();
+ }
+
+ txStackIterator contextIter(&mEvalContextStack);
+ while (contextIter.hasNext()) {
+ txIEvalContext* context = (txIEvalContext*)contextIter.next();
+ if (context != mInitialEvalContext) {
+ delete context;
+ }
+ }
+
+ txStackIterator handlerIter(&mResultHandlerStack);
+ while (handlerIter.hasNext()) {
+ txAXMLEventHandler* handler = (txAXMLEventHandler*)handlerIter.next();
+ if (handler != mObsoleteHandler) {
+ delete handler;
+ }
+ }
+
+ txStackIterator paramIter(&mParamStack);
+ while (paramIter.hasNext()) {
+ delete (txVariableMap*)paramIter.next();
+ }
+
+ delete mInitialEvalContext;
+}
+
+nsresult
+txExecutionState::init(const txXPathNode& aNode,
+ txOwningExpandedNameMap<txIGlobalParameter>* aGlobalParams)
+{
+ nsresult rv = NS_OK;
+
+ mGlobalParams = aGlobalParams;
+
+ // Set up initial context
+ mEvalContext = new txSingleNodeContext(aNode, this);
+ mInitialEvalContext = mEvalContext;
+
+ // Set up output and result-handler
+ txAXMLEventHandler* handler;
+ rv = mOutputHandlerFactory->
+ createHandlerWith(mStylesheet->getOutputFormat(), &handler);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mOutputHandler = handler;
+ mResultHandler = handler;
+ mOutputHandler->startDocument();
+
+ // Set up loaded-documents-hash
+ rv = mLoadedDocuments.init(aNode);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Init members
+ rv = mKeyHash.init();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mRecycler = new txResultRecycler;
+
+ // The actual value here doesn't really matter since noone should use this
+ // value. But lets put something errorlike in just in case
+ mGlobalVarPlaceholderValue = new StringResult(NS_LITERAL_STRING("Error"), nullptr);
+
+ // Initiate first instruction. This has to be done last since findTemplate
+ // might use us.
+ txStylesheet::ImportFrame* frame = 0;
+ txExpandedName nullName;
+ txInstruction* templ = mStylesheet->findTemplate(aNode, nullName,
+ this, nullptr, &frame);
+ pushTemplateRule(frame, nullName, nullptr);
+
+ return runTemplate(templ);
+}
+
+nsresult
+txExecutionState::end(nsresult aResult)
+{
+ NS_ASSERTION(NS_FAILED(aResult) || mTemplateRules.Length() == 1,
+ "Didn't clean up template rules properly");
+ if (NS_SUCCEEDED(aResult)) {
+ popTemplateRule();
+ }
+ else if (!mOutputHandler) {
+ return NS_OK;
+ }
+ return mOutputHandler->endDocument(aResult);
+}
+
+void
+txExecutionState::popAndDeleteEvalContext()
+{
+ if (!mEvalContextStack.isEmpty()) {
+ auto ctx = popEvalContext();
+ if (ctx != mInitialEvalContext) {
+ delete ctx;
+ }
+ }
+}
+
+void
+txExecutionState::popAndDeleteEvalContextUntil(txIEvalContext* aContext)
+{
+ auto ctx = popEvalContext();
+ while (ctx && ctx != aContext) {
+ MOZ_RELEASE_ASSERT(ctx != mInitialEvalContext);
+ delete ctx;
+ ctx = popEvalContext();
+ }
+}
+
+nsresult
+txExecutionState::getVariable(int32_t aNamespace, nsIAtom* aLName,
+ txAExprResult*& aResult)
+{
+ nsresult rv = NS_OK;
+ txExpandedName name(aNamespace, aLName);
+
+ // look for a local variable
+ if (mLocalVariables) {
+ mLocalVariables->getVariable(name, &aResult);
+ if (aResult) {
+ return NS_OK;
+ }
+ }
+
+ // look for an evaluated global variable
+ mGlobalVariableValues.getVariable(name, &aResult);
+ if (aResult) {
+ if (aResult == mGlobalVarPlaceholderValue) {
+ // XXX ErrorReport: cyclic variable-value
+ NS_RELEASE(aResult);
+ return NS_ERROR_XSLT_BAD_RECURSION;
+ }
+ return NS_OK;
+ }
+
+ // Is there perchance a global variable not evaluated yet?
+ txStylesheet::GlobalVariable* var = mStylesheet->getGlobalVariable(name);
+ if (!var) {
+ // XXX ErrorReport: variable doesn't exist in this scope
+ return NS_ERROR_FAILURE;
+ }
+
+ NS_ASSERTION((var->mExpr && !var->mFirstInstruction) ||
+ (!var->mExpr && var->mFirstInstruction),
+ "global variable should have either instruction or expression");
+
+ // Is this a stylesheet parameter that has a value?
+ if (var->mIsParam && mGlobalParams) {
+ txIGlobalParameter* param = mGlobalParams->get(name);
+ if (param) {
+ rv = param->getValue(&aResult);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mGlobalVariableValues.bindVariable(name, aResult);
+ if (NS_FAILED(rv)) {
+ NS_RELEASE(aResult);
+ return rv;
+ }
+
+ return NS_OK;
+ }
+ }
+
+ // Insert a placeholdervalue to protect against recursion
+ rv = mGlobalVariableValues.bindVariable(name, mGlobalVarPlaceholderValue);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // evaluate the global variable
+ pushEvalContext(mInitialEvalContext);
+ if (var->mExpr) {
+ txVariableMap* oldVars = mLocalVariables;
+ mLocalVariables = nullptr;
+ rv = var->mExpr->evaluate(getEvalContext(), &aResult);
+ mLocalVariables = oldVars;
+
+ if (NS_FAILED(rv)) {
+ popAndDeleteEvalContextUntil(mInitialEvalContext);
+ return rv;
+ }
+ }
+ else {
+ nsAutoPtr<txRtfHandler> rtfHandler(new txRtfHandler);
+
+ rv = pushResultHandler(rtfHandler);
+ if (NS_FAILED(rv)) {
+ popAndDeleteEvalContextUntil(mInitialEvalContext);
+ return rv;
+ }
+
+ rtfHandler.forget();
+
+ txInstruction* prevInstr = mNextInstruction;
+ // set return to nullptr to stop execution
+ mNextInstruction = nullptr;
+ rv = runTemplate(var->mFirstInstruction);
+ if (NS_FAILED(rv)) {
+ popAndDeleteEvalContextUntil(mInitialEvalContext);
+ return rv;
+ }
+
+ pushTemplateRule(nullptr, txExpandedName(), nullptr);
+ rv = txXSLTProcessor::execute(*this);
+ if (NS_FAILED(rv)) {
+ popAndDeleteEvalContextUntil(mInitialEvalContext);
+ return rv;
+ }
+
+ popTemplateRule();
+
+ mNextInstruction = prevInstr;
+ rtfHandler = (txRtfHandler*)popResultHandler();
+ rv = rtfHandler->getAsRTF(&aResult);
+ if (NS_FAILED(rv)) {
+ popAndDeleteEvalContextUntil(mInitialEvalContext);
+ return rv;
+ }
+ }
+ popEvalContext();
+
+ // Remove the placeholder and insert the calculated value
+ mGlobalVariableValues.removeVariable(name);
+ rv = mGlobalVariableValues.bindVariable(name, aResult);
+ if (NS_FAILED(rv)) {
+ NS_RELEASE(aResult);
+
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+bool
+txExecutionState::isStripSpaceAllowed(const txXPathNode& aNode)
+{
+ return mStylesheet->isStripSpaceAllowed(aNode, this);
+}
+
+void*
+txExecutionState::getPrivateContext()
+{
+ return this;
+}
+
+txResultRecycler*
+txExecutionState::recycler()
+{
+ return mRecycler;
+}
+
+void
+txExecutionState::receiveError(const nsAString& aMsg, nsresult aRes)
+{
+ // XXX implement me
+}
+
+nsresult
+txExecutionState::pushEvalContext(txIEvalContext* aContext)
+{
+ nsresult rv = mEvalContextStack.push(mEvalContext);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mEvalContext = aContext;
+
+ return NS_OK;
+}
+
+txIEvalContext*
+txExecutionState::popEvalContext()
+{
+ txIEvalContext* prev = mEvalContext;
+ mEvalContext = (txIEvalContext*)mEvalContextStack.pop();
+
+ return prev;
+}
+
+nsresult
+txExecutionState::pushBool(bool aBool)
+{
+ return mBoolStack.AppendElement(aBool) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+}
+
+bool
+txExecutionState::popBool()
+{
+ NS_ASSERTION(mBoolStack.Length(), "popping from empty stack");
+ uint32_t last = mBoolStack.Length() - 1;
+ NS_ENSURE_TRUE(last != (uint32_t)-1, false);
+
+ bool res = mBoolStack.ElementAt(last);
+ mBoolStack.RemoveElementAt(last);
+
+ return res;
+}
+
+nsresult
+txExecutionState::pushResultHandler(txAXMLEventHandler* aHandler)
+{
+ nsresult rv = mResultHandlerStack.push(mResultHandler);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mResultHandler = aHandler;
+
+ return NS_OK;
+}
+
+txAXMLEventHandler*
+txExecutionState::popResultHandler()
+{
+ txAXMLEventHandler* oldHandler = mResultHandler;
+ mResultHandler = (txAXMLEventHandler*)mResultHandlerStack.pop();
+
+ return oldHandler;
+}
+
+void
+txExecutionState::pushTemplateRule(txStylesheet::ImportFrame* aFrame,
+ const txExpandedName& aMode,
+ txVariableMap* aParams)
+{
+ TemplateRule* rule = mTemplateRules.AppendElement();
+ rule->mFrame = aFrame;
+ rule->mModeNsId = aMode.mNamespaceID;
+ rule->mModeLocalName = aMode.mLocalName;
+ rule->mParams = aParams;
+}
+
+void
+txExecutionState::popTemplateRule()
+{
+ NS_PRECONDITION(!mTemplateRules.IsEmpty(), "No rules to pop");
+ mTemplateRules.RemoveElementAt(mTemplateRules.Length() - 1);
+}
+
+txIEvalContext*
+txExecutionState::getEvalContext()
+{
+ return mEvalContext;
+}
+
+const txXPathNode*
+txExecutionState::retrieveDocument(const nsAString& aUri)
+{
+ NS_ASSERTION(!aUri.Contains(char16_t('#')),
+ "Remove the fragment.");
+
+ if (mDisableLoads) {
+ return nullptr;
+ }
+
+ MOZ_LOG(txLog::xslt, LogLevel::Debug,
+ ("Retrieve Document %s", NS_LossyConvertUTF16toASCII(aUri).get()));
+
+ // try to get already loaded document
+ txLoadedDocumentEntry *entry = mLoadedDocuments.PutEntry(aUri);
+ if (!entry) {
+ return nullptr;
+ }
+
+ if (!entry->mDocument && !entry->LoadingFailed()) {
+ // open URI
+ nsAutoString errMsg;
+ // XXX we should get the loader from the actual node
+ // triggering the load, but this will do for the time being
+ entry->mLoadResult =
+ txParseDocumentFromURI(aUri, *mLoadedDocuments.mSourceDocument,
+ errMsg, getter_Transfers(entry->mDocument));
+
+ if (entry->LoadingFailed()) {
+ receiveError(NS_LITERAL_STRING("Couldn't load document '") +
+ aUri + NS_LITERAL_STRING("': ") + errMsg,
+ entry->mLoadResult);
+ }
+ }
+
+ return entry->mDocument;
+}
+
+nsresult
+txExecutionState::getKeyNodes(const txExpandedName& aKeyName,
+ const txXPathNode& aRoot,
+ const nsAString& aKeyValue,
+ bool aIndexIfNotFound,
+ txNodeSet** aResult)
+{
+ return mKeyHash.getKeyNodes(aKeyName, aRoot, aKeyValue,
+ aIndexIfNotFound, *this, aResult);
+}
+
+txExecutionState::TemplateRule*
+txExecutionState::getCurrentTemplateRule()
+{
+ NS_PRECONDITION(!mTemplateRules.IsEmpty(), "No current rule!");
+ return &mTemplateRules[mTemplateRules.Length() - 1];
+}
+
+txInstruction*
+txExecutionState::getNextInstruction()
+{
+ txInstruction* instr = mNextInstruction;
+ if (instr) {
+ mNextInstruction = instr->mNext;
+ }
+
+ return instr;
+}
+
+nsresult
+txExecutionState::runTemplate(txInstruction* aTemplate)
+{
+ NS_ENSURE_TRUE(++mRecursionDepth < kMaxRecursionDepth,
+ NS_ERROR_XSLT_BAD_RECURSION);
+
+ nsresult rv = mLocalVarsStack.push(mLocalVariables);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mReturnStack.push(mNextInstruction);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mLocalVariables = nullptr;
+ mNextInstruction = aTemplate;
+
+ return NS_OK;
+}
+
+void
+txExecutionState::gotoInstruction(txInstruction* aNext)
+{
+ mNextInstruction = aNext;
+}
+
+void
+txExecutionState::returnFromTemplate()
+{
+ --mRecursionDepth;
+ NS_ASSERTION(!mReturnStack.isEmpty() && !mLocalVarsStack.isEmpty(),
+ "return or variable stack is empty");
+ delete mLocalVariables;
+ mNextInstruction = (txInstruction*)mReturnStack.pop();
+ mLocalVariables = (txVariableMap*)mLocalVarsStack.pop();
+}
+
+nsresult
+txExecutionState::bindVariable(const txExpandedName& aName,
+ txAExprResult* aValue)
+{
+ if (!mLocalVariables) {
+ mLocalVariables = new txVariableMap;
+ }
+ return mLocalVariables->bindVariable(aName, aValue);
+}
+
+void
+txExecutionState::removeVariable(const txExpandedName& aName)
+{
+ mLocalVariables->removeVariable(aName);
+}
+
+nsresult
+txExecutionState::pushParamMap(txVariableMap* aParams)
+{
+ nsresult rv = mParamStack.push(mTemplateParams);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mTemplateParams.forget();
+ mTemplateParams = aParams;
+
+ return NS_OK;
+}
+
+txVariableMap*
+txExecutionState::popParamMap()
+{
+ txVariableMap* oldParams = mTemplateParams.forget();
+ mTemplateParams = (txVariableMap*)mParamStack.pop();
+
+ return oldParams;
+}
diff --git a/dom/xslt/xslt/txExecutionState.h b/dom/xslt/xslt/txExecutionState.h
new file mode 100644
index 000000000..cd5467179
--- /dev/null
+++ b/dom/xslt/xslt/txExecutionState.h
@@ -0,0 +1,181 @@
+/* -*- 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_TXEXECUTIONSTATE_H
+#define TRANSFRMX_TXEXECUTIONSTATE_H
+
+#include "txCore.h"
+#include "txStack.h"
+#include "txXMLUtils.h"
+#include "txIXPathContext.h"
+#include "txVariableMap.h"
+#include "nsTHashtable.h"
+#include "nsHashKeys.h"
+#include "txKey.h"
+#include "txStylesheet.h"
+#include "txXPathTreeWalker.h"
+#include "nsTArray.h"
+
+class txAOutputHandlerFactory;
+class txAXMLEventHandler;
+class txInstruction;
+
+class txLoadedDocumentEntry : public nsStringHashKey
+{
+public:
+ explicit txLoadedDocumentEntry(KeyTypePointer aStr) : nsStringHashKey(aStr),
+ mLoadResult(NS_OK)
+ {
+ }
+ txLoadedDocumentEntry(const txLoadedDocumentEntry& aToCopy)
+ : nsStringHashKey(aToCopy)
+ {
+ NS_ERROR("We're horked.");
+ }
+ ~txLoadedDocumentEntry()
+ {
+ if (mDocument) {
+ txXPathNodeUtils::release(mDocument);
+ }
+ }
+ bool LoadingFailed()
+ {
+ NS_ASSERTION(NS_SUCCEEDED(mLoadResult) || !mDocument,
+ "Load failed but we still got a document?");
+
+ return NS_FAILED(mLoadResult);
+ }
+
+ nsAutoPtr<txXPathNode> mDocument;
+ nsresult mLoadResult;
+};
+
+class txLoadedDocumentsHash : public nsTHashtable<txLoadedDocumentEntry>
+{
+public:
+ txLoadedDocumentsHash()
+ : nsTHashtable<txLoadedDocumentEntry>(4)
+ {
+ }
+ ~txLoadedDocumentsHash();
+ MOZ_MUST_USE nsresult init(const txXPathNode& aSource);
+
+private:
+ friend class txExecutionState;
+ nsAutoPtr<txXPathNode> mSourceDocument;
+};
+
+
+class txExecutionState : public txIMatchContext
+{
+public:
+ txExecutionState(txStylesheet* aStylesheet, bool aDisableLoads);
+ ~txExecutionState();
+ nsresult init(const txXPathNode& aNode,
+ txOwningExpandedNameMap<txIGlobalParameter>* aGlobalParams);
+ nsresult end(nsresult aResult);
+
+ TX_DECL_MATCH_CONTEXT;
+
+ /**
+ * Struct holding information about a current template rule
+ */
+ class TemplateRule {
+ public:
+ txStylesheet::ImportFrame* mFrame;
+ int32_t mModeNsId;
+ nsCOMPtr<nsIAtom> mModeLocalName;
+ txVariableMap* mParams;
+ };
+
+ // Stack functions
+ nsresult pushEvalContext(txIEvalContext* aContext);
+ txIEvalContext* popEvalContext();
+
+ void popAndDeleteEvalContext();
+
+ /**
+ * Helper that deletes all entries before |aContext| and then
+ * pops it off the stack. The caller must delete |aContext| if
+ * desired.
+ */
+ void popAndDeleteEvalContextUntil(txIEvalContext* aContext);
+
+ nsresult pushBool(bool aBool);
+ bool popBool();
+ nsresult pushResultHandler(txAXMLEventHandler* aHandler);
+ txAXMLEventHandler* popResultHandler();
+ void pushTemplateRule(txStylesheet::ImportFrame* aFrame,
+ const txExpandedName& aMode,
+ txVariableMap* aParams);
+ void popTemplateRule();
+ nsresult pushParamMap(txVariableMap* aParams);
+ txVariableMap* popParamMap();
+
+ // state-getting functions
+ txIEvalContext* getEvalContext();
+ const txXPathNode* retrieveDocument(const nsAString& aUri);
+ nsresult getKeyNodes(const txExpandedName& aKeyName,
+ const txXPathNode& aRoot,
+ const nsAString& aKeyValue, bool aIndexIfNotFound,
+ txNodeSet** aResult);
+ TemplateRule* getCurrentTemplateRule();
+ const txXPathNode& getSourceDocument()
+ {
+ NS_ASSERTION(mLoadedDocuments.mSourceDocument,
+ "Need a source document!");
+
+ return *mLoadedDocuments.mSourceDocument;
+ }
+
+ // state-modification functions
+ txInstruction* getNextInstruction();
+ nsresult runTemplate(txInstruction* aInstruction);
+ nsresult runTemplate(txInstruction* aInstruction,
+ txInstruction* aReturnTo);
+ void gotoInstruction(txInstruction* aNext);
+ void returnFromTemplate();
+ nsresult bindVariable(const txExpandedName& aName,
+ txAExprResult* aValue);
+ void removeVariable(const txExpandedName& aName);
+
+ txAXMLEventHandler* mOutputHandler;
+ txAXMLEventHandler* mResultHandler;
+ nsAutoPtr<txAXMLEventHandler> mObsoleteHandler;
+ txAOutputHandlerFactory* mOutputHandlerFactory;
+
+ nsAutoPtr<txVariableMap> mTemplateParams;
+
+ RefPtr<txStylesheet> mStylesheet;
+
+private:
+ txStack mReturnStack;
+ txStack mLocalVarsStack;
+ txStack mEvalContextStack;
+ nsTArray<bool> mBoolStack;
+ txStack mResultHandlerStack;
+ txStack mParamStack;
+ txInstruction* mNextInstruction;
+ txVariableMap* mLocalVariables;
+ txVariableMap mGlobalVariableValues;
+ RefPtr<txAExprResult> mGlobalVarPlaceholderValue;
+ int32_t mRecursionDepth;
+
+ AutoTArray<TemplateRule, 10> mTemplateRules;
+
+ txIEvalContext* mEvalContext;
+ txIEvalContext* mInitialEvalContext;
+ //Document* mRTFDocument;
+ txOwningExpandedNameMap<txIGlobalParameter>* mGlobalParams;
+
+ txLoadedDocumentsHash mLoadedDocuments;
+ txKeyHash mKeyHash;
+ RefPtr<txResultRecycler> mRecycler;
+ bool mDisableLoads;
+
+ static const int32_t kMaxRecursionDepth;
+};
+
+#endif
diff --git a/dom/xslt/xslt/txFormatNumberFunctionCall.cpp b/dom/xslt/xslt/txFormatNumberFunctionCall.cpp
new file mode 100644
index 000000000..a4d4b8fb0
--- /dev/null
+++ b/dom/xslt/xslt/txFormatNumberFunctionCall.cpp
@@ -0,0 +1,430 @@
+/* -*- 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 "txXSLTFunctions.h"
+#include "nsGkAtoms.h"
+#include "txIXPathContext.h"
+#include "txStylesheet.h"
+#include <math.h>
+#include "txNamespaceMap.h"
+
+#include "prdtoa.h"
+
+#define INVALID_PARAM_VALUE \
+ NS_LITERAL_STRING("invalid parameter value for function")
+
+const char16_t txFormatNumberFunctionCall::FORMAT_QUOTE = '\'';
+
+/*
+ * FormatNumberFunctionCall
+ * A representation of the XSLT additional function: format-number()
+ */
+
+/*
+ * Creates a new format-number function call
+ */
+txFormatNumberFunctionCall::txFormatNumberFunctionCall(txStylesheet* aStylesheet,
+ txNamespaceMap* aMappings)
+ : mStylesheet(aStylesheet),
+ mMappings(aMappings)
+{
+}
+
+/*
+ * Evaluates this Expr based on the given context node and processor state
+ * @param context the context node for evaluation of this Expr
+ * @param cs the ContextState containing the stack information needed
+ * for evaluation
+ * @return the result of the evaluation
+ */
+nsresult
+txFormatNumberFunctionCall::evaluate(txIEvalContext* aContext,
+ txAExprResult** aResult)
+{
+ *aResult = nullptr;
+ if (!requireParams(2, 3, aContext))
+ return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT;
+
+ // Get number and format
+ double value;
+ txExpandedName formatName;
+
+ nsresult rv = evaluateToNumber(mParams[0], aContext, &value);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString formatStr;
+ rv = mParams[1]->evaluateToString(aContext, formatStr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (mParams.Length() == 3) {
+ nsAutoString formatQName;
+ rv = mParams[2]->evaluateToString(aContext, formatQName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = formatName.init(formatQName, mMappings, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ txDecimalFormat* format = mStylesheet->getDecimalFormat(formatName);
+ if (!format) {
+ nsAutoString err(NS_LITERAL_STRING("unknown decimal format"));
+#ifdef TX_TO_STRING
+ err.AppendLiteral(" for: ");
+ toString(err);
+#endif
+ aContext->receiveError(err, NS_ERROR_XPATH_INVALID_ARG);
+ return NS_ERROR_XPATH_INVALID_ARG;
+ }
+
+ // Special cases
+ if (mozilla::IsNaN(value)) {
+ return aContext->recycler()->getStringResult(format->mNaN, aResult);
+ }
+
+ if (value == mozilla::PositiveInfinity<double>()) {
+ return aContext->recycler()->getStringResult(format->mInfinity,
+ aResult);
+ }
+
+ if (value == mozilla::NegativeInfinity<double>()) {
+ nsAutoString res;
+ res.Append(format->mMinusSign);
+ res.Append(format->mInfinity);
+ return aContext->recycler()->getStringResult(res, aResult);
+ }
+
+ // Value is a normal finite number
+ nsAutoString prefix;
+ nsAutoString suffix;
+ int minIntegerSize=0;
+ int minFractionSize=0;
+ int maxFractionSize=0;
+ int multiplier=1;
+ int groupSize=-1;
+
+ uint32_t pos = 0;
+ uint32_t formatLen = formatStr.Length();
+ bool inQuote;
+
+ // Get right subexpression
+ inQuote = false;
+ if (mozilla::IsNegative(value)) {
+ while (pos < formatLen &&
+ (inQuote ||
+ formatStr.CharAt(pos) != format->mPatternSeparator)) {
+ if (formatStr.CharAt(pos) == FORMAT_QUOTE)
+ inQuote = !inQuote;
+ pos++;
+ }
+
+ if (pos == formatLen) {
+ pos = 0;
+ prefix.Append(format->mMinusSign);
+ }
+ else
+ pos++;
+ }
+
+ // Parse the format string
+ FormatParseState pState = Prefix;
+ inQuote = false;
+
+ char16_t c = 0;
+ while (pos < formatLen && pState != Finished) {
+ c=formatStr.CharAt(pos++);
+
+ switch (pState) {
+
+ case Prefix:
+ case Suffix:
+ if (!inQuote) {
+ if (c == format->mPercent) {
+ if (multiplier == 1)
+ multiplier = 100;
+ else {
+ nsAutoString err(INVALID_PARAM_VALUE);
+#ifdef TX_TO_STRING
+ err.AppendLiteral(": ");
+ toString(err);
+#endif
+ aContext->receiveError(err,
+ NS_ERROR_XPATH_INVALID_ARG);
+ return NS_ERROR_XPATH_INVALID_ARG;
+ }
+ }
+ else if (c == format->mPerMille) {
+ if (multiplier == 1)
+ multiplier = 1000;
+ else {
+ nsAutoString err(INVALID_PARAM_VALUE);
+#ifdef TX_TO_STRING
+ err.AppendLiteral(": ");
+ toString(err);
+#endif
+ aContext->receiveError(err,
+ NS_ERROR_XPATH_INVALID_ARG);
+ return NS_ERROR_XPATH_INVALID_ARG;
+ }
+ }
+ else if (c == format->mDecimalSeparator ||
+ c == format->mGroupingSeparator ||
+ c == format->mZeroDigit ||
+ c == format->mDigit ||
+ c == format->mPatternSeparator) {
+ pState = pState == Prefix ? IntDigit : Finished;
+ pos--;
+ break;
+ }
+ }
+
+ if (c == FORMAT_QUOTE)
+ inQuote = !inQuote;
+ else if (pState == Prefix)
+ prefix.Append(c);
+ else
+ suffix.Append(c);
+ break;
+
+ case IntDigit:
+ if (c == format->mGroupingSeparator)
+ groupSize=0;
+ else if (c == format->mDigit) {
+ if (groupSize >= 0)
+ groupSize++;
+ }
+ else {
+ pState = IntZero;
+ pos--;
+ }
+ break;
+
+ case IntZero:
+ if (c == format->mGroupingSeparator)
+ groupSize = 0;
+ else if (c == format->mZeroDigit) {
+ if (groupSize >= 0)
+ groupSize++;
+ minIntegerSize++;
+ }
+ else if (c == format->mDecimalSeparator) {
+ pState = FracZero;
+ }
+ else {
+ pState = Suffix;
+ pos--;
+ }
+ break;
+
+ case FracZero:
+ if (c == format->mZeroDigit) {
+ maxFractionSize++;
+ minFractionSize++;
+ }
+ else {
+ pState = FracDigit;
+ pos--;
+ }
+ break;
+
+ case FracDigit:
+ if (c == format->mDigit)
+ maxFractionSize++;
+ else {
+ pState = Suffix;
+ pos--;
+ }
+ break;
+
+ case Finished:
+ break;
+ }
+ }
+
+ // Did we manage to parse the entire formatstring and was it valid
+ if ((c != format->mPatternSeparator && pos < formatLen) ||
+ inQuote ||
+ groupSize == 0) {
+ nsAutoString err(INVALID_PARAM_VALUE);
+#ifdef TX_TO_STRING
+ err.AppendLiteral(": ");
+ toString(err);
+#endif
+ aContext->receiveError(err, NS_ERROR_XPATH_INVALID_ARG);
+ return NS_ERROR_XPATH_INVALID_ARG;
+ }
+
+
+ /*
+ * FINALLY we're done with the parsing
+ * now build the result string
+ */
+
+ value = fabs(value) * multiplier;
+
+ // Prefix
+ nsAutoString res(prefix);
+
+ int bufsize;
+ if (value > 1)
+ bufsize = (int)log10(value) + 30;
+ else
+ bufsize = 1 + 30;
+
+ char* buf = new char[bufsize];
+ int bufIntDigits, sign;
+ char* endp;
+ PR_dtoa(value, 0, 0, &bufIntDigits, &sign, &endp, buf, bufsize-1);
+
+ int buflen = endp - buf;
+ int intDigits;
+ intDigits = bufIntDigits > minIntegerSize ? bufIntDigits : minIntegerSize;
+
+ if (groupSize < 0)
+ groupSize = intDigits + 10; //to simplify grouping
+
+ // XXX We shouldn't use SetLength.
+ res.SetLength(res.Length() +
+ intDigits + // integer digits
+ 1 + // decimal separator
+ maxFractionSize + // fractions
+ (intDigits-1)/groupSize); // group separators
+
+ int32_t i = bufIntDigits + maxFractionSize - 1;
+ bool carry = (0 <= i+1) && (i+1 < buflen) && (buf[i+1] >= '5');
+ bool hasFraction = false;
+
+ uint32_t resPos = res.Length()-1;
+
+ // Fractions
+ for (; i >= bufIntDigits; --i) {
+ int digit;
+ if (i >= buflen || i < 0) {
+ digit = 0;
+ }
+ else {
+ digit = buf[i] - '0';
+ }
+
+ if (carry) {
+ digit = (digit + 1) % 10;
+ carry = digit == 0;
+ }
+
+ if (hasFraction || digit != 0 || i < bufIntDigits+minFractionSize) {
+ hasFraction = true;
+ res.SetCharAt((char16_t)(digit + format->mZeroDigit),
+ resPos--);
+ }
+ else {
+ res.Truncate(resPos--);
+ }
+ }
+
+ // Decimal separator
+ if (hasFraction) {
+ res.SetCharAt(format->mDecimalSeparator, resPos--);
+ }
+ else {
+ res.Truncate(resPos--);
+ }
+
+ // Integer digits
+ for (i = 0; i < intDigits; ++i) {
+ int digit;
+ if (bufIntDigits-i-1 >= buflen || bufIntDigits-i-1 < 0) {
+ digit = 0;
+ }
+ else {
+ digit = buf[bufIntDigits-i-1] - '0';
+ }
+
+ if (carry) {
+ digit = (digit + 1) % 10;
+ carry = digit == 0;
+ }
+
+ if (i != 0 && i%groupSize == 0) {
+ res.SetCharAt(format->mGroupingSeparator, resPos--);
+ }
+
+ res.SetCharAt((char16_t)(digit + format->mZeroDigit), resPos--);
+ }
+
+ if (carry) {
+ if (i%groupSize == 0) {
+ res.Insert(format->mGroupingSeparator, resPos + 1);
+ }
+ res.Insert((char16_t)(1 + format->mZeroDigit), resPos + 1);
+ }
+
+ if (!hasFraction && !intDigits && !carry) {
+ // If we havn't added any characters we add a '0'
+ // This can only happen for formats like '##.##'
+ res.Append(format->mZeroDigit);
+ }
+
+ delete [] buf;
+
+ // Build suffix
+ res.Append(suffix);
+
+ return aContext->recycler()->getStringResult(res, aResult);
+} //-- evaluate
+
+Expr::ResultType
+txFormatNumberFunctionCall::getReturnType()
+{
+ return STRING_RESULT;
+}
+
+bool
+txFormatNumberFunctionCall::isSensitiveTo(ContextSensitivity aContext)
+{
+ return argsSensitiveTo(aContext);
+}
+
+#ifdef TX_TO_STRING
+nsresult
+txFormatNumberFunctionCall::getNameAtom(nsIAtom** aAtom)
+{
+ *aAtom = nsGkAtoms::formatNumber;
+ NS_ADDREF(*aAtom);
+ return NS_OK;
+}
+#endif
+
+/*
+ * txDecimalFormat
+ * A representation of the XSLT element <xsl:decimal-format>
+ */
+
+txDecimalFormat::txDecimalFormat() : mInfinity(NS_LITERAL_STRING("Infinity")),
+ mNaN(NS_LITERAL_STRING("NaN"))
+{
+ mDecimalSeparator = '.';
+ mGroupingSeparator = ',';
+ mMinusSign = '-';
+ mPercent = '%';
+ mPerMille = 0x2030;
+ mZeroDigit = '0';
+ mDigit = '#';
+ mPatternSeparator = ';';
+}
+
+bool txDecimalFormat::isEqual(txDecimalFormat* other)
+{
+ return mDecimalSeparator == other->mDecimalSeparator &&
+ mGroupingSeparator == other->mGroupingSeparator &&
+ mInfinity.Equals(other->mInfinity) &&
+ mMinusSign == other->mMinusSign &&
+ mNaN.Equals(other->mNaN) &&
+ mPercent == other->mPercent &&
+ mPerMille == other->mPerMille &&
+ mZeroDigit == other->mZeroDigit &&
+ mDigit == other->mDigit &&
+ mPatternSeparator == other->mPatternSeparator;
+}
diff --git a/dom/xslt/xslt/txGenerateIdFunctionCall.cpp b/dom/xslt/xslt/txGenerateIdFunctionCall.cpp
new file mode 100644
index 000000000..e02090745
--- /dev/null
+++ b/dom/xslt/xslt/txGenerateIdFunctionCall.cpp
@@ -0,0 +1,114 @@
+/* -*- 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 "nsGkAtoms.h"
+#include "txIXPathContext.h"
+#include "txNodeSet.h"
+#include "txXPathTreeWalker.h"
+#include "txXSLTFunctions.h"
+#include "txExecutionState.h"
+
+/*
+ Implementation of XSLT 1.0 extension function: generate-id
+*/
+
+/**
+ * Creates a new generate-id function call
+**/
+GenerateIdFunctionCall::GenerateIdFunctionCall()
+{
+}
+
+/**
+ * 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
+ * @see FunctionCall.h
+**/
+nsresult
+GenerateIdFunctionCall::evaluate(txIEvalContext* aContext,
+ txAExprResult** aResult)
+{
+ *aResult = nullptr;
+ if (!requireParams(0, 1, aContext))
+ return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT;
+
+ txExecutionState* es =
+ static_cast<txExecutionState*>(aContext->getPrivateContext());
+ if (!es) {
+ NS_ERROR(
+ "called xslt extension function \"generate-id\" with wrong context");
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ nsresult rv = NS_OK;
+ if (mParams.IsEmpty()) {
+ StringResult* strRes;
+ rv = aContext->recycler()->getStringResult(&strRes);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ txXPathNodeUtils::getXSLTId(aContext->getContextNode(),
+ es->getSourceDocument(),
+ strRes->mValue);
+
+ *aResult = strRes;
+
+ return NS_OK;
+ }
+
+ RefPtr<txNodeSet> nodes;
+ rv = evaluateToNodeSet(mParams[0], aContext,
+ getter_AddRefs(nodes));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (nodes->isEmpty()) {
+ aContext->recycler()->getEmptyStringResult(aResult);
+
+ return NS_OK;
+ }
+
+ StringResult* strRes;
+ rv = aContext->recycler()->getStringResult(&strRes);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ txXPathNodeUtils::getXSLTId(nodes->get(0), es->getSourceDocument(),
+ strRes->mValue);
+
+ *aResult = strRes;
+
+ return NS_OK;
+}
+
+Expr::ResultType
+GenerateIdFunctionCall::getReturnType()
+{
+ return STRING_RESULT;
+}
+
+bool
+GenerateIdFunctionCall::isSensitiveTo(ContextSensitivity aContext)
+{
+ if (aContext & PRIVATE_CONTEXT) {
+ return true;
+ }
+
+ if (mParams.IsEmpty()) {
+ return !!(aContext & NODE_CONTEXT);
+ }
+
+ return argsSensitiveTo(aContext);
+}
+
+#ifdef TX_TO_STRING
+nsresult
+GenerateIdFunctionCall::getNameAtom(nsIAtom** aAtom)
+{
+ *aAtom = nsGkAtoms::generateId;
+ NS_ADDREF(*aAtom);
+ return NS_OK;
+}
+#endif
diff --git a/dom/xslt/xslt/txInstructions.cpp b/dom/xslt/xslt/txInstructions.cpp
new file mode 100644
index 000000000..f76968047
--- /dev/null
+++ b/dom/xslt/xslt/txInstructions.cpp
@@ -0,0 +1,944 @@
+/* -*- 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 "txInstructions.h"
+#include "nsError.h"
+#include "txExpr.h"
+#include "txStylesheet.h"
+#include "txNodeSetContext.h"
+#include "txTextHandler.h"
+#include "nsIConsoleService.h"
+#include "nsServiceManagerUtils.h"
+#include "txStringUtils.h"
+#include "nsGkAtoms.h"
+#include "txRtfHandler.h"
+#include "txNodeSorter.h"
+#include "txXSLTNumber.h"
+#include "txExecutionState.h"
+
+using mozilla::Move;
+
+nsresult
+txApplyDefaultElementTemplate::execute(txExecutionState& aEs)
+{
+ txExecutionState::TemplateRule* rule = aEs.getCurrentTemplateRule();
+ txExpandedName mode(rule->mModeNsId, rule->mModeLocalName);
+ txStylesheet::ImportFrame* frame = 0;
+ txInstruction* templ =
+ aEs.mStylesheet->findTemplate(aEs.getEvalContext()->getContextNode(),
+ mode, &aEs, nullptr, &frame);
+
+ aEs.pushTemplateRule(frame, mode, aEs.mTemplateParams);
+
+ return aEs.runTemplate(templ);
+}
+
+nsresult
+txApplyImports::execute(txExecutionState& aEs)
+{
+ txExecutionState::TemplateRule* rule = aEs.getCurrentTemplateRule();
+ // The frame is set to null when there is no current template rule, or
+ // when the current template rule is a default template. However this
+ // instruction isn't used in default templates.
+ if (!rule->mFrame) {
+ // XXX ErrorReport: apply-imports instantiated without a current rule
+ return NS_ERROR_XSLT_EXECUTION_FAILURE;
+ }
+
+ nsresult rv = aEs.pushParamMap(rule->mParams);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ txStylesheet::ImportFrame* frame = 0;
+ txExpandedName mode(rule->mModeNsId, rule->mModeLocalName);
+ txInstruction* templ =
+ aEs.mStylesheet->findTemplate(aEs.getEvalContext()->getContextNode(),
+ mode, &aEs, rule->mFrame, &frame);
+
+ aEs.pushTemplateRule(frame, mode, rule->mParams);
+
+ rv = aEs.runTemplate(templ);
+
+ aEs.popTemplateRule();
+ aEs.popParamMap();
+
+ return rv;
+}
+
+txApplyTemplates::txApplyTemplates(const txExpandedName& aMode)
+ : mMode(aMode)
+{
+}
+
+nsresult
+txApplyTemplates::execute(txExecutionState& aEs)
+{
+ txStylesheet::ImportFrame* frame = 0;
+ txInstruction* templ =
+ aEs.mStylesheet->findTemplate(aEs.getEvalContext()->getContextNode(),
+ mMode, &aEs, nullptr, &frame);
+
+ aEs.pushTemplateRule(frame, mMode, aEs.mTemplateParams);
+
+ return aEs.runTemplate(templ);
+}
+
+txAttribute::txAttribute(nsAutoPtr<Expr>&& aName, nsAutoPtr<Expr>&& aNamespace,
+ txNamespaceMap* aMappings)
+ : mName(Move(aName)), mNamespace(Move(aNamespace)), mMappings(aMappings)
+{
+}
+
+nsresult
+txAttribute::execute(txExecutionState& aEs)
+{
+ nsAutoPtr<txTextHandler> handler(
+ static_cast<txTextHandler*>(aEs.popResultHandler()));
+
+ nsAutoString name;
+ nsresult rv = mName->evaluateToString(aEs.getEvalContext(), name);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ const char16_t* colon;
+ if (!XMLUtils::isValidQName(name, &colon) ||
+ TX_StringEqualsAtom(name, nsGkAtoms::xmlns)) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIAtom> prefix;
+ uint32_t lnameStart = 0;
+ if (colon) {
+ prefix = NS_Atomize(Substring(name.get(), colon));
+ lnameStart = colon - name.get() + 1;
+ }
+
+ int32_t nsId = kNameSpaceID_None;
+ if (mNamespace) {
+ nsAutoString nspace;
+ rv = mNamespace->evaluateToString(aEs.getEvalContext(),
+ nspace);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!nspace.IsEmpty()) {
+ nsId = txNamespaceManager::getNamespaceID(nspace);
+ }
+ }
+ else if (colon) {
+ nsId = mMappings->lookupNamespace(prefix);
+ }
+
+ // add attribute if everything was ok
+ return nsId != kNameSpaceID_Unknown ?
+ aEs.mResultHandler->attribute(prefix, Substring(name, lnameStart),
+ nsId, handler->mValue) :
+ NS_OK;
+}
+
+txCallTemplate::txCallTemplate(const txExpandedName& aName)
+ : mName(aName)
+{
+}
+
+nsresult
+txCallTemplate::execute(txExecutionState& aEs)
+{
+ txInstruction* instr = aEs.mStylesheet->getNamedTemplate(mName);
+ NS_ENSURE_TRUE(instr, NS_ERROR_XSLT_EXECUTION_FAILURE);
+
+ nsresult rv = aEs.runTemplate(instr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+txCheckParam::txCheckParam(const txExpandedName& aName)
+ : mName(aName), mBailTarget(nullptr)
+{
+}
+
+nsresult
+txCheckParam::execute(txExecutionState& aEs)
+{
+ nsresult rv = NS_OK;
+ if (aEs.mTemplateParams) {
+ RefPtr<txAExprResult> exprRes;
+ aEs.mTemplateParams->getVariable(mName, getter_AddRefs(exprRes));
+ if (exprRes) {
+ rv = aEs.bindVariable(mName, exprRes);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aEs.gotoInstruction(mBailTarget);
+ }
+ }
+
+ return NS_OK;
+}
+
+txConditionalGoto::txConditionalGoto(nsAutoPtr<Expr>&& aCondition,
+ txInstruction* aTarget)
+ : mCondition(Move(aCondition)), mTarget(aTarget)
+{
+}
+
+nsresult
+txConditionalGoto::execute(txExecutionState& aEs)
+{
+ bool exprRes;
+ nsresult rv = mCondition->evaluateToBool(aEs.getEvalContext(), exprRes);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!exprRes) {
+ aEs.gotoInstruction(mTarget);
+ }
+
+ return NS_OK;
+}
+
+nsresult
+txComment::execute(txExecutionState& aEs)
+{
+ nsAutoPtr<txTextHandler> handler(
+ static_cast<txTextHandler*>(aEs.popResultHandler()));
+ uint32_t length = handler->mValue.Length();
+ int32_t pos = 0;
+ while ((pos = handler->mValue.FindChar('-', (uint32_t)pos)) != kNotFound) {
+ ++pos;
+ if ((uint32_t)pos == length || handler->mValue.CharAt(pos) == '-') {
+ handler->mValue.Insert(char16_t(' '), pos++);
+ ++length;
+ }
+ }
+
+ return aEs.mResultHandler->comment(handler->mValue);
+}
+
+nsresult
+txCopyBase::copyNode(const txXPathNode& aNode, txExecutionState& aEs)
+{
+ switch (txXPathNodeUtils::getNodeType(aNode)) {
+ case txXPathNodeType::ATTRIBUTE_NODE:
+ {
+ nsAutoString nodeValue;
+ txXPathNodeUtils::appendNodeValue(aNode, nodeValue);
+
+ nsCOMPtr<nsIAtom> localName =
+ txXPathNodeUtils::getLocalName(aNode);
+ return aEs.mResultHandler->
+ attribute(txXPathNodeUtils::getPrefix(aNode),
+ localName, nullptr,
+ txXPathNodeUtils::getNamespaceID(aNode),
+ nodeValue);
+ }
+ case txXPathNodeType::COMMENT_NODE:
+ {
+ nsAutoString nodeValue;
+ txXPathNodeUtils::appendNodeValue(aNode, nodeValue);
+ return aEs.mResultHandler->comment(nodeValue);
+ }
+ case txXPathNodeType::DOCUMENT_NODE:
+ case txXPathNodeType::DOCUMENT_FRAGMENT_NODE:
+ {
+ // Copy children
+ txXPathTreeWalker walker(aNode);
+ bool hasChild = walker.moveToFirstChild();
+ while (hasChild) {
+ copyNode(walker.getCurrentPosition(), aEs);
+ hasChild = walker.moveToNextSibling();
+ }
+ break;
+ }
+ case txXPathNodeType::ELEMENT_NODE:
+ {
+ nsCOMPtr<nsIAtom> localName =
+ txXPathNodeUtils::getLocalName(aNode);
+ nsresult rv = aEs.mResultHandler->
+ startElement(txXPathNodeUtils::getPrefix(aNode),
+ localName, nullptr,
+ txXPathNodeUtils::getNamespaceID(aNode));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Copy attributes
+ txXPathTreeWalker walker(aNode);
+ if (walker.moveToFirstAttribute()) {
+ do {
+ nsAutoString nodeValue;
+ walker.appendNodeValue(nodeValue);
+
+ const txXPathNode& attr = walker.getCurrentPosition();
+ localName = txXPathNodeUtils::getLocalName(attr);
+ rv = aEs.mResultHandler->
+ attribute(txXPathNodeUtils::getPrefix(attr),
+ localName, nullptr,
+ txXPathNodeUtils::getNamespaceID(attr),
+ nodeValue);
+ NS_ENSURE_SUCCESS(rv, rv);
+ } while (walker.moveToNextAttribute());
+ walker.moveToParent();
+ }
+
+ // Copy children
+ bool hasChild = walker.moveToFirstChild();
+ while (hasChild) {
+ copyNode(walker.getCurrentPosition(), aEs);
+ hasChild = walker.moveToNextSibling();
+ }
+
+ return aEs.mResultHandler->endElement();
+ }
+ case txXPathNodeType::PROCESSING_INSTRUCTION_NODE:
+ {
+ nsAutoString target, data;
+ txXPathNodeUtils::getNodeName(aNode, target);
+ txXPathNodeUtils::appendNodeValue(aNode, data);
+ return aEs.mResultHandler->processingInstruction(target, data);
+ }
+ case txXPathNodeType::TEXT_NODE:
+ case txXPathNodeType::CDATA_SECTION_NODE:
+ {
+ nsAutoString nodeValue;
+ txXPathNodeUtils::appendNodeValue(aNode, nodeValue);
+ return aEs.mResultHandler->characters(nodeValue, false);
+ }
+ }
+
+ return NS_OK;
+}
+
+txCopy::txCopy()
+ : mBailTarget(nullptr)
+{
+}
+
+nsresult
+txCopy::execute(txExecutionState& aEs)
+{
+ nsresult rv = NS_OK;
+ const txXPathNode& node = aEs.getEvalContext()->getContextNode();
+
+ switch (txXPathNodeUtils::getNodeType(node)) {
+ case txXPathNodeType::DOCUMENT_NODE:
+ case txXPathNodeType::DOCUMENT_FRAGMENT_NODE:
+ {
+ const nsAFlatString& empty = EmptyString();
+
+ // "close" current element to ensure that no attributes are added
+ rv = aEs.mResultHandler->characters(empty, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = aEs.pushBool(false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ break;
+ }
+ case txXPathNodeType::ELEMENT_NODE:
+ {
+ nsCOMPtr<nsIAtom> localName =
+ txXPathNodeUtils::getLocalName(node);
+ rv = aEs.mResultHandler->
+ startElement(txXPathNodeUtils::getPrefix(node),
+ localName, nullptr,
+ txXPathNodeUtils::getNamespaceID(node));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // XXX copy namespace nodes once we have them
+
+ rv = aEs.pushBool(true);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ break;
+ }
+ default:
+ {
+ rv = copyNode(node, aEs);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aEs.gotoInstruction(mBailTarget);
+ }
+ }
+
+ return NS_OK;
+}
+
+txCopyOf::txCopyOf(nsAutoPtr<Expr>&& aSelect)
+ : mSelect(Move(aSelect))
+{
+}
+
+nsresult
+txCopyOf::execute(txExecutionState& aEs)
+{
+ RefPtr<txAExprResult> exprRes;
+ nsresult rv = mSelect->evaluate(aEs.getEvalContext(),
+ getter_AddRefs(exprRes));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ switch (exprRes->getResultType()) {
+ case txAExprResult::NODESET:
+ {
+ txNodeSet* nodes = static_cast<txNodeSet*>
+ (static_cast<txAExprResult*>
+ (exprRes));
+ int32_t i;
+ for (i = 0; i < nodes->size(); ++i) {
+ rv = copyNode(nodes->get(i), aEs);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ break;
+ }
+ case txAExprResult::RESULT_TREE_FRAGMENT:
+ {
+ txResultTreeFragment* rtf =
+ static_cast<txResultTreeFragment*>
+ (static_cast<txAExprResult*>(exprRes));
+ return rtf->flushToHandler(aEs.mResultHandler);
+ }
+ default:
+ {
+ nsAutoString value;
+ exprRes->stringValue(value);
+ if (!value.IsEmpty()) {
+ return aEs.mResultHandler->characters(value, false);
+ }
+ break;
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult
+txEndElement::execute(txExecutionState& aEs)
+{
+ // This will return false if startElement was not called. This happens
+ // when <xsl:element> produces a bad name, or when <xsl:copy> copies a
+ // document node.
+ if (aEs.popBool()) {
+ return aEs.mResultHandler->endElement();
+ }
+
+ return NS_OK;
+}
+
+nsresult
+txErrorInstruction::execute(txExecutionState& aEs)
+{
+ // XXX ErrorReport: unknown instruction executed
+ return NS_ERROR_XSLT_EXECUTION_FAILURE;
+}
+
+txGoTo::txGoTo(txInstruction* aTarget)
+ : mTarget(aTarget)
+{
+}
+
+nsresult
+txGoTo::execute(txExecutionState& aEs)
+{
+ aEs.gotoInstruction(mTarget);
+
+ return NS_OK;
+}
+
+txInsertAttrSet::txInsertAttrSet(const txExpandedName& aName)
+ : mName(aName)
+{
+}
+
+nsresult
+txInsertAttrSet::execute(txExecutionState& aEs)
+{
+ txInstruction* instr = aEs.mStylesheet->getAttributeSet(mName);
+ NS_ENSURE_TRUE(instr, NS_ERROR_XSLT_EXECUTION_FAILURE);
+
+ nsresult rv = aEs.runTemplate(instr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+txLoopNodeSet::txLoopNodeSet(txInstruction* aTarget)
+ : mTarget(aTarget)
+{
+}
+
+nsresult
+txLoopNodeSet::execute(txExecutionState& aEs)
+{
+ aEs.popTemplateRule();
+ txNodeSetContext* context =
+ static_cast<txNodeSetContext*>(aEs.getEvalContext());
+ if (!context->hasNext()) {
+ aEs.popAndDeleteEvalContext();
+
+ return NS_OK;
+ }
+
+ context->next();
+ aEs.gotoInstruction(mTarget);
+
+ return NS_OK;
+}
+
+txLREAttribute::txLREAttribute(int32_t aNamespaceID, nsIAtom* aLocalName,
+ nsIAtom* aPrefix, nsAutoPtr<Expr>&& aValue)
+ : mNamespaceID(aNamespaceID),
+ mLocalName(aLocalName),
+ mPrefix(aPrefix),
+ mValue(Move(aValue))
+{
+ if (aNamespaceID == kNameSpaceID_None) {
+ mLowercaseLocalName = TX_ToLowerCaseAtom(aLocalName);
+ }
+}
+
+nsresult
+txLREAttribute::execute(txExecutionState& aEs)
+{
+ RefPtr<txAExprResult> exprRes;
+ nsresult rv = mValue->evaluate(aEs.getEvalContext(),
+ getter_AddRefs(exprRes));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ const nsString* value = exprRes->stringValuePointer();
+ if (value) {
+ return aEs.mResultHandler->attribute(mPrefix, mLocalName,
+ mLowercaseLocalName,
+ mNamespaceID, *value);
+ }
+
+ nsAutoString valueStr;
+ exprRes->stringValue(valueStr);
+ return aEs.mResultHandler->attribute(mPrefix, mLocalName,
+ mLowercaseLocalName,
+ mNamespaceID, valueStr);
+}
+
+txMessage::txMessage(bool aTerminate)
+ : mTerminate(aTerminate)
+{
+}
+
+nsresult
+txMessage::execute(txExecutionState& aEs)
+{
+ nsAutoPtr<txTextHandler> handler(
+ static_cast<txTextHandler*>(aEs.popResultHandler()));
+
+ nsCOMPtr<nsIConsoleService> consoleSvc =
+ do_GetService("@mozilla.org/consoleservice;1");
+ if (consoleSvc) {
+ nsAutoString logString(NS_LITERAL_STRING("xsl:message - "));
+ logString.Append(handler->mValue);
+ consoleSvc->LogStringMessage(logString.get());
+ }
+
+ return mTerminate ? NS_ERROR_XSLT_ABORTED : NS_OK;
+}
+
+txNumber::txNumber(txXSLTNumber::LevelType aLevel,
+ nsAutoPtr<txPattern>&& aCount, nsAutoPtr<txPattern>&& aFrom,
+ nsAutoPtr<Expr>&& aValue, nsAutoPtr<Expr>&& aFormat,
+ nsAutoPtr<Expr>&& aGroupingSeparator,
+ nsAutoPtr<Expr>&& aGroupingSize)
+ : mLevel(aLevel), mCount(Move(aCount)),
+ mFrom(Move(aFrom)),
+ mValue(Move(aValue)),
+ mFormat(Move(aFormat)),
+ mGroupingSeparator(Move(aGroupingSeparator)),
+ mGroupingSize(Move(aGroupingSize))
+{
+}
+
+nsresult
+txNumber::execute(txExecutionState& aEs)
+{
+ nsAutoString res;
+ nsresult rv =
+ txXSLTNumber::createNumber(mValue, mCount, mFrom, mLevel, mGroupingSize,
+ mGroupingSeparator, mFormat,
+ aEs.getEvalContext(), res);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return aEs.mResultHandler->characters(res, false);
+}
+
+nsresult
+txPopParams::execute(txExecutionState& aEs)
+{
+ delete aEs.popParamMap();
+
+ return NS_OK;
+}
+
+txProcessingInstruction::txProcessingInstruction(nsAutoPtr<Expr>&& aName)
+ : mName(Move(aName))
+{
+}
+
+nsresult
+txProcessingInstruction::execute(txExecutionState& aEs)
+{
+ nsAutoPtr<txTextHandler> handler(
+ static_cast<txTextHandler*>(aEs.popResultHandler()));
+ XMLUtils::normalizePIValue(handler->mValue);
+
+ nsAutoString name;
+ nsresult rv = mName->evaluateToString(aEs.getEvalContext(), name);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Check name validity (must be valid NCName and a PITarget)
+ // XXX Need to check for NCName and PITarget
+ const char16_t* colon;
+ if (!XMLUtils::isValidQName(name, &colon)) {
+ // XXX ErrorReport: bad PI-target
+ return NS_ERROR_FAILURE;
+ }
+
+ return aEs.mResultHandler->processingInstruction(name, handler->mValue);
+}
+
+txPushNewContext::txPushNewContext(nsAutoPtr<Expr>&& aSelect)
+ : mSelect(Move(aSelect)), mBailTarget(nullptr)
+{
+}
+
+txPushNewContext::~txPushNewContext()
+{
+}
+
+nsresult
+txPushNewContext::execute(txExecutionState& aEs)
+{
+ RefPtr<txAExprResult> exprRes;
+ nsresult rv = mSelect->evaluate(aEs.getEvalContext(),
+ getter_AddRefs(exprRes));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (exprRes->getResultType() != txAExprResult::NODESET) {
+ // XXX ErrorReport: nodeset expected
+ return NS_ERROR_XSLT_NODESET_EXPECTED;
+ }
+
+ txNodeSet* nodes = static_cast<txNodeSet*>
+ (static_cast<txAExprResult*>
+ (exprRes));
+
+ if (nodes->isEmpty()) {
+ aEs.gotoInstruction(mBailTarget);
+
+ return NS_OK;
+ }
+
+ txNodeSorter sorter;
+ uint32_t i, count = mSortKeys.Length();
+ for (i = 0; i < count; ++i) {
+ SortKey& sort = mSortKeys[i];
+ rv = sorter.addSortElement(sort.mSelectExpr, sort.mLangExpr,
+ sort.mDataTypeExpr, sort.mOrderExpr,
+ sort.mCaseOrderExpr,
+ aEs.getEvalContext());
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ RefPtr<txNodeSet> sortedNodes;
+ rv = sorter.sortNodeSet(nodes, &aEs, getter_AddRefs(sortedNodes));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ txNodeSetContext* context = new txNodeSetContext(sortedNodes, &aEs);
+ NS_ENSURE_TRUE(context, NS_ERROR_OUT_OF_MEMORY);
+
+ context->next();
+
+ rv = aEs.pushEvalContext(context);
+ if (NS_FAILED(rv)) {
+ delete context;
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+txPushNewContext::addSort(nsAutoPtr<Expr>&& aSelectExpr,
+ nsAutoPtr<Expr>&& aLangExpr,
+ nsAutoPtr<Expr>&& aDataTypeExpr,
+ nsAutoPtr<Expr>&& aOrderExpr,
+ nsAutoPtr<Expr>&& aCaseOrderExpr)
+{
+ if (SortKey *key = mSortKeys.AppendElement()) {
+ // workaround for not triggering the Copy Constructor
+ key->mSelectExpr = Move(aSelectExpr);
+ key->mLangExpr = Move(aLangExpr);
+ key->mDataTypeExpr = Move(aDataTypeExpr);
+ key->mOrderExpr = Move(aOrderExpr);
+ key->mCaseOrderExpr = Move(aCaseOrderExpr);
+ return NS_OK;
+ }
+ return NS_ERROR_OUT_OF_MEMORY;
+}
+
+nsresult
+txPushNullTemplateRule::execute(txExecutionState& aEs)
+{
+ aEs.pushTemplateRule(nullptr, txExpandedName(), nullptr);
+ return NS_OK;
+}
+
+nsresult
+txPushParams::execute(txExecutionState& aEs)
+{
+ return aEs.pushParamMap(nullptr);
+}
+
+nsresult
+txPushRTFHandler::execute(txExecutionState& aEs)
+{
+ txAXMLEventHandler* handler = new txRtfHandler;
+ nsresult rv = aEs.pushResultHandler(handler);
+ if (NS_FAILED(rv)) {
+ delete handler;
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+txPushStringHandler::txPushStringHandler(bool aOnlyText)
+ : mOnlyText(aOnlyText)
+{
+}
+
+nsresult
+txPushStringHandler::execute(txExecutionState& aEs)
+{
+ txAXMLEventHandler* handler = new txTextHandler(mOnlyText);
+ nsresult rv = aEs.pushResultHandler(handler);
+ if (NS_FAILED(rv)) {
+ delete handler;
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+txRemoveVariable::txRemoveVariable(const txExpandedName& aName)
+ : mName(aName)
+{
+}
+
+nsresult
+txRemoveVariable::execute(txExecutionState& aEs)
+{
+ aEs.removeVariable(mName);
+
+ return NS_OK;
+}
+
+nsresult
+txReturn::execute(txExecutionState& aEs)
+{
+ NS_ASSERTION(!mNext, "instructions exist after txReturn");
+ aEs.returnFromTemplate();
+
+ return NS_OK;
+}
+
+txSetParam::txSetParam(const txExpandedName& aName, nsAutoPtr<Expr>&& aValue)
+ : mName(aName), mValue(Move(aValue))
+{
+}
+
+nsresult
+txSetParam::execute(txExecutionState& aEs)
+{
+ nsresult rv = NS_OK;
+ if (!aEs.mTemplateParams) {
+ aEs.mTemplateParams = new txVariableMap;
+ NS_ENSURE_TRUE(aEs.mTemplateParams, NS_ERROR_OUT_OF_MEMORY);
+ }
+
+ RefPtr<txAExprResult> exprRes;
+ if (mValue) {
+ rv = mValue->evaluate(aEs.getEvalContext(),
+ getter_AddRefs(exprRes));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ else {
+ nsAutoPtr<txRtfHandler> rtfHandler(
+ static_cast<txRtfHandler*>(aEs.popResultHandler()));
+ rv = rtfHandler->getAsRTF(getter_AddRefs(exprRes));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ rv = aEs.mTemplateParams->bindVariable(mName, exprRes);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+txSetVariable::txSetVariable(const txExpandedName& aName,
+ nsAutoPtr<Expr>&& aValue)
+ : mName(aName), mValue(Move(aValue))
+{
+}
+
+nsresult
+txSetVariable::execute(txExecutionState& aEs)
+{
+ nsresult rv = NS_OK;
+ RefPtr<txAExprResult> exprRes;
+ if (mValue) {
+ rv = mValue->evaluate(aEs.getEvalContext(), getter_AddRefs(exprRes));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ else {
+ nsAutoPtr<txRtfHandler> rtfHandler(
+ static_cast<txRtfHandler*>(aEs.popResultHandler()));
+ rv = rtfHandler->getAsRTF(getter_AddRefs(exprRes));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return aEs.bindVariable(mName, exprRes);
+}
+
+txStartElement::txStartElement(nsAutoPtr<Expr>&& aName,
+ nsAutoPtr<Expr>&& aNamespace,
+ txNamespaceMap* aMappings)
+ : mName(Move(aName)),
+ mNamespace(Move(aNamespace)),
+ mMappings(aMappings)
+{
+}
+
+nsresult
+txStartElement::execute(txExecutionState& aEs)
+{
+ nsAutoString name;
+ nsresult rv = mName->evaluateToString(aEs.getEvalContext(), name);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+
+ int32_t nsId = kNameSpaceID_None;
+ nsCOMPtr<nsIAtom> prefix;
+ uint32_t lnameStart = 0;
+
+ const char16_t* colon;
+ if (XMLUtils::isValidQName(name, &colon)) {
+ if (colon) {
+ prefix = NS_Atomize(Substring(name.get(), colon));
+ lnameStart = colon - name.get() + 1;
+ }
+
+ if (mNamespace) {
+ nsAutoString nspace;
+ rv = mNamespace->evaluateToString(aEs.getEvalContext(),
+ nspace);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!nspace.IsEmpty()) {
+ nsId = txNamespaceManager::getNamespaceID(nspace);
+ }
+ }
+ else {
+ nsId = mMappings->lookupNamespace(prefix);
+ }
+ }
+ else {
+ nsId = kNameSpaceID_Unknown;
+ }
+
+ bool success = true;
+
+ if (nsId != kNameSpaceID_Unknown) {
+ rv = aEs.mResultHandler->startElement(prefix,
+ Substring(name, lnameStart),
+ nsId);
+ }
+ else {
+ rv = NS_ERROR_XSLT_BAD_NODE_NAME;
+ }
+
+ if (rv == NS_ERROR_XSLT_BAD_NODE_NAME) {
+ success = false;
+ // we call characters with an empty string to "close" any element to
+ // make sure that no attributes are added
+ rv = aEs.mResultHandler->characters(EmptyString(), false);
+ }
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = aEs.pushBool(success);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+
+txStartLREElement::txStartLREElement(int32_t aNamespaceID,
+ nsIAtom* aLocalName,
+ nsIAtom* aPrefix)
+ : mNamespaceID(aNamespaceID),
+ mLocalName(aLocalName),
+ mPrefix(aPrefix)
+{
+ if (aNamespaceID == kNameSpaceID_None) {
+ mLowercaseLocalName = TX_ToLowerCaseAtom(aLocalName);
+ }
+}
+
+nsresult
+txStartLREElement::execute(txExecutionState& aEs)
+{
+ nsresult rv = aEs.mResultHandler->startElement(mPrefix, mLocalName,
+ mLowercaseLocalName,
+ mNamespaceID);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = aEs.pushBool(true);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+txText::txText(const nsAString& aStr, bool aDOE)
+ : mStr(aStr),
+ mDOE(aDOE)
+{
+}
+
+nsresult
+txText::execute(txExecutionState& aEs)
+{
+ return aEs.mResultHandler->characters(mStr, mDOE);
+}
+
+txValueOf::txValueOf(nsAutoPtr<Expr>&& aExpr, bool aDOE)
+ : mExpr(Move(aExpr)),
+ mDOE(aDOE)
+{
+}
+
+nsresult
+txValueOf::execute(txExecutionState& aEs)
+{
+ RefPtr<txAExprResult> exprRes;
+ nsresult rv = mExpr->evaluate(aEs.getEvalContext(),
+ getter_AddRefs(exprRes));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ const nsString* value = exprRes->stringValuePointer();
+ if (value) {
+ if (!value->IsEmpty()) {
+ return aEs.mResultHandler->characters(*value, mDOE);
+ }
+ }
+ else {
+ nsAutoString valueStr;
+ exprRes->stringValue(valueStr);
+ if (!valueStr.IsEmpty()) {
+ return aEs.mResultHandler->characters(valueStr, mDOE);
+ }
+ }
+
+ return NS_OK;
+}
diff --git a/dom/xslt/xslt/txInstructions.h b/dom/xslt/xslt/txInstructions.h
new file mode 100644
index 000000000..d363400e8
--- /dev/null
+++ b/dom/xslt/xslt/txInstructions.h
@@ -0,0 +1,388 @@
+/* -*- 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_TXINSTRUCTIONS_H
+#define TRANSFRMX_TXINSTRUCTIONS_H
+
+#include "nsCOMPtr.h"
+#include "txCore.h"
+#include "nsString.h"
+#include "txXMLUtils.h"
+#include "txExpandedName.h"
+#include "txNamespaceMap.h"
+#include "nsAutoPtr.h"
+#include "txXSLTNumber.h"
+#include "nsTArray.h"
+
+class nsIAtom;
+class txExecutionState;
+
+class txInstruction : public txObject
+{
+public:
+ txInstruction()
+ {
+ MOZ_COUNT_CTOR(txInstruction);
+ }
+
+ virtual ~txInstruction()
+ {
+ MOZ_COUNT_DTOR(txInstruction);
+ }
+
+ virtual nsresult execute(txExecutionState& aEs) = 0;
+
+ nsAutoPtr<txInstruction> mNext;
+};
+
+#define TX_DECL_TXINSTRUCTION \
+ virtual nsresult execute(txExecutionState& aEs);
+
+
+class txApplyDefaultElementTemplate : public txInstruction
+{
+public:
+ TX_DECL_TXINSTRUCTION
+};
+
+class txApplyImports : public txInstruction
+{
+public:
+ TX_DECL_TXINSTRUCTION
+};
+
+class txApplyTemplates : public txInstruction
+{
+public:
+ explicit txApplyTemplates(const txExpandedName& aMode);
+
+ TX_DECL_TXINSTRUCTION
+
+ txExpandedName mMode;
+};
+
+class txAttribute : public txInstruction
+{
+public:
+ txAttribute(nsAutoPtr<Expr>&& aName, nsAutoPtr<Expr>&& aNamespace,
+ txNamespaceMap* aMappings);
+
+ TX_DECL_TXINSTRUCTION
+
+ nsAutoPtr<Expr> mName;
+ nsAutoPtr<Expr> mNamespace;
+ RefPtr<txNamespaceMap> mMappings;
+};
+
+class txCallTemplate : public txInstruction
+{
+public:
+ explicit txCallTemplate(const txExpandedName& aName);
+
+ TX_DECL_TXINSTRUCTION
+
+ txExpandedName mName;
+};
+
+class txCheckParam : public txInstruction
+{
+public:
+ explicit txCheckParam(const txExpandedName& aName);
+
+ TX_DECL_TXINSTRUCTION
+
+ txExpandedName mName;
+ txInstruction* mBailTarget;
+};
+
+class txConditionalGoto : public txInstruction
+{
+public:
+ txConditionalGoto(nsAutoPtr<Expr>&& aCondition, txInstruction* aTarget);
+
+ TX_DECL_TXINSTRUCTION
+
+ nsAutoPtr<Expr> mCondition;
+ txInstruction* mTarget;
+};
+
+class txComment : public txInstruction
+{
+public:
+ TX_DECL_TXINSTRUCTION
+};
+
+class txCopyBase : public txInstruction
+{
+protected:
+ nsresult copyNode(const txXPathNode& aNode, txExecutionState& aEs);
+};
+
+class txCopy : public txCopyBase
+{
+public:
+ txCopy();
+
+ TX_DECL_TXINSTRUCTION
+
+ txInstruction* mBailTarget;
+};
+
+class txCopyOf : public txCopyBase
+{
+public:
+ explicit txCopyOf(nsAutoPtr<Expr>&& aSelect);
+
+ TX_DECL_TXINSTRUCTION
+
+ nsAutoPtr<Expr> mSelect;
+};
+
+class txEndElement : public txInstruction
+{
+public:
+ TX_DECL_TXINSTRUCTION
+};
+
+class txErrorInstruction : public txInstruction
+{
+public:
+ TX_DECL_TXINSTRUCTION
+};
+
+class txGoTo : public txInstruction
+{
+public:
+ explicit txGoTo(txInstruction* aTarget);
+
+ TX_DECL_TXINSTRUCTION
+
+ txInstruction* mTarget;
+};
+
+class txInsertAttrSet : public txInstruction
+{
+public:
+ explicit txInsertAttrSet(const txExpandedName& aName);
+
+ TX_DECL_TXINSTRUCTION
+
+ txExpandedName mName;
+};
+
+class txLoopNodeSet : public txInstruction
+{
+public:
+ explicit txLoopNodeSet(txInstruction* aTarget);
+
+ TX_DECL_TXINSTRUCTION
+
+ txInstruction* mTarget;
+};
+
+class txLREAttribute : public txInstruction
+{
+public:
+ txLREAttribute(int32_t aNamespaceID, nsIAtom* aLocalName,
+ nsIAtom* aPrefix, nsAutoPtr<Expr>&& aValue);
+
+ TX_DECL_TXINSTRUCTION
+
+ int32_t mNamespaceID;
+ nsCOMPtr<nsIAtom> mLocalName;
+ nsCOMPtr<nsIAtom> mLowercaseLocalName;
+ nsCOMPtr<nsIAtom> mPrefix;
+ nsAutoPtr<Expr> mValue;
+};
+
+class txMessage : public txInstruction
+{
+public:
+ explicit txMessage(bool aTerminate);
+
+ TX_DECL_TXINSTRUCTION
+
+ bool mTerminate;
+};
+
+class txNumber : public txInstruction
+{
+public:
+ txNumber(txXSLTNumber::LevelType aLevel, nsAutoPtr<txPattern>&& aCount,
+ nsAutoPtr<txPattern>&& aFrom, nsAutoPtr<Expr>&& aValue,
+ nsAutoPtr<Expr>&& aFormat, nsAutoPtr<Expr>&& aGroupingSeparator,
+ nsAutoPtr<Expr>&& aGroupingSize);
+
+ TX_DECL_TXINSTRUCTION
+
+ txXSLTNumber::LevelType mLevel;
+ nsAutoPtr<txPattern> mCount;
+ nsAutoPtr<txPattern> mFrom;
+ nsAutoPtr<Expr> mValue;
+ nsAutoPtr<Expr> mFormat;
+ nsAutoPtr<Expr> mGroupingSeparator;
+ nsAutoPtr<Expr> mGroupingSize;
+};
+
+class txPopParams : public txInstruction
+{
+public:
+ TX_DECL_TXINSTRUCTION
+};
+
+class txProcessingInstruction : public txInstruction
+{
+public:
+ explicit txProcessingInstruction(nsAutoPtr<Expr>&& aName);
+
+ TX_DECL_TXINSTRUCTION
+
+ nsAutoPtr<Expr> mName;
+};
+
+class txPushNewContext : public txInstruction
+{
+public:
+ explicit txPushNewContext(nsAutoPtr<Expr>&& aSelect);
+ ~txPushNewContext();
+
+ TX_DECL_TXINSTRUCTION
+
+
+ nsresult addSort(nsAutoPtr<Expr>&& aSelectExpr,
+ nsAutoPtr<Expr>&& aLangExpr,
+ nsAutoPtr<Expr>&& aDataTypeExpr,
+ nsAutoPtr<Expr>&& aOrderExpr,
+ nsAutoPtr<Expr>&& aCaseOrderExpr);
+
+ struct SortKey {
+ nsAutoPtr<Expr> mSelectExpr;
+ nsAutoPtr<Expr> mLangExpr;
+ nsAutoPtr<Expr> mDataTypeExpr;
+ nsAutoPtr<Expr> mOrderExpr;
+ nsAutoPtr<Expr> mCaseOrderExpr;
+ };
+
+ nsTArray<SortKey> mSortKeys;
+ nsAutoPtr<Expr> mSelect;
+ txInstruction* mBailTarget;
+};
+
+class txPushNullTemplateRule : public txInstruction
+{
+public:
+ TX_DECL_TXINSTRUCTION
+};
+
+class txPushParams : public txInstruction
+{
+public:
+ TX_DECL_TXINSTRUCTION
+};
+
+class txPushRTFHandler : public txInstruction
+{
+public:
+ TX_DECL_TXINSTRUCTION
+};
+
+class txPushStringHandler : public txInstruction
+{
+public:
+ explicit txPushStringHandler(bool aOnlyText);
+
+ TX_DECL_TXINSTRUCTION
+
+ bool mOnlyText;
+};
+
+class txRemoveVariable : public txInstruction
+{
+public:
+ explicit txRemoveVariable(const txExpandedName& aName);
+
+ TX_DECL_TXINSTRUCTION
+
+ txExpandedName mName;
+};
+
+class txReturn : public txInstruction
+{
+public:
+ TX_DECL_TXINSTRUCTION
+};
+
+class txSetParam : public txInstruction
+{
+public:
+ txSetParam(const txExpandedName& aName, nsAutoPtr<Expr>&& aValue);
+
+ TX_DECL_TXINSTRUCTION
+
+ txExpandedName mName;
+ nsAutoPtr<Expr> mValue;
+};
+
+class txSetVariable : public txInstruction
+{
+public:
+ txSetVariable(const txExpandedName& aName, nsAutoPtr<Expr>&& aValue);
+
+ TX_DECL_TXINSTRUCTION
+
+ txExpandedName mName;
+ nsAutoPtr<Expr> mValue;
+};
+
+class txStartElement : public txInstruction
+{
+public:
+ txStartElement(nsAutoPtr<Expr>&& aName, nsAutoPtr<Expr>&& aNamespace,
+ txNamespaceMap* aMappings);
+
+ TX_DECL_TXINSTRUCTION
+
+ nsAutoPtr<Expr> mName;
+ nsAutoPtr<Expr> mNamespace;
+ RefPtr<txNamespaceMap> mMappings;
+};
+
+class txStartLREElement : public txInstruction
+{
+public:
+ txStartLREElement(int32_t aNamespaceID, nsIAtom* aLocalName,
+ nsIAtom* aPrefix);
+
+ TX_DECL_TXINSTRUCTION
+
+ int32_t mNamespaceID;
+ nsCOMPtr<nsIAtom> mLocalName;
+ nsCOMPtr<nsIAtom> mLowercaseLocalName;
+ nsCOMPtr<nsIAtom> mPrefix;
+};
+
+class txText : public txInstruction
+{
+public:
+ txText(const nsAString& aStr, bool aDOE);
+
+ TX_DECL_TXINSTRUCTION
+
+ nsString mStr;
+ bool mDOE;
+};
+
+class txValueOf : public txInstruction
+{
+public:
+ txValueOf(nsAutoPtr<Expr>&& aExpr, bool aDOE);
+
+ TX_DECL_TXINSTRUCTION
+
+ nsAutoPtr<Expr> mExpr;
+ bool mDOE;
+};
+
+#endif //TRANSFRMX_TXINSTRUCTIONS_H
diff --git a/dom/xslt/xslt/txKey.h b/dom/xslt/xslt/txKey.h
new file mode 100644
index 000000000..90a15f2f0
--- /dev/null
+++ b/dom/xslt/xslt/txKey.h
@@ -0,0 +1,212 @@
+/* -*- 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 txKey_h__
+#define txKey_h__
+
+#include "nsTHashtable.h"
+#include "txNodeSet.h"
+#include "txList.h"
+#include "txXSLTPatterns.h"
+#include "txXMLUtils.h"
+
+class txPattern;
+class Expr;
+class txExecutionState;
+
+class txKeyValueHashKey
+{
+public:
+ txKeyValueHashKey(const txExpandedName& aKeyName,
+ int32_t aRootIdentifier,
+ const nsAString& aKeyValue)
+ : mKeyName(aKeyName),
+ mKeyValue(aKeyValue),
+ mRootIdentifier(aRootIdentifier)
+ {
+ }
+
+ txExpandedName mKeyName;
+ nsString mKeyValue;
+ int32_t mRootIdentifier;
+};
+
+struct txKeyValueHashEntry : public PLDHashEntryHdr
+{
+public:
+ typedef const txKeyValueHashKey& KeyType;
+ typedef const txKeyValueHashKey* KeyTypePointer;
+
+ explicit txKeyValueHashEntry(KeyTypePointer aKey)
+ : mKey(*aKey),
+ mNodeSet(new txNodeSet(nullptr)) { }
+
+ txKeyValueHashEntry(const txKeyValueHashEntry& entry)
+ : mKey(entry.mKey),
+ mNodeSet(entry.mNodeSet) { }
+
+ bool KeyEquals(KeyTypePointer aKey) const;
+
+ static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
+
+ static PLDHashNumber HashKey(KeyTypePointer aKey);
+
+ enum { ALLOW_MEMMOVE = true };
+
+ txKeyValueHashKey mKey;
+ RefPtr<txNodeSet> mNodeSet;
+};
+
+typedef nsTHashtable<txKeyValueHashEntry> txKeyValueHash;
+
+class txIndexedKeyHashKey
+{
+public:
+ txIndexedKeyHashKey(txExpandedName aKeyName,
+ int32_t aRootIdentifier)
+ : mKeyName(aKeyName),
+ mRootIdentifier(aRootIdentifier)
+ {
+ }
+
+ txExpandedName mKeyName;
+ int32_t mRootIdentifier;
+};
+
+struct txIndexedKeyHashEntry : public PLDHashEntryHdr
+{
+public:
+ typedef const txIndexedKeyHashKey& KeyType;
+ typedef const txIndexedKeyHashKey* KeyTypePointer;
+
+ explicit txIndexedKeyHashEntry(KeyTypePointer aKey)
+ : mKey(*aKey),
+ mIndexed(false) { }
+
+ txIndexedKeyHashEntry(const txIndexedKeyHashEntry& entry)
+ : mKey(entry.mKey),
+ mIndexed(entry.mIndexed) { }
+
+ bool KeyEquals(KeyTypePointer aKey) const;
+
+ static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
+
+ static PLDHashNumber HashKey(KeyTypePointer aKey);
+
+ enum { ALLOW_MEMMOVE = true };
+
+ txIndexedKeyHashKey mKey;
+ bool mIndexed;
+};
+
+typedef nsTHashtable<txIndexedKeyHashEntry> txIndexedKeyHash;
+
+/**
+ * Class holding all <xsl:key>s of a particular expanded name in the
+ * stylesheet.
+ */
+class txXSLKey {
+
+public:
+ explicit txXSLKey(const txExpandedName& aName) : mName(aName)
+ {
+ }
+
+ /**
+ * Adds a match/use pair.
+ * @param aMatch match-pattern
+ * @param aUse use-expression
+ * @return false if an error occurred, true otherwise
+ */
+ bool addKey(nsAutoPtr<txPattern>&& aMatch, nsAutoPtr<Expr>&& aUse);
+
+ /**
+ * Indexes a subtree and adds it to the hash of key values
+ * @param aRoot Subtree root to index and add
+ * @param aKeyValueHash Hash to add values to
+ * @param aEs txExecutionState to use for XPath evaluation
+ */
+ nsresult indexSubtreeRoot(const txXPathNode& aRoot,
+ txKeyValueHash& aKeyValueHash,
+ txExecutionState& aEs);
+
+private:
+ /**
+ * Recursively searches a node, its attributes and its subtree for
+ * nodes matching any of the keys match-patterns.
+ * @param aNode Node to search
+ * @param aKey Key to use when adding into the hash
+ * @param aKeyValueHash Hash to add values to
+ * @param aEs txExecutionState to use for XPath evaluation
+ */
+ nsresult indexTree(const txXPathNode& aNode, txKeyValueHashKey& aKey,
+ txKeyValueHash& aKeyValueHash, txExecutionState& aEs);
+
+ /**
+ * Tests one node if it matches any of the keys match-patterns. If
+ * the node matches its values are added to the index.
+ * @param aNode Node to test
+ * @param aKey Key to use when adding into the hash
+ * @param aKeyValueHash Hash to add values to
+ * @param aEs txExecutionState to use for XPath evaluation
+ */
+ nsresult testNode(const txXPathNode& aNode, txKeyValueHashKey& aKey,
+ txKeyValueHash& aKeyValueHash, txExecutionState& aEs);
+
+ /**
+ * represents one match/use pair
+ */
+ struct Key {
+ nsAutoPtr<txPattern> matchPattern;
+ nsAutoPtr<Expr> useExpr;
+ };
+
+ /**
+ * List of all match/use pairs. The items as |Key|s
+ */
+ nsTArray<Key> mKeys;
+
+ /**
+ * Name of this key
+ */
+ txExpandedName mName;
+};
+
+
+class txKeyHash
+{
+public:
+ explicit txKeyHash(const txOwningExpandedNameMap<txXSLKey>& aKeys)
+ : mKeyValues(4)
+ , mIndexedKeys(1)
+ , mKeys(aKeys)
+ {
+ }
+
+ nsresult init();
+
+ nsresult getKeyNodes(const txExpandedName& aKeyName,
+ const txXPathNode& aRoot,
+ const nsAString& aKeyValue,
+ bool aIndexIfNotFound,
+ txExecutionState& aEs,
+ txNodeSet** aResult);
+
+private:
+ // Hash of all indexed key-values
+ txKeyValueHash mKeyValues;
+
+ // Hash showing which keys+roots has been indexed
+ txIndexedKeyHash mIndexedKeys;
+
+ // Map of txXSLKeys
+ const txOwningExpandedNameMap<txXSLKey>& mKeys;
+
+ // Empty nodeset returned if no key is found
+ RefPtr<txNodeSet> mEmptyNodeSet;
+};
+
+
+#endif //txKey_h__
diff --git a/dom/xslt/xslt/txKeyFunctionCall.cpp b/dom/xslt/xslt/txKeyFunctionCall.cpp
new file mode 100644
index 000000000..ae76275ff
--- /dev/null
+++ b/dom/xslt/xslt/txKeyFunctionCall.cpp
@@ -0,0 +1,395 @@
+/* -*- 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 "txExecutionState.h"
+#include "nsGkAtoms.h"
+#include "txSingleNodeContext.h"
+#include "txXSLTFunctions.h"
+#include "nsReadableUtils.h"
+#include "txKey.h"
+#include "txXSLTPatterns.h"
+#include "txNamespaceMap.h"
+#include "mozilla/HashFunctions.h"
+#include "mozilla/Move.h"
+
+using namespace mozilla;
+
+/*
+ * txKeyFunctionCall
+ * A representation of the XSLT additional function: key()
+ */
+
+/*
+ * Creates a new key function call
+ */
+txKeyFunctionCall::txKeyFunctionCall(txNamespaceMap* aMappings)
+ : mMappings(aMappings)
+{
+}
+
+/*
+ * Evaluates a key() xslt-function call. First argument is name of key
+ * to use, second argument is value to look up.
+ * @param aContext the context node for evaluation of this Expr
+ * @param aCs the ContextState containing the stack information needed
+ * for evaluation
+ * @return the result of the evaluation
+ */
+nsresult
+txKeyFunctionCall::evaluate(txIEvalContext* aContext, txAExprResult** aResult)
+{
+ if (!aContext || !requireParams(2, 2, aContext))
+ return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT;
+
+ txExecutionState* es =
+ static_cast<txExecutionState*>(aContext->getPrivateContext());
+
+ nsAutoString keyQName;
+ nsresult rv = mParams[0]->evaluateToString(aContext, keyQName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ txExpandedName keyName;
+ rv = keyName.init(keyQName, mMappings, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ RefPtr<txAExprResult> exprResult;
+ rv = mParams[1]->evaluate(aContext, getter_AddRefs(exprResult));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ txXPathTreeWalker walker(aContext->getContextNode());
+ walker.moveToRoot();
+
+ RefPtr<txNodeSet> res;
+ txNodeSet* nodeSet;
+ if (exprResult->getResultType() == txAExprResult::NODESET &&
+ (nodeSet = static_cast<txNodeSet*>
+ (static_cast<txAExprResult*>
+ (exprResult)))->size() > 1) {
+ rv = aContext->recycler()->getNodeSet(getter_AddRefs(res));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ int32_t i;
+ for (i = 0; i < nodeSet->size(); ++i) {
+ nsAutoString val;
+ txXPathNodeUtils::appendNodeValue(nodeSet->get(i), val);
+
+ RefPtr<txNodeSet> nodes;
+ rv = es->getKeyNodes(keyName, walker.getCurrentPosition(), val,
+ i == 0, getter_AddRefs(nodes));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ res->add(*nodes);
+ }
+ }
+ else {
+ nsAutoString val;
+ exprResult->stringValue(val);
+ rv = es->getKeyNodes(keyName, walker.getCurrentPosition(), val,
+ true, getter_AddRefs(res));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ *aResult = res;
+ NS_ADDREF(*aResult);
+
+ return NS_OK;
+}
+
+Expr::ResultType
+txKeyFunctionCall::getReturnType()
+{
+ return NODESET_RESULT;
+}
+
+bool
+txKeyFunctionCall::isSensitiveTo(ContextSensitivity aContext)
+{
+ return (aContext & NODE_CONTEXT) || argsSensitiveTo(aContext);
+}
+
+#ifdef TX_TO_STRING
+nsresult
+txKeyFunctionCall::getNameAtom(nsIAtom** aAtom)
+{
+ *aAtom = nsGkAtoms::key;
+ NS_ADDREF(*aAtom);
+ return NS_OK;
+}
+#endif
+
+/**
+ * Hash functions
+ */
+
+bool
+txKeyValueHashEntry::KeyEquals(KeyTypePointer aKey) const
+{
+ return mKey.mKeyName == aKey->mKeyName &&
+ mKey.mRootIdentifier == aKey->mRootIdentifier &&
+ mKey.mKeyValue.Equals(aKey->mKeyValue);
+}
+
+PLDHashNumber
+txKeyValueHashEntry::HashKey(KeyTypePointer aKey)
+{
+ const txKeyValueHashKey* key =
+ static_cast<const txKeyValueHashKey*>(aKey);
+
+ return AddToHash(HashString(key->mKeyValue),
+ key->mKeyName.mNamespaceID,
+ key->mRootIdentifier,
+ key->mKeyName.mLocalName.get());
+}
+
+bool
+txIndexedKeyHashEntry::KeyEquals(KeyTypePointer aKey) const
+{
+ return mKey.mKeyName == aKey->mKeyName &&
+ mKey.mRootIdentifier == aKey->mRootIdentifier;
+}
+
+PLDHashNumber
+txIndexedKeyHashEntry::HashKey(KeyTypePointer aKey)
+{
+ const txIndexedKeyHashKey* key =
+ static_cast<const txIndexedKeyHashKey*>(aKey);
+ return HashGeneric(key->mKeyName.mNamespaceID,
+ key->mRootIdentifier,
+ key->mKeyName.mLocalName.get());
+}
+
+/*
+ * Class managing XSLT-keys
+ */
+
+nsresult
+txKeyHash::getKeyNodes(const txExpandedName& aKeyName,
+ const txXPathNode& aRoot,
+ const nsAString& aKeyValue,
+ bool aIndexIfNotFound,
+ txExecutionState& aEs,
+ txNodeSet** aResult)
+{
+ *aResult = nullptr;
+
+ int32_t identifier = txXPathNodeUtils::getUniqueIdentifier(aRoot);
+
+ txKeyValueHashKey valueKey(aKeyName, identifier, aKeyValue);
+ txKeyValueHashEntry* valueEntry = mKeyValues.GetEntry(valueKey);
+ if (valueEntry) {
+ *aResult = valueEntry->mNodeSet;
+ NS_ADDREF(*aResult);
+
+ return NS_OK;
+ }
+
+ // We didn't find a value. This could either mean that that key has no
+ // nodes with that value or that the key hasn't been indexed using this
+ // document.
+
+ if (!aIndexIfNotFound) {
+ // If aIndexIfNotFound is set then the caller knows this key is
+ // indexed, so don't bother investigating.
+ *aResult = mEmptyNodeSet;
+ NS_ADDREF(*aResult);
+
+ return NS_OK;
+ }
+
+ txIndexedKeyHashKey indexKey(aKeyName, identifier);
+ txIndexedKeyHashEntry* indexEntry = mIndexedKeys.PutEntry(indexKey);
+ NS_ENSURE_TRUE(indexEntry, NS_ERROR_OUT_OF_MEMORY);
+
+ if (indexEntry->mIndexed) {
+ // The key was indexed and apparently didn't contain this value so
+ // return the empty nodeset.
+ *aResult = mEmptyNodeSet;
+ NS_ADDREF(*aResult);
+
+ return NS_OK;
+ }
+
+ // The key needs to be indexed.
+ txXSLKey* xslKey = mKeys.get(aKeyName);
+ if (!xslKey) {
+ // The key didn't exist, so bail.
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ nsresult rv = xslKey->indexSubtreeRoot(aRoot, mKeyValues, aEs);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ indexEntry->mIndexed = true;
+
+ // Now that the key is indexed we can get its value.
+ valueEntry = mKeyValues.GetEntry(valueKey);
+ if (valueEntry) {
+ *aResult = valueEntry->mNodeSet;
+ NS_ADDREF(*aResult);
+ }
+ else {
+ *aResult = mEmptyNodeSet;
+ NS_ADDREF(*aResult);
+ }
+
+ return NS_OK;
+}
+
+nsresult
+txKeyHash::init()
+{
+ mEmptyNodeSet = new txNodeSet(nullptr);
+
+ return NS_OK;
+}
+
+
+/**
+ * Adds a match/use pair.
+ * @param aMatch match-pattern
+ * @param aUse use-expression
+ * @return false if an error occurred, true otherwise
+ */
+bool txXSLKey::addKey(nsAutoPtr<txPattern>&& aMatch, nsAutoPtr<Expr>&& aUse)
+{
+ if (!aMatch || !aUse)
+ return false;
+
+ Key* key = mKeys.AppendElement();
+ if (!key)
+ return false;
+
+ key->matchPattern = Move(aMatch);
+ key->useExpr = Move(aUse);
+
+ return true;
+}
+
+/**
+ * Indexes a document and adds it to the hash of key values
+ * @param aRoot Subtree root to index and add
+ * @param aKeyValueHash Hash to add values to
+ * @param aEs txExecutionState to use for XPath evaluation
+ */
+nsresult txXSLKey::indexSubtreeRoot(const txXPathNode& aRoot,
+ txKeyValueHash& aKeyValueHash,
+ txExecutionState& aEs)
+{
+ txKeyValueHashKey key(mName,
+ txXPathNodeUtils::getUniqueIdentifier(aRoot),
+ EmptyString());
+ return indexTree(aRoot, key, aKeyValueHash, aEs);
+}
+
+/**
+ * Recursively searches a node, its attributes and its subtree for
+ * nodes matching any of the keys match-patterns.
+ * @param aNode Node to search
+ * @param aKey Key to use when adding into the hash
+ * @param aKeyValueHash Hash to add values to
+ * @param aEs txExecutionState to use for XPath evaluation
+ */
+nsresult txXSLKey::indexTree(const txXPathNode& aNode,
+ txKeyValueHashKey& aKey,
+ txKeyValueHash& aKeyValueHash,
+ txExecutionState& aEs)
+{
+ nsresult rv = testNode(aNode, aKey, aKeyValueHash, aEs);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // check if the node's attributes match
+ txXPathTreeWalker walker(aNode);
+ if (walker.moveToFirstAttribute()) {
+ do {
+ rv = testNode(walker.getCurrentPosition(), aKey, aKeyValueHash,
+ aEs);
+ NS_ENSURE_SUCCESS(rv, rv);
+ } while (walker.moveToNextAttribute());
+ walker.moveToParent();
+ }
+
+ // check if the node's descendants match
+ if (walker.moveToFirstChild()) {
+ do {
+ rv = indexTree(walker.getCurrentPosition(), aKey, aKeyValueHash,
+ aEs);
+ NS_ENSURE_SUCCESS(rv, rv);
+ } while (walker.moveToNextSibling());
+ }
+
+ return NS_OK;
+}
+
+/**
+ * Tests one node if it matches any of the keys match-patterns. If
+ * the node matches its values are added to the index.
+ * @param aNode Node to test
+ * @param aKey Key to use when adding into the hash
+ * @param aKeyValueHash Hash to add values to
+ * @param aEs txExecutionState to use for XPath evaluation
+ */
+nsresult txXSLKey::testNode(const txXPathNode& aNode,
+ txKeyValueHashKey& aKey,
+ txKeyValueHash& aKeyValueHash,
+ txExecutionState& aEs)
+{
+ nsAutoString val;
+ uint32_t currKey, numKeys = mKeys.Length();
+ for (currKey = 0; currKey < numKeys; ++currKey) {
+ if (mKeys[currKey].matchPattern->matches(aNode, &aEs)) {
+ txSingleNodeContext *evalContext =
+ new txSingleNodeContext(aNode, &aEs);
+ NS_ENSURE_TRUE(evalContext, NS_ERROR_OUT_OF_MEMORY);
+
+ nsresult rv = aEs.pushEvalContext(evalContext);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ RefPtr<txAExprResult> exprResult;
+ rv = mKeys[currKey].useExpr->evaluate(evalContext,
+ getter_AddRefs(exprResult));
+
+ delete aEs.popEvalContext();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (exprResult->getResultType() == txAExprResult::NODESET) {
+ txNodeSet* res = static_cast<txNodeSet*>
+ (static_cast<txAExprResult*>
+ (exprResult));
+ int32_t i;
+ for (i = 0; i < res->size(); ++i) {
+ val.Truncate();
+ txXPathNodeUtils::appendNodeValue(res->get(i), val);
+
+ aKey.mKeyValue.Assign(val);
+ txKeyValueHashEntry* entry = aKeyValueHash.PutEntry(aKey);
+ NS_ENSURE_TRUE(entry && entry->mNodeSet,
+ NS_ERROR_OUT_OF_MEMORY);
+
+ if (entry->mNodeSet->isEmpty() ||
+ entry->mNodeSet->get(entry->mNodeSet->size() - 1) !=
+ aNode) {
+ entry->mNodeSet->append(aNode);
+ }
+ }
+ }
+ else {
+ exprResult->stringValue(val);
+
+ aKey.mKeyValue.Assign(val);
+ txKeyValueHashEntry* entry = aKeyValueHash.PutEntry(aKey);
+ NS_ENSURE_TRUE(entry && entry->mNodeSet,
+ NS_ERROR_OUT_OF_MEMORY);
+
+ if (entry->mNodeSet->isEmpty() ||
+ entry->mNodeSet->get(entry->mNodeSet->size() - 1) !=
+ aNode) {
+ entry->mNodeSet->append(aNode);
+ }
+ }
+ }
+ }
+
+ return NS_OK;
+}
diff --git a/dom/xslt/xslt/txMozillaStylesheetCompiler.cpp b/dom/xslt/xslt/txMozillaStylesheetCompiler.cpp
new file mode 100644
index 000000000..726441757
--- /dev/null
+++ b/dom/xslt/xslt/txMozillaStylesheetCompiler.cpp
@@ -0,0 +1,718 @@
+/* -*- 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 "nsCOMArray.h"
+#include "nsIAuthPrompt.h"
+#include "nsIDOMNode.h"
+#include "nsIDOMDocument.h"
+#include "nsIDocument.h"
+#include "nsIExpatSink.h"
+#include "nsIChannelEventSink.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsILoadGroup.h"
+#include "nsIParser.h"
+#include "nsCharsetSource.h"
+#include "nsIRequestObserver.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsContentPolicyUtils.h"
+#include "nsIStreamConverterService.h"
+#include "nsSyncLoadService.h"
+#include "nsIURI.h"
+#include "nsIPrincipal.h"
+#include "nsIWindowWatcher.h"
+#include "nsIXMLContentSink.h"
+#include "nsMimeTypes.h"
+#include "nsNetUtil.h"
+#include "nsParserCIID.h"
+#include "nsGkAtoms.h"
+#include "txLog.h"
+#include "txMozillaXSLTProcessor.h"
+#include "txStylesheetCompiler.h"
+#include "txXMLUtils.h"
+#include "nsAttrName.h"
+#include "nsIScriptError.h"
+#include "nsIURL.h"
+#include "nsError.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/EncodingUtils.h"
+#include "mozilla/UniquePtr.h"
+
+using namespace mozilla;
+using mozilla::dom::EncodingUtils;
+using mozilla::net::ReferrerPolicy;
+
+static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID);
+
+static void
+getSpec(nsIChannel* aChannel, nsAString& aSpec)
+{
+ if (!aChannel) {
+ return;
+ }
+
+ nsCOMPtr<nsIURI> uri;
+ aChannel->GetOriginalURI(getter_AddRefs(uri));
+ if (!uri) {
+ return;
+ }
+
+ nsAutoCString spec;
+ uri->GetSpec(spec);
+ AppendUTF8toUTF16(spec, aSpec);
+}
+
+class txStylesheetSink final : public nsIXMLContentSink,
+ public nsIExpatSink,
+ public nsIStreamListener,
+ public nsIInterfaceRequestor
+{
+public:
+ txStylesheetSink(txStylesheetCompiler* aCompiler, nsIParser* aParser);
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIEXPATSINK
+ NS_DECL_NSISTREAMLISTENER
+ NS_DECL_NSIREQUESTOBSERVER
+ NS_DECL_NSIINTERFACEREQUESTOR
+
+ // nsIContentSink
+ NS_IMETHOD WillParse(void) override { return NS_OK; }
+ NS_IMETHOD DidBuildModel(bool aTerminated) override;
+ NS_IMETHOD WillInterrupt(void) override { return NS_OK; }
+ NS_IMETHOD WillResume(void) override { return NS_OK; }
+ NS_IMETHOD SetParser(nsParserBase* aParser) override { return NS_OK; }
+ virtual void FlushPendingNotifications(mozFlushType aType) override { }
+ NS_IMETHOD SetDocumentCharset(nsACString& aCharset) override { return NS_OK; }
+ virtual nsISupports *GetTarget() override { return nullptr; }
+
+private:
+ RefPtr<txStylesheetCompiler> mCompiler;
+ nsCOMPtr<nsIStreamListener> mListener;
+ nsCOMPtr<nsIParser> mParser;
+ bool mCheckedForXML;
+
+protected:
+ ~txStylesheetSink() {}
+
+ // This exists solely to suppress a warning from nsDerivedSafe
+ txStylesheetSink();
+};
+
+txStylesheetSink::txStylesheetSink(txStylesheetCompiler* aCompiler,
+ nsIParser* aParser)
+ : mCompiler(aCompiler)
+ , mParser(aParser)
+ , mCheckedForXML(false)
+{
+ mListener = do_QueryInterface(aParser);
+}
+
+NS_IMPL_ISUPPORTS(txStylesheetSink,
+ nsIXMLContentSink,
+ nsIContentSink,
+ nsIExpatSink,
+ nsIStreamListener,
+ nsIRequestObserver,
+ nsIInterfaceRequestor)
+
+NS_IMETHODIMP
+txStylesheetSink::HandleStartElement(const char16_t *aName,
+ const char16_t **aAtts,
+ uint32_t aAttsCount,
+ uint32_t aLineNumber)
+{
+ NS_PRECONDITION(aAttsCount % 2 == 0, "incorrect aAttsCount");
+
+ nsresult rv =
+ mCompiler->startElement(aName, aAtts, aAttsCount / 2);
+ if (NS_FAILED(rv)) {
+ mCompiler->cancel(rv);
+
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+txStylesheetSink::HandleEndElement(const char16_t *aName)
+{
+ nsresult rv = mCompiler->endElement();
+ if (NS_FAILED(rv)) {
+ mCompiler->cancel(rv);
+
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+txStylesheetSink::HandleComment(const char16_t *aName)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+txStylesheetSink::HandleCDataSection(const char16_t *aData,
+ uint32_t aLength)
+{
+ return HandleCharacterData(aData, aLength);
+}
+
+NS_IMETHODIMP
+txStylesheetSink::HandleDoctypeDecl(const nsAString & aSubset,
+ const nsAString & aName,
+ const nsAString & aSystemId,
+ const nsAString & aPublicId,
+ nsISupports *aCatalogData)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+txStylesheetSink::HandleCharacterData(const char16_t *aData,
+ uint32_t aLength)
+{
+ nsresult rv = mCompiler->characters(Substring(aData, aData + aLength));
+ if (NS_FAILED(rv)) {
+ mCompiler->cancel(rv);
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+txStylesheetSink::HandleProcessingInstruction(const char16_t *aTarget,
+ const char16_t *aData)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+txStylesheetSink::HandleXMLDeclaration(const char16_t *aVersion,
+ const char16_t *aEncoding,
+ int32_t aStandalone)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+txStylesheetSink::ReportError(const char16_t *aErrorText,
+ const char16_t *aSourceText,
+ nsIScriptError *aError,
+ bool *_retval)
+{
+ NS_PRECONDITION(aError && aSourceText && aErrorText, "Check arguments!!!");
+
+ // The expat driver should report the error.
+ *_retval = true;
+
+ mCompiler->cancel(NS_ERROR_FAILURE, aErrorText, aSourceText);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+txStylesheetSink::DidBuildModel(bool aTerminated)
+{
+ return mCompiler->doneLoading();
+}
+
+NS_IMETHODIMP
+txStylesheetSink::OnDataAvailable(nsIRequest *aRequest, nsISupports *aContext,
+ nsIInputStream *aInputStream,
+ uint64_t aOffset, uint32_t aCount)
+{
+ if (!mCheckedForXML) {
+ nsCOMPtr<nsIDTD> dtd;
+ mParser->GetDTD(getter_AddRefs(dtd));
+ if (dtd) {
+ mCheckedForXML = true;
+ if (!(dtd->GetType() & NS_IPARSER_FLAG_XML)) {
+ nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
+ nsAutoString spec;
+ getSpec(channel, spec);
+ mCompiler->cancel(NS_ERROR_XSLT_WRONG_MIME_TYPE, nullptr,
+ spec.get());
+
+ return NS_ERROR_XSLT_WRONG_MIME_TYPE;
+ }
+ }
+ }
+
+ return mListener->OnDataAvailable(aRequest, mParser, aInputStream,
+ aOffset, aCount);
+}
+
+NS_IMETHODIMP
+txStylesheetSink::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext)
+{
+ int32_t charsetSource = kCharsetFromDocTypeDefault;
+
+ nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
+
+ // check channel's charset...
+ nsAutoCString charsetVal;
+ nsAutoCString charset;
+ if (NS_SUCCEEDED(channel->GetContentCharset(charsetVal))) {
+ if (EncodingUtils::FindEncodingForLabel(charsetVal, charset)) {
+ charsetSource = kCharsetFromChannel;
+ }
+ }
+
+ if (charset.IsEmpty()) {
+ charset.AssignLiteral("UTF-8");
+ }
+
+ mParser->SetDocumentCharset(charset, charsetSource);
+
+ nsAutoCString contentType;
+ channel->GetContentType(contentType);
+
+ // Time to sniff! Note: this should go away once file channels do
+ // sniffing themselves.
+ nsCOMPtr<nsIURI> uri;
+ channel->GetURI(getter_AddRefs(uri));
+ bool sniff;
+ if (NS_SUCCEEDED(uri->SchemeIs("file", &sniff)) && sniff &&
+ contentType.Equals(UNKNOWN_CONTENT_TYPE)) {
+ nsresult rv;
+ nsCOMPtr<nsIStreamConverterService> serv =
+ do_GetService("@mozilla.org/streamConverters;1", &rv);
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsIStreamListener> converter;
+ rv = serv->AsyncConvertData(UNKNOWN_CONTENT_TYPE,
+ "*/*",
+ mListener,
+ mParser,
+ getter_AddRefs(converter));
+ if (NS_SUCCEEDED(rv)) {
+ mListener = converter;
+ }
+ }
+ }
+
+ return mListener->OnStartRequest(aRequest, mParser);
+}
+
+NS_IMETHODIMP
+txStylesheetSink::OnStopRequest(nsIRequest *aRequest, nsISupports *aContext,
+ nsresult aStatusCode)
+{
+ bool success = true;
+
+ nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
+ if (httpChannel) {
+ httpChannel->GetRequestSucceeded(&success);
+ }
+
+ nsresult result = aStatusCode;
+ if (!success) {
+ // XXX We sometimes want to use aStatusCode here, but the parser resets
+ // it to NS_ERROR_NOINTERFACE because we don't implement
+ // nsIHTMLContentSink.
+ result = NS_ERROR_XSLT_NETWORK_ERROR;
+ }
+ else if (!mCheckedForXML) {
+ nsCOMPtr<nsIDTD> dtd;
+ mParser->GetDTD(getter_AddRefs(dtd));
+ if (dtd && !(dtd->GetType() & NS_IPARSER_FLAG_XML)) {
+ result = NS_ERROR_XSLT_WRONG_MIME_TYPE;
+ }
+ }
+
+ if (NS_FAILED(result)) {
+ nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
+ nsAutoString spec;
+ getSpec(channel, spec);
+ mCompiler->cancel(result, nullptr, spec.get());
+ }
+
+ nsresult rv = mListener->OnStopRequest(aRequest, mParser, aStatusCode);
+ mListener = nullptr;
+ mParser = nullptr;
+ return rv;
+}
+
+NS_IMETHODIMP
+txStylesheetSink::GetInterface(const nsIID& aIID, void** aResult)
+{
+ if (aIID.Equals(NS_GET_IID(nsIAuthPrompt))) {
+ NS_ENSURE_ARG(aResult);
+ *aResult = nullptr;
+
+ nsresult rv;
+ nsCOMPtr<nsIWindowWatcher> wwatcher =
+ do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAuthPrompt> prompt;
+ rv = wwatcher->GetNewAuthPrompter(nullptr, getter_AddRefs(prompt));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ prompt.forget(aResult);
+
+ return NS_OK;
+ }
+
+ return NS_ERROR_NO_INTERFACE;
+}
+
+class txCompileObserver final : public txACompileObserver
+{
+public:
+ txCompileObserver(txMozillaXSLTProcessor* aProcessor,
+ nsIDocument* aLoaderDocument);
+
+ TX_DECL_ACOMPILEOBSERVER
+ NS_INLINE_DECL_REFCOUNTING(txCompileObserver)
+
+ nsresult startLoad(nsIURI* aUri, txStylesheetCompiler* aCompiler,
+ nsIPrincipal* aSourcePrincipal,
+ ReferrerPolicy aReferrerPolicy);
+
+private:
+ RefPtr<txMozillaXSLTProcessor> mProcessor;
+ nsCOMPtr<nsIDocument> mLoaderDocument;
+
+ // This exists solely to suppress a warning from nsDerivedSafe
+ txCompileObserver();
+
+ // Private destructor, to discourage deletion outside of Release():
+ ~txCompileObserver()
+ {
+ }
+};
+
+txCompileObserver::txCompileObserver(txMozillaXSLTProcessor* aProcessor,
+ nsIDocument* aLoaderDocument)
+ : mProcessor(aProcessor),
+ mLoaderDocument(aLoaderDocument)
+{
+}
+
+nsresult
+txCompileObserver::loadURI(const nsAString& aUri,
+ const nsAString& aReferrerUri,
+ ReferrerPolicy aReferrerPolicy,
+ txStylesheetCompiler* aCompiler)
+{
+ if (mProcessor->IsLoadDisabled()) {
+ return NS_ERROR_XSLT_LOAD_BLOCKED_ERROR;
+ }
+
+ nsCOMPtr<nsIURI> uri;
+ nsresult rv = NS_NewURI(getter_AddRefs(uri), aUri);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIURI> referrerUri;
+ rv = NS_NewURI(getter_AddRefs(referrerUri), aReferrerUri);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ PrincipalOriginAttributes attrs;
+ nsCOMPtr<nsIPrincipal> referrerPrincipal =
+ BasePrincipal::CreateCodebasePrincipal(referrerUri, attrs);
+ NS_ENSURE_TRUE(referrerPrincipal, NS_ERROR_FAILURE);
+
+ return startLoad(uri, aCompiler, referrerPrincipal, aReferrerPolicy);
+}
+
+void
+txCompileObserver::onDoneCompiling(txStylesheetCompiler* aCompiler,
+ nsresult aResult,
+ const char16_t *aErrorText,
+ const char16_t *aParam)
+{
+ if (NS_SUCCEEDED(aResult)) {
+ mProcessor->setStylesheet(aCompiler->getStylesheet());
+ }
+ else {
+ mProcessor->reportError(aResult, aErrorText, aParam);
+ }
+}
+
+nsresult
+txCompileObserver::startLoad(nsIURI* aUri, txStylesheetCompiler* aCompiler,
+ nsIPrincipal* aReferrerPrincipal,
+ ReferrerPolicy aReferrerPolicy)
+{
+ nsCOMPtr<nsILoadGroup> loadGroup = mLoaderDocument->GetDocumentLoadGroup();
+ if (!loadGroup) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIChannel> channel;
+ nsresult rv = NS_NewChannelWithTriggeringPrincipal(
+ getter_AddRefs(channel),
+ aUri,
+ mLoaderDocument,
+ aReferrerPrincipal, // triggeringPrincipal
+ nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS,
+ nsIContentPolicy::TYPE_XSLT,
+ loadGroup);
+
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ channel->SetContentType(NS_LITERAL_CSTRING("text/xml"));
+
+ nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
+ if (httpChannel) {
+ httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
+ NS_LITERAL_CSTRING("*/*"),
+ false);
+
+ nsCOMPtr<nsIURI> referrerURI;
+ aReferrerPrincipal->GetURI(getter_AddRefs(referrerURI));
+ if (referrerURI) {
+ httpChannel->SetReferrerWithPolicy(referrerURI, aReferrerPolicy);
+ }
+ }
+
+ nsCOMPtr<nsIParser> parser = do_CreateInstance(kCParserCID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ RefPtr<txStylesheetSink> sink = new txStylesheetSink(aCompiler, parser);
+ NS_ENSURE_TRUE(sink, NS_ERROR_OUT_OF_MEMORY);
+
+ channel->SetNotificationCallbacks(sink);
+
+ parser->SetCommand(kLoadAsData);
+ parser->SetContentSink(sink);
+ parser->Parse(aUri);
+
+ return channel->AsyncOpen2(sink);
+}
+
+nsresult
+TX_LoadSheet(nsIURI* aUri, txMozillaXSLTProcessor* aProcessor,
+ nsIDocument* aLoaderDocument, ReferrerPolicy aReferrerPolicy)
+{
+ nsIPrincipal* principal = aLoaderDocument->NodePrincipal();
+
+ nsAutoCString spec;
+ aUri->GetSpec(spec);
+ MOZ_LOG(txLog::xslt, LogLevel::Info, ("TX_LoadSheet: %s\n", spec.get()));
+
+ RefPtr<txCompileObserver> observer =
+ new txCompileObserver(aProcessor, aLoaderDocument);
+ NS_ENSURE_TRUE(observer, NS_ERROR_OUT_OF_MEMORY);
+
+ RefPtr<txStylesheetCompiler> compiler =
+ new txStylesheetCompiler(NS_ConvertUTF8toUTF16(spec), aReferrerPolicy,
+ observer);
+ NS_ENSURE_TRUE(compiler, NS_ERROR_OUT_OF_MEMORY);
+
+ return observer->startLoad(aUri, compiler, principal, aReferrerPolicy);
+}
+
+/**
+ * handling DOM->txStylesheet
+ * Observer needs to do synchronous loads.
+ */
+static nsresult
+handleNode(nsINode* aNode, txStylesheetCompiler* aCompiler)
+{
+ nsresult rv = NS_OK;
+
+ if (aNode->IsElement()) {
+ dom::Element* element = aNode->AsElement();
+
+ uint32_t attsCount = element->GetAttrCount();
+ UniquePtr<txStylesheetAttr[]> atts;
+ if (attsCount > 0) {
+ atts = MakeUnique<txStylesheetAttr[]>(attsCount);
+ uint32_t counter;
+ for (counter = 0; counter < attsCount; ++counter) {
+ txStylesheetAttr& att = atts[counter];
+ const nsAttrName* name = element->GetAttrNameAt(counter);
+ att.mNamespaceID = name->NamespaceID();
+ att.mLocalName = name->LocalName();
+ att.mPrefix = name->GetPrefix();
+ element->GetAttr(att.mNamespaceID, att.mLocalName, att.mValue);
+ }
+ }
+
+ mozilla::dom::NodeInfo *ni = element->NodeInfo();
+
+ rv = aCompiler->startElement(ni->NamespaceID(),
+ ni->NameAtom(),
+ ni->GetPrefixAtom(), atts.get(),
+ attsCount);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // explicitly destroy the attrs here since we no longer need it
+ atts = nullptr;
+
+ for (nsIContent* child = element->GetFirstChild();
+ child;
+ child = child->GetNextSibling()) {
+
+ rv = handleNode(child, aCompiler);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ rv = aCompiler->endElement();
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ else if (aNode->IsNodeOfType(nsINode::eTEXT)) {
+ nsAutoString chars;
+ static_cast<nsIContent*>(aNode)->AppendTextTo(chars);
+ rv = aCompiler->characters(chars);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ else if (aNode->IsNodeOfType(nsINode::eDOCUMENT)) {
+ for (nsIContent* child = aNode->GetFirstChild();
+ child;
+ child = child->GetNextSibling()) {
+
+ rv = handleNode(child, aCompiler);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ }
+
+ return NS_OK;
+}
+
+class txSyncCompileObserver final : public txACompileObserver
+{
+public:
+ explicit txSyncCompileObserver(txMozillaXSLTProcessor* aProcessor);
+
+ TX_DECL_ACOMPILEOBSERVER
+ NS_INLINE_DECL_REFCOUNTING(txSyncCompileObserver)
+
+private:
+ // Private destructor, to discourage deletion outside of Release():
+ ~txSyncCompileObserver()
+ {
+ }
+
+ RefPtr<txMozillaXSLTProcessor> mProcessor;
+};
+
+txSyncCompileObserver::txSyncCompileObserver(txMozillaXSLTProcessor* aProcessor)
+ : mProcessor(aProcessor)
+{
+}
+
+nsresult
+txSyncCompileObserver::loadURI(const nsAString& aUri,
+ const nsAString& aReferrerUri,
+ ReferrerPolicy aReferrerPolicy,
+ txStylesheetCompiler* aCompiler)
+{
+ if (mProcessor->IsLoadDisabled()) {
+ return NS_ERROR_XSLT_LOAD_BLOCKED_ERROR;
+ }
+
+ nsCOMPtr<nsIURI> uri;
+ nsresult rv = NS_NewURI(getter_AddRefs(uri), aUri);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIURI> referrerUri;
+ rv = NS_NewURI(getter_AddRefs(referrerUri), aReferrerUri);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIPrincipal> referrerPrincipal =
+ BasePrincipal::CreateCodebasePrincipal(referrerUri, PrincipalOriginAttributes());
+ NS_ENSURE_TRUE(referrerPrincipal, NS_ERROR_FAILURE);
+
+ // This is probably called by js, a loadGroup for the channel doesn't
+ // make sense.
+ nsCOMPtr<nsINode> source;
+ if (mProcessor) {
+ source =
+ do_QueryInterface(mProcessor->GetSourceContentModel());
+ }
+ nsAutoSyncOperation sync(source ? source->OwnerDoc() : nullptr);
+ nsCOMPtr<nsIDOMDocument> document;
+
+ rv = nsSyncLoadService::LoadDocument(uri, nsIContentPolicy::TYPE_XSLT,
+ referrerPrincipal,
+ nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS,
+ nullptr, false,
+ aReferrerPolicy,
+ getter_AddRefs(document));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIDocument> doc = do_QueryInterface(document);
+ rv = handleNode(doc, aCompiler);
+ if (NS_FAILED(rv)) {
+ nsAutoCString spec;
+ uri->GetSpec(spec);
+ aCompiler->cancel(rv, nullptr, NS_ConvertUTF8toUTF16(spec).get());
+ return rv;
+ }
+
+ rv = aCompiler->doneLoading();
+ return rv;
+}
+
+void txSyncCompileObserver::onDoneCompiling(txStylesheetCompiler* aCompiler,
+ nsresult aResult,
+ const char16_t *aErrorText,
+ const char16_t *aParam)
+{
+}
+
+nsresult
+TX_CompileStylesheet(nsINode* aNode, txMozillaXSLTProcessor* aProcessor,
+ txStylesheet** aStylesheet)
+{
+ // If we move GetBaseURI to nsINode this can be simplified.
+ nsCOMPtr<nsIDocument> doc = aNode->OwnerDoc();
+
+ nsCOMPtr<nsIURI> uri;
+ if (aNode->IsNodeOfType(nsINode::eCONTENT)) {
+ uri = static_cast<nsIContent*>(aNode)->GetBaseURI();
+ }
+ else {
+ NS_ASSERTION(aNode->IsNodeOfType(nsINode::eDOCUMENT), "not a doc");
+ uri = static_cast<nsIDocument*>(aNode)->GetBaseURI();
+ }
+ NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE);
+
+ nsAutoCString spec;
+ uri->GetSpec(spec);
+ NS_ConvertUTF8toUTF16 baseURI(spec);
+
+ nsIURI* docUri = doc->GetDocumentURI();
+ NS_ENSURE_TRUE(docUri, NS_ERROR_FAILURE);
+
+ // We need to remove the ref, a URI with a ref would mean that we have an
+ // embedded stylesheet.
+ docUri->CloneIgnoringRef(getter_AddRefs(uri));
+ NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE);
+
+ uri->GetSpec(spec);
+ NS_ConvertUTF8toUTF16 stylesheetURI(spec);
+
+ RefPtr<txSyncCompileObserver> obs =
+ new txSyncCompileObserver(aProcessor);
+ NS_ENSURE_TRUE(obs, NS_ERROR_OUT_OF_MEMORY);
+
+ RefPtr<txStylesheetCompiler> compiler =
+ new txStylesheetCompiler(stylesheetURI, doc->GetReferrerPolicy(), obs);
+ NS_ENSURE_TRUE(compiler, NS_ERROR_OUT_OF_MEMORY);
+
+ compiler->setBaseURI(baseURI);
+
+ nsresult rv = handleNode(aNode, compiler);
+ if (NS_FAILED(rv)) {
+ compiler->cancel(rv);
+ return rv;
+ }
+
+ rv = compiler->doneLoading();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ *aStylesheet = compiler->getStylesheet();
+ NS_ADDREF(*aStylesheet);
+
+ return NS_OK;
+}
diff --git a/dom/xslt/xslt/txMozillaTextOutput.cpp b/dom/xslt/xslt/txMozillaTextOutput.cpp
new file mode 100644
index 000000000..8af2018c0
--- /dev/null
+++ b/dom/xslt/xslt/txMozillaTextOutput.cpp
@@ -0,0 +1,256 @@
+/* -*- 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 "txMozillaTextOutput.h"
+#include "nsContentCID.h"
+#include "nsIContent.h"
+#include "nsIDocument.h"
+#include "nsIDOMDocument.h"
+#include "nsIDOMDocumentFragment.h"
+#include "nsIDocumentTransformer.h"
+#include "nsCharsetSource.h"
+#include "nsIPrincipal.h"
+#include "txURIUtils.h"
+#include "nsContentCreatorFunctions.h"
+#include "nsContentUtils.h"
+#include "nsGkAtoms.h"
+#include "mozilla/dom/EncodingUtils.h"
+#include "nsTextNode.h"
+#include "nsNameSpaceManager.h"
+
+using namespace mozilla::dom;
+
+txMozillaTextOutput::txMozillaTextOutput(nsITransformObserver* aObserver)
+{
+ MOZ_COUNT_CTOR(txMozillaTextOutput);
+ mObserver = do_GetWeakReference(aObserver);
+}
+
+txMozillaTextOutput::txMozillaTextOutput(nsIDOMDocumentFragment* aDest)
+{
+ MOZ_COUNT_CTOR(txMozillaTextOutput);
+ mTextParent = do_QueryInterface(aDest);
+ mDocument = mTextParent->OwnerDoc();
+}
+
+txMozillaTextOutput::~txMozillaTextOutput()
+{
+ MOZ_COUNT_DTOR(txMozillaTextOutput);
+}
+
+nsresult
+txMozillaTextOutput::attribute(nsIAtom* aPrefix, nsIAtom* aLocalName,
+ nsIAtom* aLowercaseLocalName,
+ int32_t aNsID, const nsString& aValue)
+{
+ return NS_OK;
+}
+
+nsresult
+txMozillaTextOutput::attribute(nsIAtom* aPrefix, const nsSubstring& aName,
+ const int32_t aNsID,
+ const nsString& aValue)
+{
+ return NS_OK;
+}
+
+nsresult
+txMozillaTextOutput::characters(const nsSubstring& aData, bool aDOE)
+{
+ mText.Append(aData);
+
+ return NS_OK;
+}
+
+nsresult
+txMozillaTextOutput::comment(const nsString& aData)
+{
+ return NS_OK;
+}
+
+nsresult
+txMozillaTextOutput::endDocument(nsresult aResult)
+{
+ NS_ENSURE_TRUE(mDocument && mTextParent, NS_ERROR_FAILURE);
+
+ RefPtr<nsTextNode> text = new nsTextNode(mDocument->NodeInfoManager());
+
+ text->SetText(mText, false);
+ nsresult rv = mTextParent->AppendChildTo(text, true);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // This should really be handled by nsIDocument::EndLoad
+ MOZ_ASSERT(mDocument->GetReadyStateEnum() ==
+ nsIDocument::READYSTATE_LOADING, "Bad readyState");
+ mDocument->SetReadyStateInternal(nsIDocument::READYSTATE_INTERACTIVE);
+
+ if (NS_SUCCEEDED(aResult)) {
+ nsCOMPtr<nsITransformObserver> observer = do_QueryReferent(mObserver);
+ if (observer) {
+ observer->OnTransformDone(aResult, mDocument);
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult
+txMozillaTextOutput::endElement()
+{
+ return NS_OK;
+}
+
+nsresult
+txMozillaTextOutput::processingInstruction(const nsString& aTarget,
+ const nsString& aData)
+{
+ return NS_OK;
+}
+
+nsresult
+txMozillaTextOutput::startDocument()
+{
+ return NS_OK;
+}
+
+nsresult
+txMozillaTextOutput::createResultDocument(nsIDOMDocument* aSourceDocument,
+ bool aLoadedAsData)
+{
+ /*
+ * Create an XHTML document to hold the text.
+ *
+ * <html>
+ * <head />
+ * <body>
+ * <pre id="transformiixResult"> * The text comes here * </pre>
+ * <body>
+ * </html>
+ *
+ * Except if we are transforming into a non-displayed document we create
+ * the following DOM
+ *
+ * <transformiix:result> * The text comes here * </transformiix:result>
+ */
+
+ // Create the document
+ nsresult rv = NS_NewXMLDocument(getter_AddRefs(mDocument),
+ aLoadedAsData);
+ NS_ENSURE_SUCCESS(rv, rv);
+ // This should really be handled by nsIDocument::BeginLoad
+ MOZ_ASSERT(mDocument->GetReadyStateEnum() ==
+ nsIDocument::READYSTATE_UNINITIALIZED, "Bad readyState");
+ mDocument->SetReadyStateInternal(nsIDocument::READYSTATE_LOADING);
+ nsCOMPtr<nsIDocument> source = do_QueryInterface(aSourceDocument);
+ NS_ENSURE_STATE(source);
+ bool hasHadScriptObject = false;
+ nsIScriptGlobalObject* sgo =
+ source->GetScriptHandlingObject(hasHadScriptObject);
+ NS_ENSURE_STATE(sgo || !hasHadScriptObject);
+ mDocument->SetScriptHandlingObject(sgo);
+
+ NS_ASSERTION(mDocument, "Need document");
+
+ // Reset and set up document
+ URIUtils::ResetWithSource(mDocument, source);
+
+ // Set the charset
+ if (!mOutputFormat.mEncoding.IsEmpty()) {
+ nsAutoCString canonicalCharset;
+
+ if (EncodingUtils::FindEncodingForLabel(mOutputFormat.mEncoding,
+ canonicalCharset)) {
+ mDocument->SetDocumentCharacterSetSource(kCharsetFromOtherComponent);
+ mDocument->SetDocumentCharacterSet(canonicalCharset);
+ }
+ }
+
+ // Notify the contentsink that the document is created
+ nsCOMPtr<nsITransformObserver> observer = do_QueryReferent(mObserver);
+ if (observer) {
+ rv = observer->OnDocumentCreated(mDocument);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // Create the content
+
+ // When transforming into a non-displayed document (i.e. when there is no
+ // observer) we only create a transformiix:result root element.
+ if (!observer) {
+ int32_t namespaceID;
+ rv = nsContentUtils::NameSpaceManager()->
+ RegisterNameSpace(NS_LITERAL_STRING(kTXNameSpaceURI), namespaceID);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mTextParent =
+ mDocument->CreateElem(nsDependentAtomString(nsGkAtoms::result),
+ nsGkAtoms::transformiix, namespaceID);
+
+
+ rv = mDocument->AppendChildTo(mTextParent, true);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ else {
+ nsCOMPtr<nsIContent> html, head, body;
+ rv = createXHTMLElement(nsGkAtoms::html, getter_AddRefs(html));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = createXHTMLElement(nsGkAtoms::head, getter_AddRefs(head));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = html->AppendChildTo(head, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = createXHTMLElement(nsGkAtoms::body, getter_AddRefs(body));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = html->AppendChildTo(body, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = createXHTMLElement(nsGkAtoms::pre, getter_AddRefs(mTextParent));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mTextParent->SetAttr(kNameSpaceID_None, nsGkAtoms::id,
+ NS_LITERAL_STRING("transformiixResult"),
+ false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = body->AppendChildTo(mTextParent, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mDocument->AppendChildTo(html, true);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return NS_OK;
+}
+
+nsresult
+txMozillaTextOutput::startElement(nsIAtom* aPrefix, nsIAtom* aLocalName,
+ nsIAtom* aLowercaseLocalName, int32_t aNsID)
+{
+ return NS_OK;
+}
+
+nsresult
+txMozillaTextOutput::startElement(nsIAtom* aPrefix, const nsSubstring& aName,
+ const int32_t aNsID)
+{
+ return NS_OK;
+}
+
+void txMozillaTextOutput::getOutputDocument(nsIDOMDocument** aDocument)
+{
+ CallQueryInterface(mDocument, aDocument);
+}
+
+nsresult
+txMozillaTextOutput::createXHTMLElement(nsIAtom* aName,
+ nsIContent** aResult)
+{
+ nsCOMPtr<Element> element = mDocument->CreateHTMLElement(aName);
+ element.forget(aResult);
+ return NS_OK;
+}
diff --git a/dom/xslt/xslt/txMozillaTextOutput.h b/dom/xslt/xslt/txMozillaTextOutput.h
new file mode 100644
index 000000000..14f03ab99
--- /dev/null
+++ b/dom/xslt/xslt/txMozillaTextOutput.h
@@ -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/. */
+
+#ifndef TRANSFRMX_MOZILLA_TEXT_OUTPUT_H
+#define TRANSFRMX_MOZILLA_TEXT_OUTPUT_H
+
+#include "txXMLEventHandler.h"
+#include "nsCOMPtr.h"
+#include "nsWeakPtr.h"
+#include "txOutputFormat.h"
+
+class nsIDOMDocument;
+class nsIDOMDocumentFragment;
+class nsITransformObserver;
+class nsIDocument;
+class nsIContent;
+
+class txMozillaTextOutput : public txAOutputXMLEventHandler
+{
+public:
+ explicit txMozillaTextOutput(nsITransformObserver* aObserver);
+ explicit txMozillaTextOutput(nsIDOMDocumentFragment* aDest);
+ virtual ~txMozillaTextOutput();
+
+ TX_DECL_TXAXMLEVENTHANDLER
+ TX_DECL_TXAOUTPUTXMLEVENTHANDLER
+
+ nsresult createResultDocument(nsIDOMDocument* aSourceDocument,
+ bool aLoadedAsData);
+
+private:
+ nsresult createXHTMLElement(nsIAtom* aName, nsIContent** aResult);
+
+ nsCOMPtr<nsIContent> mTextParent;
+ nsWeakPtr mObserver;
+ nsCOMPtr<nsIDocument> mDocument;
+ txOutputFormat mOutputFormat;
+ nsString mText;
+};
+
+#endif
diff --git a/dom/xslt/xslt/txMozillaXMLOutput.cpp b/dom/xslt/xslt/txMozillaXMLOutput.cpp
new file mode 100644
index 000000000..069413d97
--- /dev/null
+++ b/dom/xslt/xslt/txMozillaXMLOutput.cpp
@@ -0,0 +1,1075 @@
+/* -*- 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 "txMozillaXMLOutput.h"
+
+#include "nsIDocument.h"
+#include "nsIDocShell.h"
+#include "nsScriptLoader.h"
+#include "nsIDOMDocument.h"
+#include "nsIDOMDocumentType.h"
+#include "nsIScriptElement.h"
+#include "nsCharsetSource.h"
+#include "nsIRefreshURI.h"
+#include "nsPIDOMWindow.h"
+#include "nsIContent.h"
+#include "nsContentCID.h"
+#include "nsUnicharUtils.h"
+#include "nsGkAtoms.h"
+#include "txLog.h"
+#include "nsIConsoleService.h"
+#include "nsIDOMDocumentFragment.h"
+#include "nsNameSpaceManager.h"
+#include "txStringUtils.h"
+#include "txURIUtils.h"
+#include "nsIHTMLDocument.h"
+#include "nsIStyleSheetLinkingElement.h"
+#include "nsIDocumentTransformer.h"
+#include "mozilla/StyleSheetInlines.h"
+#include "mozilla/css/Loader.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/EncodingUtils.h"
+#include "nsContentUtils.h"
+#include "txXMLUtils.h"
+#include "nsContentSink.h"
+#include "nsINode.h"
+#include "nsContentCreatorFunctions.h"
+#include "nsError.h"
+#include "nsIFrame.h"
+#include <algorithm>
+#include "nsTextNode.h"
+#include "mozilla/dom/Comment.h"
+#include "mozilla/dom/ProcessingInstruction.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+#define TX_ENSURE_CURRENTNODE \
+ NS_ASSERTION(mCurrentNode, "mCurrentNode is nullptr"); \
+ if (!mCurrentNode) \
+ return NS_ERROR_UNEXPECTED
+
+txMozillaXMLOutput::txMozillaXMLOutput(txOutputFormat* aFormat,
+ nsITransformObserver* aObserver)
+ : mTreeDepth(0),
+ mBadChildLevel(0),
+ mTableState(NORMAL),
+ mCreatingNewDocument(true),
+ mOpenedElementIsHTML(false),
+ mRootContentCreated(false),
+ mNoFixup(false)
+{
+ MOZ_COUNT_CTOR(txMozillaXMLOutput);
+ if (aObserver) {
+ mNotifier = new txTransformNotifier();
+ if (mNotifier) {
+ mNotifier->Init(aObserver);
+ }
+ }
+
+ mOutputFormat.merge(*aFormat);
+ mOutputFormat.setFromDefaults();
+}
+
+txMozillaXMLOutput::txMozillaXMLOutput(txOutputFormat* aFormat,
+ nsIDOMDocumentFragment* aFragment,
+ bool aNoFixup)
+ : mTreeDepth(0),
+ mBadChildLevel(0),
+ mTableState(NORMAL),
+ mCreatingNewDocument(false),
+ mOpenedElementIsHTML(false),
+ mRootContentCreated(false),
+ mNoFixup(aNoFixup)
+{
+ MOZ_COUNT_CTOR(txMozillaXMLOutput);
+ mOutputFormat.merge(*aFormat);
+ mOutputFormat.setFromDefaults();
+
+ mCurrentNode = do_QueryInterface(aFragment);
+ mDocument = mCurrentNode->OwnerDoc();
+ mNodeInfoManager = mDocument->NodeInfoManager();
+}
+
+txMozillaXMLOutput::~txMozillaXMLOutput()
+{
+ MOZ_COUNT_DTOR(txMozillaXMLOutput);
+}
+
+nsresult
+txMozillaXMLOutput::attribute(nsIAtom* aPrefix,
+ nsIAtom* aLocalName,
+ nsIAtom* aLowercaseLocalName,
+ const int32_t aNsID,
+ const nsString& aValue)
+{
+ nsCOMPtr<nsIAtom> owner;
+ if (mOpenedElementIsHTML && aNsID == kNameSpaceID_None) {
+ if (aLowercaseLocalName) {
+ aLocalName = aLowercaseLocalName;
+ }
+ else {
+ owner = TX_ToLowerCaseAtom(aLocalName);
+ NS_ENSURE_TRUE(owner, NS_ERROR_OUT_OF_MEMORY);
+
+ aLocalName = owner;
+ }
+ }
+
+ return attributeInternal(aPrefix, aLocalName, aNsID, aValue);
+}
+
+nsresult
+txMozillaXMLOutput::attribute(nsIAtom* aPrefix,
+ const nsSubstring& aLocalName,
+ const int32_t aNsID,
+ const nsString& aValue)
+{
+ nsCOMPtr<nsIAtom> lname;
+
+ if (mOpenedElementIsHTML && aNsID == kNameSpaceID_None) {
+ nsAutoString lnameStr;
+ nsContentUtils::ASCIIToLower(aLocalName, lnameStr);
+ lname = NS_Atomize(lnameStr);
+ }
+ else {
+ lname = NS_Atomize(aLocalName);
+ }
+
+ NS_ENSURE_TRUE(lname, NS_ERROR_OUT_OF_MEMORY);
+
+ // Check that it's a valid name
+ if (!nsContentUtils::IsValidNodeName(lname, aPrefix, aNsID)) {
+ // Try without prefix
+ aPrefix = nullptr;
+ if (!nsContentUtils::IsValidNodeName(lname, aPrefix, aNsID)) {
+ // Don't return error here since the callers don't deal
+ return NS_OK;
+ }
+ }
+
+ return attributeInternal(aPrefix, lname, aNsID, aValue);
+}
+
+nsresult
+txMozillaXMLOutput::attributeInternal(nsIAtom* aPrefix,
+ nsIAtom* aLocalName,
+ int32_t aNsID,
+ const nsString& aValue)
+{
+ if (!mOpenedElement) {
+ // XXX Signal this? (can't add attributes after element closed)
+ return NS_OK;
+ }
+
+ NS_ASSERTION(!mBadChildLevel, "mBadChildLevel set when element is opened");
+
+ return mOpenedElement->SetAttr(aNsID, aLocalName, aPrefix, aValue,
+ false);
+}
+
+nsresult
+txMozillaXMLOutput::characters(const nsSubstring& aData, bool aDOE)
+{
+ nsresult rv = closePrevious(false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!mBadChildLevel) {
+ mText.Append(aData);
+ }
+
+ return NS_OK;
+}
+
+nsresult
+txMozillaXMLOutput::comment(const nsString& aData)
+{
+ nsresult rv = closePrevious(true);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (mBadChildLevel) {
+ return NS_OK;
+ }
+
+ TX_ENSURE_CURRENTNODE;
+
+ RefPtr<Comment> comment = new Comment(mNodeInfoManager);
+
+ rv = comment->SetText(aData, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return mCurrentNode->AppendChildTo(comment, true);
+}
+
+nsresult
+txMozillaXMLOutput::endDocument(nsresult aResult)
+{
+ TX_ENSURE_CURRENTNODE;
+
+ if (NS_FAILED(aResult)) {
+ if (mNotifier) {
+ mNotifier->OnTransformEnd(aResult);
+ }
+
+ return NS_OK;
+ }
+
+ nsresult rv = closePrevious(true);
+ if (NS_FAILED(rv)) {
+ if (mNotifier) {
+ mNotifier->OnTransformEnd(rv);
+ }
+
+ return rv;
+ }
+
+ if (mCreatingNewDocument) {
+ // This should really be handled by nsIDocument::EndLoad
+ MOZ_ASSERT(mDocument->GetReadyStateEnum() ==
+ nsIDocument::READYSTATE_LOADING, "Bad readyState");
+ mDocument->SetReadyStateInternal(nsIDocument::READYSTATE_INTERACTIVE);
+ nsScriptLoader* loader = mDocument->ScriptLoader();
+ if (loader) {
+ loader->ParsingComplete(false);
+ }
+ }
+
+ if (!mRefreshString.IsEmpty()) {
+ nsPIDOMWindowOuter* win = mDocument->GetWindow();
+ if (win) {
+ nsCOMPtr<nsIRefreshURI> refURI =
+ do_QueryInterface(win->GetDocShell());
+ if (refURI) {
+ refURI->SetupRefreshURIFromHeader(mDocument->GetDocBaseURI(),
+ mDocument->NodePrincipal(),
+ mRefreshString);
+ }
+ }
+ }
+
+ if (mNotifier) {
+ mNotifier->OnTransformEnd();
+ }
+
+ return NS_OK;
+}
+
+nsresult
+txMozillaXMLOutput::endElement()
+{
+ TX_ENSURE_CURRENTNODE;
+
+ if (mBadChildLevel) {
+ --mBadChildLevel;
+ MOZ_LOG(txLog::xslt, LogLevel::Debug,
+ ("endElement, mBadChildLevel = %d\n", mBadChildLevel));
+ return NS_OK;
+ }
+
+ --mTreeDepth;
+
+ nsresult rv = closePrevious(true);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_ASSERTION(mCurrentNode->IsElement(), "borked mCurrentNode");
+ NS_ENSURE_TRUE(mCurrentNode->IsElement(), NS_ERROR_UNEXPECTED);
+
+ Element* element = mCurrentNode->AsElement();
+
+ // Handle html-elements
+ if (!mNoFixup) {
+ if (element->IsHTMLElement()) {
+ rv = endHTMLElement(element);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // Handle elements that are different when parser-created
+ if (element->IsAnyOfHTMLElements(nsGkAtoms::title,
+ nsGkAtoms::object,
+ nsGkAtoms::applet,
+ nsGkAtoms::select,
+ nsGkAtoms::textarea) ||
+ element->IsSVGElement(nsGkAtoms::title)) {
+ element->DoneAddingChildren(true);
+ } else if (element->IsSVGElement(nsGkAtoms::script) ||
+ element->IsHTMLElement(nsGkAtoms::script)) {
+ nsCOMPtr<nsIScriptElement> sele = do_QueryInterface(element);
+ MOZ_ASSERT(sele, "script elements need to implement nsIScriptElement");
+ bool block = sele->AttemptToExecute();
+ // If the act of insertion evaluated the script, we're fine.
+ // Else, add this script element to the array of loading scripts.
+ if (block) {
+ rv = mNotifier->AddScriptElement(sele);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ } else if (element->IsAnyOfHTMLElements(nsGkAtoms::input,
+ nsGkAtoms::button,
+ nsGkAtoms::menuitem,
+ nsGkAtoms::audio,
+ nsGkAtoms::video)) {
+ element->DoneCreatingElement();
+ }
+ }
+
+ if (mCreatingNewDocument) {
+ // Handle all sorts of stylesheets
+ nsCOMPtr<nsIStyleSheetLinkingElement> ssle =
+ do_QueryInterface(mCurrentNode);
+ if (ssle) {
+ ssle->SetEnableUpdates(true);
+ bool willNotify;
+ bool isAlternate;
+ nsresult rv = ssle->UpdateStyleSheet(mNotifier, &willNotify,
+ &isAlternate);
+ if (mNotifier && NS_SUCCEEDED(rv) && willNotify && !isAlternate) {
+ mNotifier->AddPendingStylesheet();
+ }
+ }
+ }
+
+ // Add the element to the tree if it wasn't added before and take one step
+ // up the tree
+ uint32_t last = mCurrentNodeStack.Count() - 1;
+ NS_ASSERTION(last != (uint32_t)-1, "empty stack");
+
+ nsCOMPtr<nsINode> parent = mCurrentNodeStack.SafeObjectAt(last);
+ mCurrentNodeStack.RemoveObjectAt(last);
+
+ if (mCurrentNode == mNonAddedNode) {
+ if (parent == mDocument) {
+ NS_ASSERTION(!mRootContentCreated,
+ "Parent to add to shouldn't be a document if we "
+ "have a root content");
+ mRootContentCreated = true;
+ }
+
+ // Check to make sure that script hasn't inserted the node somewhere
+ // else in the tree
+ if (!mCurrentNode->GetParentNode()) {
+ parent->AppendChildTo(mNonAddedNode, true);
+ }
+ mNonAddedNode = nullptr;
+ }
+
+ mCurrentNode = parent;
+
+ mTableState =
+ static_cast<TableState>(NS_PTR_TO_INT32(mTableStateStack.pop()));
+
+ return NS_OK;
+}
+
+void txMozillaXMLOutput::getOutputDocument(nsIDOMDocument** aDocument)
+{
+ CallQueryInterface(mDocument, aDocument);
+}
+
+nsresult
+txMozillaXMLOutput::processingInstruction(const nsString& aTarget, const nsString& aData)
+{
+ nsresult rv = closePrevious(true);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (mOutputFormat.mMethod == eHTMLOutput)
+ return NS_OK;
+
+ TX_ENSURE_CURRENTNODE;
+
+ rv = nsContentUtils::CheckQName(aTarget, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIContent> pi =
+ NS_NewXMLProcessingInstruction(mNodeInfoManager, aTarget, aData);
+
+ nsCOMPtr<nsIStyleSheetLinkingElement> ssle;
+ if (mCreatingNewDocument) {
+ ssle = do_QueryInterface(pi);
+ if (ssle) {
+ ssle->InitStyleLinkElement(false);
+ ssle->SetEnableUpdates(false);
+ }
+ }
+
+ rv = mCurrentNode->AppendChildTo(pi, true);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (ssle) {
+ ssle->SetEnableUpdates(true);
+ bool willNotify;
+ bool isAlternate;
+ rv = ssle->UpdateStyleSheet(mNotifier, &willNotify, &isAlternate);
+ if (mNotifier && NS_SUCCEEDED(rv) && willNotify && !isAlternate) {
+ mNotifier->AddPendingStylesheet();
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult
+txMozillaXMLOutput::startDocument()
+{
+ if (mNotifier) {
+ mNotifier->OnTransformStart();
+ }
+
+ if (mCreatingNewDocument) {
+ nsScriptLoader* loader = mDocument->ScriptLoader();
+ if (loader) {
+ loader->BeginDeferringScripts();
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult
+txMozillaXMLOutput::startElement(nsIAtom* aPrefix, nsIAtom* aLocalName,
+ nsIAtom* aLowercaseLocalName,
+ const int32_t aNsID)
+{
+ NS_PRECONDITION(aNsID != kNameSpaceID_None || !aPrefix,
+ "Can't have prefix without namespace");
+
+ if (mOutputFormat.mMethod == eHTMLOutput && aNsID == kNameSpaceID_None) {
+ nsCOMPtr<nsIAtom> owner;
+ if (!aLowercaseLocalName) {
+ owner = TX_ToLowerCaseAtom(aLocalName);
+ NS_ENSURE_TRUE(owner, NS_ERROR_OUT_OF_MEMORY);
+
+ aLowercaseLocalName = owner;
+ }
+ return startElementInternal(nullptr,
+ aLowercaseLocalName,
+ kNameSpaceID_XHTML);
+ }
+
+ return startElementInternal(aPrefix, aLocalName, aNsID);
+}
+
+nsresult
+txMozillaXMLOutput::startElement(nsIAtom* aPrefix,
+ const nsSubstring& aLocalName,
+ const int32_t aNsID)
+{
+ int32_t nsId = aNsID;
+ nsCOMPtr<nsIAtom> lname;
+
+ if (mOutputFormat.mMethod == eHTMLOutput && aNsID == kNameSpaceID_None) {
+ nsId = kNameSpaceID_XHTML;
+
+ nsAutoString lnameStr;
+ nsContentUtils::ASCIIToLower(aLocalName, lnameStr);
+ lname = NS_Atomize(lnameStr);
+ }
+ else {
+ lname = NS_Atomize(aLocalName);
+ }
+
+ // No biggie if we lose the prefix due to OOM
+ NS_ENSURE_TRUE(lname, NS_ERROR_OUT_OF_MEMORY);
+
+ // Check that it's a valid name
+ if (!nsContentUtils::IsValidNodeName(lname, aPrefix, nsId)) {
+ // Try without prefix
+ aPrefix = nullptr;
+ if (!nsContentUtils::IsValidNodeName(lname, aPrefix, nsId)) {
+ return NS_ERROR_XSLT_BAD_NODE_NAME;
+ }
+ }
+
+ return startElementInternal(aPrefix, lname, nsId);
+}
+
+nsresult
+txMozillaXMLOutput::startElementInternal(nsIAtom* aPrefix,
+ nsIAtom* aLocalName,
+ int32_t aNsID)
+{
+ TX_ENSURE_CURRENTNODE;
+
+ if (mBadChildLevel) {
+ ++mBadChildLevel;
+ MOZ_LOG(txLog::xslt, LogLevel::Debug,
+ ("startElement, mBadChildLevel = %d\n", mBadChildLevel));
+ return NS_OK;
+ }
+
+ nsresult rv = closePrevious(true);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Push and init state
+ if (mTreeDepth == MAX_REFLOW_DEPTH) {
+ // eCloseElement couldn't add the parent so we fail as well or we've
+ // reached the limit of the depth of the tree that we allow.
+ ++mBadChildLevel;
+ MOZ_LOG(txLog::xslt, LogLevel::Debug,
+ ("startElement, mBadChildLevel = %d\n", mBadChildLevel));
+ return NS_OK;
+ }
+
+ ++mTreeDepth;
+
+ rv = mTableStateStack.push(NS_INT32_TO_PTR(mTableState));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!mCurrentNodeStack.AppendObject(mCurrentNode)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ mTableState = NORMAL;
+ mOpenedElementIsHTML = false;
+
+ // Create the element
+ RefPtr<NodeInfo> ni =
+ mNodeInfoManager->GetNodeInfo(aLocalName, aPrefix, aNsID,
+ nsIDOMNode::ELEMENT_NODE);
+
+ NS_NewElement(getter_AddRefs(mOpenedElement), ni.forget(),
+ mCreatingNewDocument ?
+ FROM_PARSER_XSLT : FROM_PARSER_FRAGMENT);
+
+ // Set up the element and adjust state
+ if (!mNoFixup) {
+ if (aNsID == kNameSpaceID_XHTML) {
+ mOpenedElementIsHTML = (mOutputFormat.mMethod == eHTMLOutput);
+ rv = startHTMLElement(mOpenedElement, mOpenedElementIsHTML);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ }
+ }
+
+ if (mCreatingNewDocument) {
+ // Handle all sorts of stylesheets
+ nsCOMPtr<nsIStyleSheetLinkingElement> ssle =
+ do_QueryInterface(mOpenedElement);
+ if (ssle) {
+ ssle->InitStyleLinkElement(false);
+ ssle->SetEnableUpdates(false);
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult
+txMozillaXMLOutput::closePrevious(bool aFlushText)
+{
+ TX_ENSURE_CURRENTNODE;
+
+ nsresult rv;
+ if (mOpenedElement) {
+ bool currentIsDoc = mCurrentNode == mDocument;
+ if (currentIsDoc && mRootContentCreated) {
+ // We already have a document element, but the XSLT spec allows this.
+ // As a workaround, create a wrapper object and use that as the
+ // document element.
+
+ rv = createTxWrapper();
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ rv = mCurrentNode->AppendChildTo(mOpenedElement, true);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (currentIsDoc) {
+ mRootContentCreated = true;
+ nsContentSink::NotifyDocElementCreated(mDocument);
+ }
+
+ mCurrentNode = mOpenedElement;
+ mOpenedElement = nullptr;
+ }
+ else if (aFlushText && !mText.IsEmpty()) {
+ // Text can't appear in the root of a document
+ if (mDocument == mCurrentNode) {
+ if (XMLUtils::isWhitespace(mText)) {
+ mText.Truncate();
+
+ return NS_OK;
+ }
+
+ rv = createTxWrapper();
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ RefPtr<nsTextNode> text = new nsTextNode(mNodeInfoManager);
+
+ rv = text->SetText(mText, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mCurrentNode->AppendChildTo(text, true);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mText.Truncate();
+ }
+
+ return NS_OK;
+}
+
+nsresult
+txMozillaXMLOutput::createTxWrapper()
+{
+ NS_ASSERTION(mDocument == mCurrentNode,
+ "creating wrapper when document isn't parent");
+
+ int32_t namespaceID;
+ nsresult rv = nsContentUtils::NameSpaceManager()->
+ RegisterNameSpace(NS_LITERAL_STRING(kTXNameSpaceURI), namespaceID);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<Element> wrapper =
+ mDocument->CreateElem(nsDependentAtomString(nsGkAtoms::result),
+ nsGkAtoms::transformiix, namespaceID);
+
+ uint32_t i, j, childCount = mDocument->GetChildCount();
+#ifdef DEBUG
+ // Keep track of the location of the current documentElement, if there is
+ // one, so we can verify later
+ uint32_t rootLocation = 0;
+#endif
+ for (i = 0, j = 0; i < childCount; ++i) {
+ nsCOMPtr<nsIContent> childContent = mDocument->GetChildAt(j);
+
+#ifdef DEBUG
+ if (childContent->IsElement()) {
+ rootLocation = j;
+ }
+#endif
+
+ if (childContent->NodeInfo()->NameAtom() == nsGkAtoms::documentTypeNodeName) {
+#ifdef DEBUG
+ // The new documentElement should go after the document type.
+ // This is needed for cases when there is no existing
+ // documentElement in the document.
+ rootLocation = std::max(rootLocation, j + 1);
+#endif
+ ++j;
+ }
+ else {
+ mDocument->RemoveChildAt(j, true);
+
+ rv = wrapper->AppendChildTo(childContent, true);
+ NS_ENSURE_SUCCESS(rv, rv);
+ break;
+ }
+ }
+
+ if (!mCurrentNodeStack.AppendObject(wrapper)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ mCurrentNode = wrapper;
+ mRootContentCreated = true;
+ NS_ASSERTION(rootLocation == mDocument->GetChildCount(),
+ "Incorrect root location");
+ return mDocument->AppendChildTo(wrapper, true);
+}
+
+nsresult
+txMozillaXMLOutput::startHTMLElement(nsIContent* aElement, bool aIsHTML)
+{
+ nsresult rv = NS_OK;
+
+ if ((!aElement->IsHTMLElement(nsGkAtoms::tr) || !aIsHTML) &&
+ NS_PTR_TO_INT32(mTableStateStack.peek()) == ADDED_TBODY) {
+ uint32_t last = mCurrentNodeStack.Count() - 1;
+ NS_ASSERTION(last != (uint32_t)-1, "empty stack");
+
+ mCurrentNode = mCurrentNodeStack.SafeObjectAt(last);
+ mCurrentNodeStack.RemoveObjectAt(last);
+ mTableStateStack.pop();
+ }
+
+ if (aElement->IsHTMLElement(nsGkAtoms::table) && aIsHTML) {
+ mTableState = TABLE;
+ }
+ else if (aElement->IsHTMLElement(nsGkAtoms::tr) && aIsHTML &&
+ NS_PTR_TO_INT32(mTableStateStack.peek()) == TABLE) {
+ nsCOMPtr<nsIContent> tbody;
+ rv = createHTMLElement(nsGkAtoms::tbody, getter_AddRefs(tbody));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mCurrentNode->AppendChildTo(tbody, true);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mTableStateStack.push(NS_INT32_TO_PTR(ADDED_TBODY));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!mCurrentNodeStack.AppendObject(tbody)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ mCurrentNode = tbody;
+ }
+ else if (aElement->IsHTMLElement(nsGkAtoms::head) &&
+ mOutputFormat.mMethod == eHTMLOutput) {
+ // Insert META tag, according to spec, 16.2, like
+ // <META http-equiv="Content-Type" content="text/html; charset=EUC-JP">
+ nsCOMPtr<nsIContent> meta;
+ rv = createHTMLElement(nsGkAtoms::meta, getter_AddRefs(meta));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = meta->SetAttr(kNameSpaceID_None, nsGkAtoms::httpEquiv,
+ NS_LITERAL_STRING("Content-Type"), false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString metacontent;
+ metacontent.Append(mOutputFormat.mMediaType);
+ metacontent.AppendLiteral("; charset=");
+ metacontent.Append(mOutputFormat.mEncoding);
+ rv = meta->SetAttr(kNameSpaceID_None, nsGkAtoms::content,
+ metacontent, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // No need to notify since aElement hasn't been inserted yet
+ NS_ASSERTION(!aElement->IsInUncomposedDoc(), "should not be in doc");
+ rv = aElement->AppendChildTo(meta, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return NS_OK;
+}
+
+nsresult
+txMozillaXMLOutput::endHTMLElement(nsIContent* aElement)
+{
+ if (mTableState == ADDED_TBODY) {
+ NS_ASSERTION(aElement->IsHTMLElement(nsGkAtoms::tbody),
+ "Element flagged as added tbody isn't a tbody");
+ uint32_t last = mCurrentNodeStack.Count() - 1;
+ NS_ASSERTION(last != (uint32_t)-1, "empty stack");
+
+ mCurrentNode = mCurrentNodeStack.SafeObjectAt(last);
+ mCurrentNodeStack.RemoveObjectAt(last);
+ mTableState = static_cast<TableState>
+ (NS_PTR_TO_INT32(mTableStateStack.pop()));
+
+ return NS_OK;
+ }
+ else if (mCreatingNewDocument && aElement->IsHTMLElement(nsGkAtoms::meta)) {
+ // handle HTTP-EQUIV data
+ nsAutoString httpEquiv;
+ aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::httpEquiv, httpEquiv);
+ if (!httpEquiv.IsEmpty()) {
+ nsAutoString value;
+ aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::content, value);
+ if (!value.IsEmpty()) {
+ nsContentUtils::ASCIIToLower(httpEquiv);
+ nsCOMPtr<nsIAtom> header = NS_Atomize(httpEquiv);
+ processHTTPEquiv(header, value);
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+void txMozillaXMLOutput::processHTTPEquiv(nsIAtom* aHeader, const nsString& aValue)
+{
+ // For now we only handle "refresh". There's a longer list in
+ // HTMLContentSink::ProcessHeaderData
+ if (aHeader == nsGkAtoms::refresh)
+ LossyCopyUTF16toASCII(aValue, mRefreshString);
+}
+
+nsresult
+txMozillaXMLOutput::createResultDocument(const nsSubstring& aName, int32_t aNsID,
+ nsIDOMDocument* aSourceDocument,
+ bool aLoadedAsData)
+{
+ nsresult rv;
+
+ // Create the document
+ if (mOutputFormat.mMethod == eHTMLOutput) {
+ rv = NS_NewHTMLDocument(getter_AddRefs(mDocument),
+ aLoadedAsData);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ else {
+ // We should check the root name/namespace here and create the
+ // appropriate document
+ rv = NS_NewXMLDocument(getter_AddRefs(mDocument),
+ aLoadedAsData);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ // This should really be handled by nsIDocument::BeginLoad
+ MOZ_ASSERT(mDocument->GetReadyStateEnum() ==
+ nsIDocument::READYSTATE_UNINITIALIZED, "Bad readyState");
+ mDocument->SetReadyStateInternal(nsIDocument::READYSTATE_LOADING);
+ mDocument->SetMayStartLayout(false);
+ nsCOMPtr<nsIDocument> source = do_QueryInterface(aSourceDocument);
+ NS_ENSURE_STATE(source);
+ bool hasHadScriptObject = false;
+ nsIScriptGlobalObject* sgo =
+ source->GetScriptHandlingObject(hasHadScriptObject);
+ NS_ENSURE_STATE(sgo || !hasHadScriptObject);
+
+ mCurrentNode = mDocument;
+ mNodeInfoManager = mDocument->NodeInfoManager();
+
+ // Reset and set up the document
+ URIUtils::ResetWithSource(mDocument, source);
+
+ // Make sure we set the script handling object after resetting with the
+ // source, so that we have the right principal.
+ mDocument->SetScriptHandlingObject(sgo);
+
+ // Set the charset
+ if (!mOutputFormat.mEncoding.IsEmpty()) {
+ nsAutoCString canonicalCharset;
+ if (EncodingUtils::FindEncodingForLabel(mOutputFormat.mEncoding,
+ canonicalCharset)) {
+ mDocument->SetDocumentCharacterSetSource(kCharsetFromOtherComponent);
+ mDocument->SetDocumentCharacterSet(canonicalCharset);
+ }
+ }
+
+ // Set the mime-type
+ if (!mOutputFormat.mMediaType.IsEmpty()) {
+ mDocument->SetContentType(mOutputFormat.mMediaType);
+ }
+ else if (mOutputFormat.mMethod == eHTMLOutput) {
+ mDocument->SetContentType(NS_LITERAL_STRING("text/html"));
+ }
+ else {
+ mDocument->SetContentType(NS_LITERAL_STRING("application/xml"));
+ }
+
+ if (mOutputFormat.mMethod == eXMLOutput &&
+ mOutputFormat.mOmitXMLDeclaration != eTrue) {
+ int32_t standalone;
+ if (mOutputFormat.mStandalone == eNotSet) {
+ standalone = -1;
+ }
+ else if (mOutputFormat.mStandalone == eFalse) {
+ standalone = 0;
+ }
+ else {
+ standalone = 1;
+ }
+
+ // Could use mOutputFormat.mVersion.get() when we support
+ // versions > 1.0.
+ static const char16_t kOneDotZero[] = { '1', '.', '0', '\0' };
+ mDocument->SetXMLDeclaration(kOneDotZero, mOutputFormat.mEncoding.get(),
+ standalone);
+ }
+
+ // Set up script loader of the result document.
+ nsScriptLoader *loader = mDocument->ScriptLoader();
+ if (mNotifier) {
+ loader->AddObserver(mNotifier);
+ }
+ else {
+ // Don't load scripts, we can't notify the caller when they're loaded.
+ loader->SetEnabled(false);
+ }
+
+ if (mNotifier) {
+ rv = mNotifier->SetOutputDocument(mDocument);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // Do this after calling OnDocumentCreated to ensure that the
+ // PresShell/PresContext has been hooked up and get notified.
+ nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(mDocument);
+ if (htmlDoc) {
+ htmlDoc->SetCompatibilityMode(eCompatibility_FullStandards);
+ }
+
+ // Add a doc-type if requested
+ if (!mOutputFormat.mSystemId.IsEmpty()) {
+ nsAutoString qName;
+ if (mOutputFormat.mMethod == eHTMLOutput) {
+ qName.AssignLiteral("html");
+ }
+ else {
+ qName.Assign(aName);
+ }
+
+ nsCOMPtr<nsIDOMDocumentType> documentType;
+
+ nsresult rv = nsContentUtils::CheckQName(qName);
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsIAtom> doctypeName = NS_Atomize(qName);
+ if (!doctypeName) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // Indicate that there is no internal subset (not just an empty one)
+ rv = NS_NewDOMDocumentType(getter_AddRefs(documentType),
+ mNodeInfoManager,
+ doctypeName,
+ mOutputFormat.mPublicId,
+ mOutputFormat.mSystemId,
+ NullString());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIContent> docType = do_QueryInterface(documentType);
+ rv = mDocument->AppendChildTo(docType, true);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult
+txMozillaXMLOutput::createHTMLElement(nsIAtom* aName,
+ nsIContent** aResult)
+{
+ NS_ASSERTION(mOutputFormat.mMethod == eHTMLOutput,
+ "need to adjust createHTMLElement");
+
+ *aResult = nullptr;
+
+ RefPtr<NodeInfo> ni;
+ ni = mNodeInfoManager->GetNodeInfo(aName, nullptr,
+ kNameSpaceID_XHTML,
+ nsIDOMNode::ELEMENT_NODE);
+
+ nsCOMPtr<Element> el;
+ nsresult rv =
+ NS_NewHTMLElement(getter_AddRefs(el), ni.forget(),
+ mCreatingNewDocument ?
+ FROM_PARSER_XSLT : FROM_PARSER_FRAGMENT);
+ el.forget(aResult);
+ return rv;
+}
+
+txTransformNotifier::txTransformNotifier()
+ : mPendingStylesheetCount(0),
+ mInTransform(false)
+{
+}
+
+txTransformNotifier::~txTransformNotifier()
+{
+}
+
+NS_IMPL_ISUPPORTS(txTransformNotifier,
+ nsIScriptLoaderObserver,
+ nsICSSLoaderObserver)
+
+NS_IMETHODIMP
+txTransformNotifier::ScriptAvailable(nsresult aResult,
+ nsIScriptElement *aElement,
+ bool aIsInline,
+ nsIURI *aURI,
+ int32_t aLineNo)
+{
+ if (NS_FAILED(aResult) &&
+ mScriptElements.RemoveObject(aElement)) {
+ SignalTransformEnd();
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+txTransformNotifier::ScriptEvaluated(nsresult aResult,
+ nsIScriptElement *aElement,
+ bool aIsInline)
+{
+ if (mScriptElements.RemoveObject(aElement)) {
+ SignalTransformEnd();
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+txTransformNotifier::StyleSheetLoaded(StyleSheet* aSheet,
+ bool aWasAlternate,
+ nsresult aStatus)
+{
+ if (mPendingStylesheetCount == 0) {
+ // We weren't waiting on this stylesheet anyway. This can happen if
+ // SignalTransformEnd got called with an error aResult. See
+ // http://bugzilla.mozilla.org/show_bug.cgi?id=215465.
+ return NS_OK;
+ }
+
+ // We're never waiting for alternate stylesheets
+ if (!aWasAlternate) {
+ --mPendingStylesheetCount;
+ SignalTransformEnd();
+ }
+
+ return NS_OK;
+}
+
+void
+txTransformNotifier::Init(nsITransformObserver* aObserver)
+{
+ mObserver = aObserver;
+}
+
+nsresult
+txTransformNotifier::AddScriptElement(nsIScriptElement* aElement)
+{
+ return mScriptElements.AppendObject(aElement) ? NS_OK :
+ NS_ERROR_OUT_OF_MEMORY;
+}
+
+void
+txTransformNotifier::AddPendingStylesheet()
+{
+ ++mPendingStylesheetCount;
+}
+
+void
+txTransformNotifier::OnTransformEnd(nsresult aResult)
+{
+ mInTransform = false;
+ SignalTransformEnd(aResult);
+}
+
+void
+txTransformNotifier::OnTransformStart()
+{
+ mInTransform = true;
+}
+
+nsresult
+txTransformNotifier::SetOutputDocument(nsIDocument* aDocument)
+{
+ mDocument = aDocument;
+
+ // Notify the contentsink that the document is created
+ return mObserver->OnDocumentCreated(mDocument);
+}
+
+void
+txTransformNotifier::SignalTransformEnd(nsresult aResult)
+{
+ if (mInTransform ||
+ (NS_SUCCEEDED(aResult) &&
+ (mScriptElements.Count() > 0 || mPendingStylesheetCount > 0))) {
+ return;
+ }
+
+ // mPendingStylesheetCount is nonzero at this point only if aResult is an
+ // error. Set it to 0 so we won't reenter this code when we stop the
+ // CSSLoader.
+ mPendingStylesheetCount = 0;
+ mScriptElements.Clear();
+
+ // Make sure that we don't get deleted while this function is executed and
+ // we remove ourselfs from the scriptloader
+ nsCOMPtr<nsIScriptLoaderObserver> kungFuDeathGrip(this);
+
+ if (mDocument) {
+ mDocument->ScriptLoader()->RemoveObserver(this);
+ // XXX Maybe we want to cancel script loads if NS_FAILED(rv)?
+
+ if (NS_FAILED(aResult)) {
+ mDocument->CSSLoader()->Stop();
+ }
+ }
+
+ if (NS_SUCCEEDED(aResult)) {
+ mObserver->OnTransformDone(aResult, mDocument);
+ }
+}
diff --git a/dom/xslt/xslt/txMozillaXMLOutput.h b/dom/xslt/xslt/txMozillaXMLOutput.h
new file mode 100644
index 000000000..acdf9bd02
--- /dev/null
+++ b/dom/xslt/xslt/txMozillaXMLOutput.h
@@ -0,0 +1,134 @@
+/* -*- 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_MOZILLA_XML_OUTPUT_H
+#define TRANSFRMX_MOZILLA_XML_OUTPUT_H
+
+#include "txXMLEventHandler.h"
+#include "nsAutoPtr.h"
+#include "nsIScriptLoaderObserver.h"
+#include "txOutputFormat.h"
+#include "nsCOMArray.h"
+#include "nsICSSLoaderObserver.h"
+#include "txStack.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/dom/Element.h"
+
+class nsIContent;
+class nsIDOMDocument;
+class nsIAtom;
+class nsIDOMDocumentFragment;
+class nsITransformObserver;
+class nsNodeInfoManager;
+class nsIDocument;
+class nsINode;
+
+class txTransformNotifier final : public nsIScriptLoaderObserver,
+ public nsICSSLoaderObserver
+{
+public:
+ txTransformNotifier();
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISCRIPTLOADEROBSERVER
+
+ // nsICSSLoaderObserver
+ NS_IMETHOD StyleSheetLoaded(mozilla::StyleSheet* aSheet,
+ bool aWasAlternate,
+ nsresult aStatus) override;
+
+ void Init(nsITransformObserver* aObserver);
+ nsresult AddScriptElement(nsIScriptElement* aElement);
+ void AddPendingStylesheet();
+ void OnTransformEnd(nsresult aResult = NS_OK);
+ void OnTransformStart();
+ nsresult SetOutputDocument(nsIDocument* aDocument);
+
+private:
+ ~txTransformNotifier();
+ void SignalTransformEnd(nsresult aResult = NS_OK);
+
+ nsCOMPtr<nsIDocument> mDocument;
+ nsCOMPtr<nsITransformObserver> mObserver;
+ nsCOMArray<nsIScriptElement> mScriptElements;
+ uint32_t mPendingStylesheetCount;
+ bool mInTransform;
+};
+
+class txMozillaXMLOutput : public txAOutputXMLEventHandler
+{
+public:
+ txMozillaXMLOutput(txOutputFormat* aFormat,
+ nsITransformObserver* aObserver);
+ txMozillaXMLOutput(txOutputFormat* aFormat,
+ nsIDOMDocumentFragment* aFragment,
+ bool aNoFixup);
+ ~txMozillaXMLOutput();
+
+ TX_DECL_TXAXMLEVENTHANDLER
+ TX_DECL_TXAOUTPUTXMLEVENTHANDLER
+
+ nsresult closePrevious(bool aFlushText);
+
+ nsresult createResultDocument(const nsSubstring& aName, int32_t aNsID,
+ nsIDOMDocument* aSourceDocument,
+ bool aLoadedAsData);
+
+private:
+ nsresult createTxWrapper();
+ nsresult startHTMLElement(nsIContent* aElement, bool aXHTML);
+ nsresult endHTMLElement(nsIContent* aElement);
+ void processHTTPEquiv(nsIAtom* aHeader, const nsString& aValue);
+ nsresult createHTMLElement(nsIAtom* aName,
+ nsIContent** aResult);
+
+ nsresult attributeInternal(nsIAtom* aPrefix, nsIAtom* aLocalName,
+ int32_t aNsID, const nsString& aValue);
+ nsresult startElementInternal(nsIAtom* aPrefix, nsIAtom* aLocalName,
+ int32_t aNsID);
+
+ nsCOMPtr<nsIDocument> mDocument;
+ nsCOMPtr<nsINode> mCurrentNode; // This is updated once an element is
+ // 'closed' (i.e. once we're done
+ // adding attributes to it).
+ // until then the opened element is
+ // kept in mOpenedElement
+ nsCOMPtr<mozilla::dom::Element> mOpenedElement;
+ RefPtr<nsNodeInfoManager> mNodeInfoManager;
+
+ nsCOMArray<nsINode> mCurrentNodeStack;
+
+ nsCOMPtr<nsIContent> mNonAddedNode;
+
+ RefPtr<txTransformNotifier> mNotifier;
+
+ uint32_t mTreeDepth, mBadChildLevel;
+ nsCString mRefreshString;
+
+ txStack mTableStateStack;
+ enum TableState {
+ NORMAL, // An element needing no special treatment
+ TABLE, // A HTML table element
+ ADDED_TBODY // An inserted tbody not coming from the stylesheet
+ };
+ TableState mTableState;
+
+ nsAutoString mText;
+
+ txOutputFormat mOutputFormat;
+
+ bool mCreatingNewDocument;
+
+ bool mOpenedElementIsHTML;
+
+ // Set to true when we know there's a root content in our document.
+ bool mRootContentCreated;
+
+ bool mNoFixup;
+
+ enum txAction { eCloseElement = 1, eFlushText = 2 };
+};
+
+#endif
diff --git a/dom/xslt/xslt/txMozillaXSLTProcessor.cpp b/dom/xslt/xslt/txMozillaXSLTProcessor.cpp
new file mode 100644
index 000000000..facb435b4
--- /dev/null
+++ b/dom/xslt/xslt/txMozillaXSLTProcessor.cpp
@@ -0,0 +1,1591 @@
+/* -*- 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 "txMozillaXSLTProcessor.h"
+#include "nsContentCID.h"
+#include "nsError.h"
+#include "nsIChannel.h"
+#include "mozilla/dom/Element.h"
+#include "nsIDOMElement.h"
+#include "nsIDOMText.h"
+#include "nsIDocument.h"
+#include "nsIDOMDocument.h"
+#include "nsIDOMDocumentFragment.h"
+#include "nsIDOMNodeList.h"
+#include "nsIIOService.h"
+#include "nsILoadGroup.h"
+#include "nsIStringBundle.h"
+#include "nsIURI.h"
+#include "XPathResult.h"
+#include "txExecutionState.h"
+#include "txMozillaTextOutput.h"
+#include "txMozillaXMLOutput.h"
+#include "txURIUtils.h"
+#include "txXMLUtils.h"
+#include "txUnknownHandler.h"
+#include "txXSLTProcessor.h"
+#include "nsIPrincipal.h"
+#include "nsThreadUtils.h"
+#include "jsapi.h"
+#include "txExprParser.h"
+#include "nsIErrorService.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsJSUtils.h"
+#include "nsIXPConnect.h"
+#include "nsVariant.h"
+#include "mozilla/dom/DocumentFragment.h"
+#include "mozilla/dom/XSLTProcessorBinding.h"
+
+using namespace mozilla::dom;
+
+static NS_DEFINE_CID(kXMLDocumentCID, NS_XMLDOCUMENT_CID);
+
+/**
+ * Output Handler Factories
+ */
+class txToDocHandlerFactory : public txAOutputHandlerFactory
+{
+public:
+ txToDocHandlerFactory(txExecutionState* aEs,
+ nsIDOMDocument* aSourceDocument,
+ nsITransformObserver* aObserver,
+ bool aDocumentIsData)
+ : mEs(aEs), mSourceDocument(aSourceDocument), mObserver(aObserver),
+ mDocumentIsData(aDocumentIsData)
+ {
+ }
+
+ TX_DECL_TXAOUTPUTHANDLERFACTORY
+
+private:
+ txExecutionState* mEs;
+ nsCOMPtr<nsIDOMDocument> mSourceDocument;
+ nsCOMPtr<nsITransformObserver> mObserver;
+ bool mDocumentIsData;
+};
+
+class txToFragmentHandlerFactory : public txAOutputHandlerFactory
+{
+public:
+ explicit txToFragmentHandlerFactory(nsIDOMDocumentFragment* aFragment)
+ : mFragment(aFragment)
+ {
+ }
+
+ TX_DECL_TXAOUTPUTHANDLERFACTORY
+
+private:
+ nsCOMPtr<nsIDOMDocumentFragment> mFragment;
+};
+
+nsresult
+txToDocHandlerFactory::createHandlerWith(txOutputFormat* aFormat,
+ txAXMLEventHandler** aHandler)
+{
+ *aHandler = nullptr;
+ switch (aFormat->mMethod) {
+ case eMethodNotSet:
+ case eXMLOutput:
+ {
+ *aHandler = new txUnknownHandler(mEs);
+ return NS_OK;
+ }
+
+ case eHTMLOutput:
+ {
+ nsAutoPtr<txMozillaXMLOutput> handler(
+ new txMozillaXMLOutput(aFormat, mObserver));
+
+ nsresult rv = handler->createResultDocument(EmptyString(),
+ kNameSpaceID_None,
+ mSourceDocument,
+ mDocumentIsData);
+ if (NS_SUCCEEDED(rv)) {
+ *aHandler = handler.forget();
+ }
+
+ return rv;
+ }
+
+ case eTextOutput:
+ {
+ nsAutoPtr<txMozillaTextOutput> handler(
+ new txMozillaTextOutput(mObserver));
+
+ nsresult rv = handler->createResultDocument(mSourceDocument,
+ mDocumentIsData);
+ if (NS_SUCCEEDED(rv)) {
+ *aHandler = handler.forget();
+ }
+
+ return rv;
+ }
+ }
+
+ NS_RUNTIMEABORT("Unknown output method");
+
+ return NS_ERROR_FAILURE;
+}
+
+nsresult
+txToDocHandlerFactory::createHandlerWith(txOutputFormat* aFormat,
+ const nsSubstring& aName,
+ int32_t aNsID,
+ txAXMLEventHandler** aHandler)
+{
+ *aHandler = nullptr;
+ switch (aFormat->mMethod) {
+ case eMethodNotSet:
+ {
+ NS_ERROR("How can method not be known when root element is?");
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ case eXMLOutput:
+ case eHTMLOutput:
+ {
+ nsAutoPtr<txMozillaXMLOutput> handler(
+ new txMozillaXMLOutput(aFormat, mObserver));
+
+ nsresult rv = handler->createResultDocument(aName, aNsID,
+ mSourceDocument,
+ mDocumentIsData);
+ if (NS_SUCCEEDED(rv)) {
+ *aHandler = handler.forget();
+ }
+
+ return rv;
+ }
+
+ case eTextOutput:
+ {
+ nsAutoPtr<txMozillaTextOutput> handler(
+ new txMozillaTextOutput(mObserver));
+
+ nsresult rv = handler->createResultDocument(mSourceDocument,
+ mDocumentIsData);
+ if (NS_SUCCEEDED(rv)) {
+ *aHandler = handler.forget();
+ }
+
+ return rv;
+ }
+ }
+
+ NS_RUNTIMEABORT("Unknown output method");
+
+ return NS_ERROR_FAILURE;
+}
+
+nsresult
+txToFragmentHandlerFactory::createHandlerWith(txOutputFormat* aFormat,
+ txAXMLEventHandler** aHandler)
+{
+ *aHandler = nullptr;
+ switch (aFormat->mMethod) {
+ case eMethodNotSet:
+ {
+ txOutputFormat format;
+ format.merge(*aFormat);
+ nsCOMPtr<nsIDOMDocument> domdoc;
+ mFragment->GetOwnerDocument(getter_AddRefs(domdoc));
+ NS_ASSERTION(domdoc, "unable to get ownerdocument");
+ nsCOMPtr<nsIDocument> doc = do_QueryInterface(domdoc);
+
+ if (doc && doc->IsHTMLDocument()) {
+ format.mMethod = eHTMLOutput;
+ } else {
+ format.mMethod = eXMLOutput;
+ }
+
+ *aHandler = new txMozillaXMLOutput(&format, mFragment, false);
+ break;
+ }
+
+ case eXMLOutput:
+ case eHTMLOutput:
+ {
+ *aHandler = new txMozillaXMLOutput(aFormat, mFragment, false);
+ break;
+ }
+
+ case eTextOutput:
+ {
+ *aHandler = new txMozillaTextOutput(mFragment);
+ break;
+ }
+ }
+ NS_ENSURE_TRUE(*aHandler, NS_ERROR_OUT_OF_MEMORY);
+ return NS_OK;
+}
+
+nsresult
+txToFragmentHandlerFactory::createHandlerWith(txOutputFormat* aFormat,
+ const nsSubstring& aName,
+ int32_t aNsID,
+ txAXMLEventHandler** aHandler)
+{
+ *aHandler = nullptr;
+ NS_ASSERTION(aFormat->mMethod != eMethodNotSet,
+ "How can method not be known when root element is?");
+ NS_ENSURE_TRUE(aFormat->mMethod != eMethodNotSet, NS_ERROR_UNEXPECTED);
+ return createHandlerWith(aFormat, aHandler);
+}
+
+class txVariable : public txIGlobalParameter
+{
+public:
+ explicit txVariable(nsIVariant* aValue) : mValue(aValue)
+ {
+ NS_ASSERTION(aValue, "missing value");
+ }
+ explicit txVariable(txAExprResult* aValue) : mTxValue(aValue)
+ {
+ NS_ASSERTION(aValue, "missing value");
+ }
+ nsresult getValue(txAExprResult** aValue)
+ {
+ NS_ASSERTION(mValue || mTxValue, "variablevalue is null");
+
+ if (!mTxValue) {
+ nsresult rv = Convert(mValue, getter_AddRefs(mTxValue));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ *aValue = mTxValue;
+ NS_ADDREF(*aValue);
+
+ return NS_OK;
+ }
+ nsresult getValue(nsIVariant** aValue)
+ {
+ *aValue = mValue;
+ NS_ADDREF(*aValue);
+ return NS_OK;
+ }
+ nsIVariant* getValue()
+ {
+ return mValue;
+ }
+ void setValue(nsIVariant* aValue)
+ {
+ NS_ASSERTION(aValue, "setting variablevalue to null");
+ mValue = aValue;
+ mTxValue = nullptr;
+ }
+ void setValue(txAExprResult* aValue)
+ {
+ NS_ASSERTION(aValue, "setting variablevalue to null");
+ mValue = nullptr;
+ mTxValue = aValue;
+ }
+
+ friend void ImplCycleCollectionUnlink(txVariable& aVariable);
+ friend void ImplCycleCollectionTraverse(
+ nsCycleCollectionTraversalCallback& aCallback, txVariable& aVariable,
+ const char* aName, uint32_t aFlags);
+
+private:
+ static nsresult Convert(nsIVariant *aValue, txAExprResult** aResult);
+
+ nsCOMPtr<nsIVariant> mValue;
+ RefPtr<txAExprResult> mTxValue;
+};
+
+inline void
+ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
+ txVariable& aVariable, const char* aName,
+ uint32_t aFlags)
+{
+ ImplCycleCollectionTraverse(aCallback, aVariable.mValue, aName, aFlags);
+}
+
+inline void
+ImplCycleCollectionUnlink(txOwningExpandedNameMap<txIGlobalParameter>& aMap)
+{
+ aMap.clear();
+}
+
+inline void
+ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
+ txOwningExpandedNameMap<txIGlobalParameter>& aMap,
+ const char* aName,
+ uint32_t aFlags = 0)
+{
+ aFlags |= CycleCollectionEdgeNameArrayFlag;
+ txOwningExpandedNameMap<txIGlobalParameter>::iterator iter(aMap);
+ while (iter.next()) {
+ ImplCycleCollectionTraverse(aCallback,
+ *static_cast<txVariable*>(iter.value()),
+ aName, aFlags);
+ }
+}
+
+/**
+ * txMozillaXSLTProcessor
+ */
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(txMozillaXSLTProcessor,
+ mOwner, mEmbeddedStylesheetRoot,
+ mSource, mVariables)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(txMozillaXSLTProcessor)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(txMozillaXSLTProcessor)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(txMozillaXSLTProcessor)
+ NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+ NS_INTERFACE_MAP_ENTRY(nsIXSLTProcessor)
+ NS_INTERFACE_MAP_ENTRY(nsIXSLTProcessorPrivate)
+ NS_INTERFACE_MAP_ENTRY(nsIDocumentTransformer)
+ NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXSLTProcessor)
+NS_INTERFACE_MAP_END
+
+txMozillaXSLTProcessor::txMozillaXSLTProcessor()
+ : mOwner(nullptr),
+ mStylesheetDocument(nullptr),
+ mTransformResult(NS_OK),
+ mCompileResult(NS_OK),
+ mFlags(0)
+{
+}
+
+txMozillaXSLTProcessor::txMozillaXSLTProcessor(nsISupports* aOwner)
+ : mOwner(aOwner),
+ mStylesheetDocument(nullptr),
+ mTransformResult(NS_OK),
+ mCompileResult(NS_OK),
+ mFlags(0)
+{
+}
+
+txMozillaXSLTProcessor::~txMozillaXSLTProcessor()
+{
+ if (mStylesheetDocument) {
+ mStylesheetDocument->RemoveMutationObserver(this);
+ }
+}
+
+NS_IMETHODIMP
+txMozillaXSLTProcessor::SetTransformObserver(nsITransformObserver* aObserver)
+{
+ mObserver = aObserver;
+ return NS_OK;
+}
+
+nsresult
+txMozillaXSLTProcessor::SetSourceContentModel(nsIDocument* aDocument,
+ const nsTArray<nsCOMPtr<nsIContent>>& aSource)
+{
+ if (NS_FAILED(mTransformResult)) {
+ notifyError();
+ return NS_OK;
+ }
+
+ mSource = aDocument->CreateDocumentFragment();
+
+ ErrorResult rv;
+ for (nsIContent* child : aSource) {
+ // XPath data model doesn't have DocumentType nodes.
+ if (child->NodeType() != nsIDOMNode::DOCUMENT_TYPE_NODE) {
+ mSource->AppendChild(*child, rv);
+ if (rv.Failed()) {
+ return rv.StealNSResult();
+ }
+ }
+ }
+
+ if (mStylesheet) {
+ return DoTransform();
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+txMozillaXSLTProcessor::AddXSLTParamNamespace(const nsString& aPrefix,
+ const nsString& aNamespace)
+{
+ nsCOMPtr<nsIAtom> pre = NS_Atomize(aPrefix);
+ return mParamNamespaceMap.mapNamespace(pre, aNamespace);
+}
+
+
+class txXSLTParamContext : public txIParseContext,
+ public txIEvalContext
+{
+public:
+ txXSLTParamContext(txNamespaceMap *aResolver, const txXPathNode& aContext,
+ txResultRecycler* aRecycler)
+ : mResolver(aResolver),
+ mContext(aContext),
+ mRecycler(aRecycler)
+ {
+ }
+
+ // txIParseContext
+ nsresult resolveNamespacePrefix(nsIAtom* aPrefix, int32_t& aID)
+ {
+ aID = mResolver->lookupNamespace(aPrefix);
+ return aID == kNameSpaceID_Unknown ? NS_ERROR_DOM_NAMESPACE_ERR :
+ NS_OK;
+ }
+ nsresult resolveFunctionCall(nsIAtom* aName, int32_t aID,
+ FunctionCall** aFunction)
+ {
+ return NS_ERROR_XPATH_UNKNOWN_FUNCTION;
+ }
+ bool caseInsensitiveNameTests()
+ {
+ return false;
+ }
+ void SetErrorOffset(uint32_t aOffset)
+ {
+ }
+
+ // txIEvalContext
+ nsresult getVariable(int32_t aNamespace, nsIAtom* aLName,
+ txAExprResult*& aResult)
+ {
+ aResult = nullptr;
+ return NS_ERROR_INVALID_ARG;
+ }
+ bool isStripSpaceAllowed(const txXPathNode& aNode)
+ {
+ return false;
+ }
+ void* getPrivateContext()
+ {
+ return nullptr;
+ }
+ txResultRecycler* recycler()
+ {
+ return mRecycler;
+ }
+ void receiveError(const nsAString& aMsg, nsresult aRes)
+ {
+ }
+ const txXPathNode& getContextNode()
+ {
+ return mContext;
+ }
+ uint32_t size()
+ {
+ return 1;
+ }
+ uint32_t position()
+ {
+ return 1;
+ }
+
+private:
+ txNamespaceMap *mResolver;
+ const txXPathNode& mContext;
+ txResultRecycler* mRecycler;
+};
+
+
+NS_IMETHODIMP
+txMozillaXSLTProcessor::AddXSLTParam(const nsString& aName,
+ const nsString& aNamespace,
+ const nsString& aSelect,
+ const nsString& aValue,
+ nsIDOMNode* aContext)
+{
+ nsresult rv = NS_OK;
+
+ if (aSelect.IsVoid() == aValue.IsVoid()) {
+ // Ignore if neither or both are specified
+ return NS_ERROR_FAILURE;
+ }
+
+ RefPtr<txAExprResult> value;
+ if (!aSelect.IsVoid()) {
+
+ // Set up context
+ nsAutoPtr<txXPathNode> contextNode(
+ txXPathNativeNode::createXPathNode(aContext));
+ NS_ENSURE_TRUE(contextNode, NS_ERROR_OUT_OF_MEMORY);
+
+ if (!mRecycler) {
+ mRecycler = new txResultRecycler;
+ }
+
+ txXSLTParamContext paramContext(&mParamNamespaceMap, *contextNode,
+ mRecycler);
+
+ // Parse
+ nsAutoPtr<Expr> expr;
+ rv = txExprParser::createExpr(aSelect, &paramContext,
+ getter_Transfers(expr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Evaluate
+ rv = expr->evaluate(&paramContext, getter_AddRefs(value));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ else {
+ value = new StringResult(aValue, nullptr);
+ }
+
+ nsCOMPtr<nsIAtom> name = NS_Atomize(aName);
+ int32_t nsId = kNameSpaceID_Unknown;
+ rv = nsContentUtils::NameSpaceManager()->
+ RegisterNameSpace(aNamespace, nsId);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ txExpandedName varName(nsId, name);
+ txVariable* var = static_cast<txVariable*>(mVariables.get(varName));
+ if (var) {
+ var->setValue(value);
+
+ return NS_OK;
+ }
+
+ var = new txVariable(value);
+ NS_ENSURE_TRUE(var, NS_ERROR_OUT_OF_MEMORY);
+
+ return mVariables.add(varName, var);
+}
+
+class nsTransformBlockerEvent : public mozilla::Runnable {
+public:
+ RefPtr<txMozillaXSLTProcessor> mProcessor;
+
+ explicit nsTransformBlockerEvent(txMozillaXSLTProcessor* processor)
+ : mProcessor(processor)
+ {}
+
+ ~nsTransformBlockerEvent()
+ {
+ nsCOMPtr<nsIDocument> document = mProcessor->GetSourceContentModel()->OwnerDoc();
+ document->UnblockOnload(true);
+ }
+
+ NS_IMETHOD Run() override
+ {
+ mProcessor->TransformToDoc(nullptr, false);
+ return NS_OK;
+ }
+};
+
+nsresult
+txMozillaXSLTProcessor::DoTransform()
+{
+ NS_ENSURE_TRUE(mSource, NS_ERROR_UNEXPECTED);
+ NS_ENSURE_TRUE(mStylesheet, NS_ERROR_UNEXPECTED);
+ NS_ASSERTION(mObserver, "no observer");
+ NS_ASSERTION(NS_IsMainThread(), "should only be on main thread");
+
+ nsCOMPtr<nsIRunnable> event = new nsTransformBlockerEvent(this);
+ mSource->OwnerDoc()->BlockOnload();
+ nsresult rv = NS_DispatchToCurrentThread(event);
+ if (NS_FAILED(rv)) {
+ // XXX Maybe we should just display the source document in this case?
+ // Also, set up context information, see bug 204655.
+ reportError(rv, nullptr, nullptr);
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+txMozillaXSLTProcessor::ImportStylesheet(nsIDOMNode *aStyle)
+{
+ NS_ENSURE_TRUE(aStyle, NS_ERROR_NULL_POINTER);
+
+ // We don't support importing multiple stylesheets yet.
+ NS_ENSURE_TRUE(!mStylesheetDocument && !mStylesheet,
+ NS_ERROR_NOT_IMPLEMENTED);
+
+ nsCOMPtr<nsINode> node = do_QueryInterface(aStyle);
+ if (!node || !nsContentUtils::SubjectPrincipalOrSystemIfNativeCaller()->Subsumes(node->NodePrincipal())) {
+ return NS_ERROR_DOM_SECURITY_ERR;
+ }
+
+ nsCOMPtr<nsINode> styleNode = do_QueryInterface(aStyle);
+ NS_ENSURE_TRUE(styleNode &&
+ (styleNode->IsElement() ||
+ styleNode->IsNodeOfType(nsINode::eDOCUMENT)),
+ NS_ERROR_INVALID_ARG);
+
+ nsresult rv = TX_CompileStylesheet(styleNode, this,
+ getter_AddRefs(mStylesheet));
+ // XXX set up exception context, bug 204658
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (styleNode->IsElement()) {
+ mStylesheetDocument = styleNode->OwnerDoc();
+ NS_ENSURE_TRUE(mStylesheetDocument, NS_ERROR_UNEXPECTED);
+
+ mEmbeddedStylesheetRoot = static_cast<nsIContent*>(styleNode.get());
+ }
+ else {
+ mStylesheetDocument = static_cast<nsIDocument*>(styleNode.get());
+ }
+
+ mStylesheetDocument->AddMutationObserver(this);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+txMozillaXSLTProcessor::TransformToDocument(nsIDOMNode *aSource,
+ nsIDOMDocument **aResult)
+{
+ NS_ENSURE_ARG(aSource);
+ NS_ENSURE_ARG_POINTER(aResult);
+ NS_ENSURE_SUCCESS(mCompileResult, mCompileResult);
+
+ if (!nsContentUtils::CanCallerAccess(aSource)) {
+ return NS_ERROR_DOM_SECURITY_ERR;
+ }
+
+ nsresult rv = ensureStylesheet();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mSource = do_QueryInterface(aSource);
+
+ return TransformToDoc(aResult, true);
+}
+
+nsresult
+txMozillaXSLTProcessor::TransformToDoc(nsIDOMDocument **aResult,
+ bool aCreateDataDocument)
+{
+ nsAutoPtr<txXPathNode> sourceNode(txXPathNativeNode::createXPathNode(mSource));
+ if (!sourceNode) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ nsCOMPtr<nsIDOMDocument> sourceDOMDocument = do_QueryInterface(mSource->OwnerDoc());
+
+ txExecutionState es(mStylesheet, IsLoadDisabled());
+
+ // XXX Need to add error observers
+
+ // If aResult is non-null, we're a data document
+ txToDocHandlerFactory handlerFactory(&es, sourceDOMDocument, mObserver,
+ aCreateDataDocument);
+ es.mOutputHandlerFactory = &handlerFactory;
+
+ nsresult rv = es.init(*sourceNode, &mVariables);
+
+ // Process root of XML source document
+ if (NS_SUCCEEDED(rv)) {
+ rv = txXSLTProcessor::execute(es);
+ }
+
+ nsresult endRv = es.end(rv);
+ if (NS_SUCCEEDED(rv)) {
+ rv = endRv;
+ }
+
+ if (NS_SUCCEEDED(rv)) {
+ if (aResult) {
+ txAOutputXMLEventHandler* handler =
+ static_cast<txAOutputXMLEventHandler*>(es.mOutputHandler);
+ handler->getOutputDocument(aResult);
+ nsCOMPtr<nsIDocument> doc = do_QueryInterface(*aResult);
+ MOZ_ASSERT(doc->GetReadyStateEnum() ==
+ nsIDocument::READYSTATE_INTERACTIVE, "Bad readyState");
+ doc->SetReadyStateInternal(nsIDocument::READYSTATE_COMPLETE);
+ }
+ }
+ else if (mObserver) {
+ // XXX set up context information, bug 204655
+ reportError(rv, nullptr, nullptr);
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+txMozillaXSLTProcessor::TransformToFragment(nsIDOMNode *aSource,
+ nsIDOMDocument *aOutput,
+ nsIDOMDocumentFragment **aResult)
+{
+ NS_ENSURE_ARG(aSource);
+ NS_ENSURE_ARG(aOutput);
+ NS_ENSURE_ARG_POINTER(aResult);
+ NS_ENSURE_SUCCESS(mCompileResult, mCompileResult);
+
+ nsCOMPtr<nsINode> node = do_QueryInterface(aSource);
+ nsCOMPtr<nsIDocument> doc = do_QueryInterface(aOutput);
+ NS_ENSURE_TRUE(node && doc, NS_ERROR_DOM_SECURITY_ERR);
+ nsIPrincipal* subject = nsContentUtils::SubjectPrincipalOrSystemIfNativeCaller();
+ if (!subject->Subsumes(node->NodePrincipal()) ||
+ !subject->Subsumes(doc->NodePrincipal()))
+ {
+ return NS_ERROR_DOM_SECURITY_ERR;
+ }
+
+ nsresult rv = ensureStylesheet();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoPtr<txXPathNode> sourceNode(txXPathNativeNode::createXPathNode(aSource));
+ if (!sourceNode) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ txExecutionState es(mStylesheet, IsLoadDisabled());
+
+ // XXX Need to add error observers
+
+ rv = aOutput->CreateDocumentFragment(aResult);
+ NS_ENSURE_SUCCESS(rv, rv);
+ txToFragmentHandlerFactory handlerFactory(*aResult);
+ es.mOutputHandlerFactory = &handlerFactory;
+
+ rv = es.init(*sourceNode, &mVariables);
+
+ // Process root of XML source document
+ if (NS_SUCCEEDED(rv)) {
+ rv = txXSLTProcessor::execute(es);
+ }
+ // XXX setup exception context, bug 204658
+ nsresult endRv = es.end(rv);
+ if (NS_SUCCEEDED(rv)) {
+ rv = endRv;
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+txMozillaXSLTProcessor::SetParameter(const nsAString & aNamespaceURI,
+ const nsAString & aLocalName,
+ nsIVariant *aValue)
+{
+ NS_ENSURE_ARG(aValue);
+
+ nsCOMPtr<nsIVariant> value = aValue;
+
+ uint16_t dataType;
+ value->GetDataType(&dataType);
+ switch (dataType) {
+ // Number
+ case nsIDataType::VTYPE_INT8:
+ case nsIDataType::VTYPE_INT16:
+ case nsIDataType::VTYPE_INT32:
+ case nsIDataType::VTYPE_INT64:
+ case nsIDataType::VTYPE_UINT8:
+ case nsIDataType::VTYPE_UINT16:
+ case nsIDataType::VTYPE_UINT32:
+ case nsIDataType::VTYPE_UINT64:
+ case nsIDataType::VTYPE_FLOAT:
+ case nsIDataType::VTYPE_DOUBLE:
+
+ // Boolean
+ case nsIDataType::VTYPE_BOOL:
+
+ // String
+ case nsIDataType::VTYPE_CHAR:
+ case nsIDataType::VTYPE_WCHAR:
+ case nsIDataType::VTYPE_DOMSTRING:
+ case nsIDataType::VTYPE_CHAR_STR:
+ case nsIDataType::VTYPE_WCHAR_STR:
+ case nsIDataType::VTYPE_STRING_SIZE_IS:
+ case nsIDataType::VTYPE_WSTRING_SIZE_IS:
+ case nsIDataType::VTYPE_UTF8STRING:
+ case nsIDataType::VTYPE_CSTRING:
+ case nsIDataType::VTYPE_ASTRING:
+ {
+ break;
+ }
+
+ // Nodeset
+ case nsIDataType::VTYPE_INTERFACE:
+ case nsIDataType::VTYPE_INTERFACE_IS:
+ {
+ nsCOMPtr<nsISupports> supports;
+ nsresult rv = value->GetAsISupports(getter_AddRefs(supports));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIDOMNode> node = do_QueryInterface(supports);
+ if (node) {
+ if (!nsContentUtils::CanCallerAccess(node)) {
+ return NS_ERROR_DOM_SECURITY_ERR;
+ }
+
+ break;
+ }
+
+ nsCOMPtr<nsIXPathResult> xpathResult = do_QueryInterface(supports);
+ if (xpathResult) {
+ RefPtr<txAExprResult> result;
+ nsresult rv = xpathResult->GetExprResult(getter_AddRefs(result));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (result->getResultType() == txAExprResult::NODESET) {
+ txNodeSet *nodeSet =
+ static_cast<txNodeSet*>
+ (static_cast<txAExprResult*>(result));
+
+ nsCOMPtr<nsIDOMNode> node;
+ int32_t i, count = nodeSet->size();
+ for (i = 0; i < count; ++i) {
+ rv = txXPathNativeNode::getNode(nodeSet->get(i),
+ getter_AddRefs(node));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!nsContentUtils::CanCallerAccess(node)) {
+ return NS_ERROR_DOM_SECURITY_ERR;
+ }
+ }
+ }
+
+ // Clone the XPathResult so that mutations don't affect this
+ // variable.
+ nsCOMPtr<nsIXPathResult> clone;
+ rv = xpathResult->Clone(getter_AddRefs(clone));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ RefPtr<nsVariant> variant = new nsVariant();
+
+ rv = variant->SetAsISupports(clone);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ value = variant;
+
+ break;
+ }
+
+ nsCOMPtr<nsIDOMNodeList> nodeList = do_QueryInterface(supports);
+ if (nodeList) {
+ uint32_t length;
+ nodeList->GetLength(&length);
+
+ nsCOMPtr<nsIDOMNode> node;
+ uint32_t i;
+ for (i = 0; i < length; ++i) {
+ nodeList->Item(i, getter_AddRefs(node));
+
+ if (!nsContentUtils::CanCallerAccess(node)) {
+ return NS_ERROR_DOM_SECURITY_ERR;
+ }
+ }
+
+ break;
+ }
+
+ // Random JS Objects will be converted to a string.
+ nsCOMPtr<nsIXPConnectJSObjectHolder> holder =
+ do_QueryInterface(supports);
+ if (holder) {
+ break;
+ }
+
+ // We don't know how to handle this type of param.
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ case nsIDataType::VTYPE_ARRAY:
+ {
+ uint16_t type;
+ nsIID iid;
+ uint32_t count;
+ void* array;
+ nsresult rv = value->GetAsArray(&type, &iid, &count, &array);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (type != nsIDataType::VTYPE_INTERFACE &&
+ type != nsIDataType::VTYPE_INTERFACE_IS) {
+ free(array);
+
+ // We only support arrays of DOM nodes.
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ nsISupports** values = static_cast<nsISupports**>(array);
+
+ uint32_t i;
+ for (i = 0; i < count; ++i) {
+ nsISupports *supports = values[i];
+ nsCOMPtr<nsIDOMNode> node = do_QueryInterface(supports);
+
+ if (node) {
+ rv = nsContentUtils::CanCallerAccess(node) ? NS_OK :
+ NS_ERROR_DOM_SECURITY_ERR;
+ }
+ else {
+ // We only support arrays of DOM nodes.
+ rv = NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ if (NS_FAILED(rv)) {
+ while (i < count) {
+ NS_IF_RELEASE(values[i]);
+ ++i;
+ }
+ free(array);
+
+ return rv;
+ }
+
+ NS_RELEASE(supports);
+ }
+
+ free(array);
+
+ break;
+ }
+
+ default:
+ {
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ int32_t nsId = kNameSpaceID_Unknown;
+ nsresult rv = nsContentUtils::NameSpaceManager()->
+ RegisterNameSpace(aNamespaceURI, nsId);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIAtom> localName = NS_Atomize(aLocalName);
+ txExpandedName varName(nsId, localName);
+
+ txVariable* var = static_cast<txVariable*>(mVariables.get(varName));
+ if (var) {
+ var->setValue(value);
+ return NS_OK;
+ }
+
+ var = new txVariable(value);
+ return mVariables.add(varName, var);
+}
+
+NS_IMETHODIMP
+txMozillaXSLTProcessor::GetParameter(const nsAString& aNamespaceURI,
+ const nsAString& aLocalName,
+ nsIVariant **aResult)
+{
+ int32_t nsId = kNameSpaceID_Unknown;
+ nsresult rv = nsContentUtils::NameSpaceManager()->
+ RegisterNameSpace(aNamespaceURI, nsId);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIAtom> localName = NS_Atomize(aLocalName);
+ txExpandedName varName(nsId, localName);
+
+ txVariable* var = static_cast<txVariable*>(mVariables.get(varName));
+ if (var) {
+ return var->getValue(aResult);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+txMozillaXSLTProcessor::RemoveParameter(const nsAString& aNamespaceURI,
+ const nsAString& aLocalName)
+{
+ int32_t nsId = kNameSpaceID_Unknown;
+ nsresult rv = nsContentUtils::NameSpaceManager()->
+ RegisterNameSpace(aNamespaceURI, nsId);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIAtom> localName = NS_Atomize(aLocalName);
+ txExpandedName varName(nsId, localName);
+
+ mVariables.remove(varName);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+txMozillaXSLTProcessor::ClearParameters()
+{
+ mVariables.clear();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+txMozillaXSLTProcessor::Reset()
+{
+ if (mStylesheetDocument) {
+ mStylesheetDocument->RemoveMutationObserver(this);
+ }
+ mStylesheet = nullptr;
+ mStylesheetDocument = nullptr;
+ mEmbeddedStylesheetRoot = nullptr;
+ mCompileResult = NS_OK;
+ mVariables.clear();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+txMozillaXSLTProcessor::SetFlags(uint32_t aFlags)
+{
+ NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(),
+ NS_ERROR_DOM_SECURITY_ERR);
+
+ mFlags = aFlags;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+txMozillaXSLTProcessor::GetFlags(uint32_t* aFlags)
+{
+ NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(),
+ NS_ERROR_DOM_SECURITY_ERR);
+
+ *aFlags = mFlags;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+txMozillaXSLTProcessor::LoadStyleSheet(nsIURI* aUri,
+ nsIDocument* aLoaderDocument)
+{
+ mozilla::net::ReferrerPolicy refpol = mozilla::net::RP_Default;
+ if (mStylesheetDocument) {
+ refpol = mStylesheetDocument->GetReferrerPolicy();
+ }
+
+ nsresult rv = TX_LoadSheet(aUri, this, aLoaderDocument, refpol);
+ if (NS_FAILED(rv) && mObserver) {
+ // This is most likely a network or security error, just
+ // use the uri as context.
+ nsAutoCString spec;
+ aUri->GetSpec(spec);
+ CopyUTF8toUTF16(spec, mSourceText);
+ nsresult status = NS_ERROR_GET_MODULE(rv) == NS_ERROR_MODULE_XSLT ? rv :
+ NS_ERROR_XSLT_NETWORK_ERROR;
+ reportError(status, nullptr, nullptr);
+ }
+ return rv;
+}
+
+nsresult
+txMozillaXSLTProcessor::setStylesheet(txStylesheet* aStylesheet)
+{
+ mStylesheet = aStylesheet;
+ if (mSource) {
+ return DoTransform();
+ }
+ return NS_OK;
+}
+
+void
+txMozillaXSLTProcessor::reportError(nsresult aResult,
+ const char16_t *aErrorText,
+ const char16_t *aSourceText)
+{
+ if (!mObserver) {
+ return;
+ }
+
+ mTransformResult = aResult;
+
+ if (aErrorText) {
+ mErrorText.Assign(aErrorText);
+ }
+ else {
+ nsCOMPtr<nsIStringBundleService> sbs =
+ mozilla::services::GetStringBundleService();
+ if (sbs) {
+ nsXPIDLString errorText;
+ sbs->FormatStatusMessage(aResult, EmptyString().get(),
+ getter_Copies(errorText));
+
+ nsXPIDLString errorMessage;
+ nsCOMPtr<nsIStringBundle> bundle;
+ sbs->CreateBundle(XSLT_MSGS_URL, getter_AddRefs(bundle));
+
+ if (bundle) {
+ const char16_t* error[] = { errorText.get() };
+ if (mStylesheet) {
+ bundle->FormatStringFromName(u"TransformError",
+ error, 1,
+ getter_Copies(errorMessage));
+ }
+ else {
+ bundle->FormatStringFromName(u"LoadingError",
+ error, 1,
+ getter_Copies(errorMessage));
+ }
+ }
+ mErrorText.Assign(errorMessage);
+ }
+ }
+
+ if (aSourceText) {
+ mSourceText.Assign(aSourceText);
+ }
+
+ if (mSource) {
+ notifyError();
+ }
+}
+
+void
+txMozillaXSLTProcessor::notifyError()
+{
+ nsresult rv;
+ nsCOMPtr<nsIDOMDocument> errorDocument = do_CreateInstance(kXMLDocumentCID,
+ &rv);
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ // Set up the document
+ nsCOMPtr<nsIDocument> document = do_QueryInterface(errorDocument);
+ if (!document) {
+ return;
+ }
+ URIUtils::ResetWithSource(document, mSource);
+
+ MOZ_ASSERT(document->GetReadyStateEnum() ==
+ nsIDocument::READYSTATE_UNINITIALIZED,
+ "Bad readyState.");
+ document->SetReadyStateInternal(nsIDocument::READYSTATE_LOADING);
+
+ NS_NAMED_LITERAL_STRING(ns, "http://www.mozilla.org/newlayout/xml/parsererror.xml");
+
+ nsCOMPtr<nsIDOMElement> element;
+ rv = errorDocument->CreateElementNS(ns, NS_LITERAL_STRING("parsererror"),
+ getter_AddRefs(element));
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ nsCOMPtr<nsIDOMNode> resultNode;
+ rv = errorDocument->AppendChild(element, getter_AddRefs(resultNode));
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ nsCOMPtr<nsIDOMText> text;
+ rv = errorDocument->CreateTextNode(mErrorText, getter_AddRefs(text));
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ rv = element->AppendChild(text, getter_AddRefs(resultNode));
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ if (!mSourceText.IsEmpty()) {
+ nsCOMPtr<nsIDOMElement> sourceElement;
+ rv = errorDocument->CreateElementNS(ns,
+ NS_LITERAL_STRING("sourcetext"),
+ getter_AddRefs(sourceElement));
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ rv = element->AppendChild(sourceElement, getter_AddRefs(resultNode));
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ rv = errorDocument->CreateTextNode(mSourceText, getter_AddRefs(text));
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ rv = sourceElement->AppendChild(text, getter_AddRefs(resultNode));
+ if (NS_FAILED(rv)) {
+ return;
+ }
+ }
+
+ MOZ_ASSERT(document->GetReadyStateEnum() ==
+ nsIDocument::READYSTATE_LOADING,
+ "Bad readyState.");
+ document->SetReadyStateInternal(nsIDocument::READYSTATE_INTERACTIVE);
+
+ mObserver->OnTransformDone(mTransformResult, document);
+}
+
+nsresult
+txMozillaXSLTProcessor::ensureStylesheet()
+{
+ if (mStylesheet) {
+ return NS_OK;
+ }
+
+ NS_ENSURE_TRUE(mStylesheetDocument, NS_ERROR_NOT_INITIALIZED);
+
+ nsINode* style = mEmbeddedStylesheetRoot;
+ if (!style) {
+ style = mStylesheetDocument;
+ }
+
+ return TX_CompileStylesheet(style, this, getter_AddRefs(mStylesheet));
+}
+
+void
+txMozillaXSLTProcessor::NodeWillBeDestroyed(const nsINode* aNode)
+{
+ nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
+ if (NS_FAILED(mCompileResult)) {
+ return;
+ }
+
+ mCompileResult = ensureStylesheet();
+ mStylesheetDocument = nullptr;
+ mEmbeddedStylesheetRoot = nullptr;
+}
+
+void
+txMozillaXSLTProcessor::CharacterDataChanged(nsIDocument* aDocument,
+ nsIContent *aContent,
+ CharacterDataChangeInfo* aInfo)
+{
+ mStylesheet = nullptr;
+}
+
+void
+txMozillaXSLTProcessor::AttributeChanged(nsIDocument* aDocument,
+ Element* aElement,
+ int32_t aNameSpaceID,
+ nsIAtom* aAttribute,
+ int32_t aModType,
+ const nsAttrValue* aOldValue)
+{
+ mStylesheet = nullptr;
+}
+
+void
+txMozillaXSLTProcessor::ContentAppended(nsIDocument* aDocument,
+ nsIContent* aContainer,
+ nsIContent* aFirstNewContent,
+ int32_t /* unused */)
+{
+ mStylesheet = nullptr;
+}
+
+void
+txMozillaXSLTProcessor::ContentInserted(nsIDocument* aDocument,
+ nsIContent* aContainer,
+ nsIContent* aChild,
+ int32_t /* unused */)
+{
+ mStylesheet = nullptr;
+}
+
+void
+txMozillaXSLTProcessor::ContentRemoved(nsIDocument* aDocument,
+ nsIContent* aContainer,
+ nsIContent* aChild,
+ int32_t aIndexInContainer,
+ nsIContent* aPreviousSibling)
+{
+ mStylesheet = nullptr;
+}
+
+/* virtual */ JSObject*
+txMozillaXSLTProcessor::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+ return XSLTProcessorBinding::Wrap(aCx, this, aGivenProto);
+}
+
+
+/* static */ already_AddRefed<txMozillaXSLTProcessor>
+txMozillaXSLTProcessor::Constructor(const GlobalObject& aGlobal,
+ mozilla::ErrorResult& aRv)
+{
+ RefPtr<txMozillaXSLTProcessor> processor =
+ new txMozillaXSLTProcessor(aGlobal.GetAsSupports());
+ return processor.forget();
+}
+
+void
+txMozillaXSLTProcessor::ImportStylesheet(nsINode& stylesheet,
+ mozilla::ErrorResult& aRv)
+{
+ aRv = ImportStylesheet(stylesheet.AsDOMNode());
+}
+
+already_AddRefed<DocumentFragment>
+txMozillaXSLTProcessor::TransformToFragment(nsINode& source,
+ nsIDocument& docVal,
+ mozilla::ErrorResult& aRv)
+{
+ nsCOMPtr<nsIDOMDocumentFragment> fragment;
+ nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(&docVal);
+ if (!domDoc) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return nullptr;
+ }
+ aRv = TransformToFragment(source.AsDOMNode(), domDoc, getter_AddRefs(fragment));
+ return fragment.forget().downcast<DocumentFragment>();
+}
+
+already_AddRefed<nsIDocument>
+txMozillaXSLTProcessor::TransformToDocument(nsINode& source,
+ mozilla::ErrorResult& aRv)
+{
+ nsCOMPtr<nsIDOMDocument> document;
+ aRv = TransformToDocument(source.AsDOMNode(), getter_AddRefs(document));
+ nsCOMPtr<nsIDocument> domDoc = do_QueryInterface(document);
+ return domDoc.forget();
+}
+
+void
+txMozillaXSLTProcessor::SetParameter(JSContext* aCx,
+ const nsAString& aNamespaceURI,
+ const nsAString& aLocalName,
+ JS::Handle<JS::Value> aValue,
+ mozilla::ErrorResult& aRv)
+{
+ nsCOMPtr<nsIVariant> val;
+ aRv = nsContentUtils::XPConnect()->JSToVariant(aCx, aValue,
+ getter_AddRefs(val));
+ if (aRv.Failed()) {
+ return;
+ }
+ aRv = SetParameter(aNamespaceURI, aLocalName, val);
+}
+
+nsIVariant*
+txMozillaXSLTProcessor::GetParameter(const nsAString& aNamespaceURI,
+ const nsAString& aLocalName,
+ mozilla::ErrorResult& aRv)
+{
+ nsCOMPtr<nsIVariant> val;
+ aRv = GetParameter(aNamespaceURI, aLocalName, getter_AddRefs(val));
+ return val;
+}
+
+/* static*/
+nsresult
+txMozillaXSLTProcessor::Startup()
+{
+ if (!txXSLTProcessor::init()) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ nsCOMPtr<nsIErrorService> errorService =
+ do_GetService(NS_ERRORSERVICE_CONTRACTID);
+ if (errorService) {
+ errorService->RegisterErrorStringBundle(NS_ERROR_MODULE_XSLT,
+ XSLT_MSGS_URL);
+ }
+
+ return NS_OK;
+}
+
+/* static*/
+void
+txMozillaXSLTProcessor::Shutdown()
+{
+ txXSLTProcessor::shutdown();
+
+ nsCOMPtr<nsIErrorService> errorService =
+ do_GetService(NS_ERRORSERVICE_CONTRACTID);
+ if (errorService) {
+ errorService->UnregisterErrorStringBundle(NS_ERROR_MODULE_XSLT);
+ }
+}
+
+/* static*/
+nsresult
+txVariable::Convert(nsIVariant *aValue, txAExprResult** aResult)
+{
+ *aResult = nullptr;
+
+ uint16_t dataType;
+ aValue->GetDataType(&dataType);
+ switch (dataType) {
+ // Number
+ case nsIDataType::VTYPE_INT8:
+ case nsIDataType::VTYPE_INT16:
+ case nsIDataType::VTYPE_INT32:
+ case nsIDataType::VTYPE_INT64:
+ case nsIDataType::VTYPE_UINT8:
+ case nsIDataType::VTYPE_UINT16:
+ case nsIDataType::VTYPE_UINT32:
+ case nsIDataType::VTYPE_UINT64:
+ case nsIDataType::VTYPE_FLOAT:
+ case nsIDataType::VTYPE_DOUBLE:
+ {
+ double value;
+ nsresult rv = aValue->GetAsDouble(&value);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ *aResult = new NumberResult(value, nullptr);
+ NS_ADDREF(*aResult);
+
+ return NS_OK;
+ }
+
+ // Boolean
+ case nsIDataType::VTYPE_BOOL:
+ {
+ bool value;
+ nsresult rv = aValue->GetAsBool(&value);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ *aResult = new BooleanResult(value);
+ NS_ADDREF(*aResult);
+
+ return NS_OK;
+ }
+
+ // String
+ case nsIDataType::VTYPE_CHAR:
+ case nsIDataType::VTYPE_WCHAR:
+ case nsIDataType::VTYPE_DOMSTRING:
+ case nsIDataType::VTYPE_CHAR_STR:
+ case nsIDataType::VTYPE_WCHAR_STR:
+ case nsIDataType::VTYPE_STRING_SIZE_IS:
+ case nsIDataType::VTYPE_WSTRING_SIZE_IS:
+ case nsIDataType::VTYPE_UTF8STRING:
+ case nsIDataType::VTYPE_CSTRING:
+ case nsIDataType::VTYPE_ASTRING:
+ {
+ nsAutoString value;
+ nsresult rv = aValue->GetAsAString(value);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ *aResult = new StringResult(value, nullptr);
+ NS_ADDREF(*aResult);
+
+ return NS_OK;
+ }
+
+ // Nodeset
+ case nsIDataType::VTYPE_INTERFACE:
+ case nsIDataType::VTYPE_INTERFACE_IS:
+ {
+ nsCOMPtr<nsISupports> supports;
+ nsresult rv = aValue->GetAsISupports(getter_AddRefs(supports));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIDOMNode> node = do_QueryInterface(supports);
+ if (node) {
+ nsAutoPtr<txXPathNode> xpathNode(txXPathNativeNode::createXPathNode(node));
+ if (!xpathNode) {
+ return NS_ERROR_FAILURE;
+ }
+
+ *aResult = new txNodeSet(*xpathNode, nullptr);
+ if (!*aResult) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ NS_ADDREF(*aResult);
+
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIXPathResult> xpathResult = do_QueryInterface(supports);
+ if (xpathResult) {
+ return xpathResult->GetExprResult(aResult);
+ }
+
+ nsCOMPtr<nsIDOMNodeList> nodeList = do_QueryInterface(supports);
+ if (nodeList) {
+ RefPtr<txNodeSet> nodeSet = new txNodeSet(nullptr);
+ if (!nodeSet) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ uint32_t length;
+ nodeList->GetLength(&length);
+
+ nsCOMPtr<nsIDOMNode> node;
+ uint32_t i;
+ for (i = 0; i < length; ++i) {
+ nodeList->Item(i, getter_AddRefs(node));
+
+ nsAutoPtr<txXPathNode> xpathNode(
+ txXPathNativeNode::createXPathNode(node));
+ if (!xpathNode) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nodeSet->add(*xpathNode);
+ }
+
+ NS_ADDREF(*aResult = nodeSet);
+
+ return NS_OK;
+ }
+
+ // Convert random JS Objects to a string.
+ nsCOMPtr<nsIXPConnectJSObjectHolder> holder =
+ do_QueryInterface(supports);
+ if (holder) {
+ JSContext* cx = nsContentUtils::GetCurrentJSContext();
+ NS_ENSURE_TRUE(cx, NS_ERROR_NOT_AVAILABLE);
+
+ JS::Rooted<JSObject*> jsobj(cx, holder->GetJSObject());
+ NS_ENSURE_STATE(jsobj);
+
+ JS::Rooted<JS::Value> v(cx, JS::ObjectValue(*jsobj));
+ JS::Rooted<JSString*> str(cx, JS::ToString(cx, v));
+ NS_ENSURE_TRUE(str, NS_ERROR_FAILURE);
+
+ nsAutoJSString value;
+ NS_ENSURE_TRUE(value.init(cx, str), NS_ERROR_FAILURE);
+
+ *aResult = new StringResult(value, nullptr);
+ NS_ADDREF(*aResult);
+
+ return NS_OK;
+ }
+
+ break;
+ }
+
+ case nsIDataType::VTYPE_ARRAY:
+ {
+ uint16_t type;
+ nsIID iid;
+ uint32_t count;
+ void* array;
+ nsresult rv = aValue->GetAsArray(&type, &iid, &count, &array);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_ASSERTION(type == nsIDataType::VTYPE_INTERFACE ||
+ type == nsIDataType::VTYPE_INTERFACE_IS,
+ "Huh, we checked this in SetParameter?");
+
+ nsISupports** values = static_cast<nsISupports**>(array);
+
+ RefPtr<txNodeSet> nodeSet = new txNodeSet(nullptr);
+ if (!nodeSet) {
+ NS_FREE_XPCOM_ISUPPORTS_POINTER_ARRAY(count, values);
+
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ uint32_t i;
+ for (i = 0; i < count; ++i) {
+ nsISupports *supports = values[i];
+ nsCOMPtr<nsIDOMNode> node = do_QueryInterface(supports);
+ NS_ASSERTION(node, "Huh, we checked this in SetParameter?");
+
+ nsAutoPtr<txXPathNode> xpathNode(
+ txXPathNativeNode::createXPathNode(node));
+ if (!xpathNode) {
+ while (i < count) {
+ NS_RELEASE(values[i]);
+ ++i;
+ }
+ free(array);
+
+ return NS_ERROR_FAILURE;
+ }
+
+ nodeSet->add(*xpathNode);
+
+ NS_RELEASE(supports);
+ }
+
+ free(array);
+
+ NS_ADDREF(*aResult = nodeSet);
+
+ return NS_OK;
+ }
+ }
+
+ return NS_ERROR_ILLEGAL_VALUE;
+}
diff --git a/dom/xslt/xslt/txMozillaXSLTProcessor.h b/dom/xslt/xslt/txMozillaXSLTProcessor.h
new file mode 100644
index 000000000..93d3c2c30
--- /dev/null
+++ b/dom/xslt/xslt/txMozillaXSLTProcessor.h
@@ -0,0 +1,196 @@
+/* -*- 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_TXMOZILLAXSLTPROCESSOR_H
+#define TRANSFRMX_TXMOZILLAXSLTPROCESSOR_H
+
+#include "nsAutoPtr.h"
+#include "nsStubMutationObserver.h"
+#include "nsIDocumentTransformer.h"
+#include "nsIXSLTProcessor.h"
+#include "nsIXSLTProcessorPrivate.h"
+#include "txExpandedNameMap.h"
+#include "txNamespaceMap.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsWrapperCache.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/ErrorResult.h"
+#include "mozilla/net/ReferrerPolicy.h"
+
+class nsINode;
+class nsIDOMNode;
+class nsIURI;
+class txStylesheet;
+class txResultRecycler;
+class txIGlobalParameter;
+
+namespace mozilla {
+namespace dom {
+
+class Document;
+class DocumentFragment;
+class GlobalObject;
+
+} // namespace dom
+} // namespace mozilla
+
+/* bacd8ad0-552f-11d3-a9f7-000064657374 */
+#define TRANSFORMIIX_XSLT_PROCESSOR_CID \
+{ 0x618ee71d, 0xd7a7, 0x41a1, {0xa3, 0xfb, 0xc2, 0xbe, 0xdc, 0x6a, 0x21, 0x7e} }
+
+#define TRANSFORMIIX_XSLT_PROCESSOR_CONTRACTID \
+"@mozilla.org/document-transformer;1?type=xslt"
+
+#define XSLT_MSGS_URL "chrome://global/locale/xslt/xslt.properties"
+
+/**
+ * txMozillaXSLTProcessor is a front-end to the XSLT Processor.
+ */
+class txMozillaXSLTProcessor final : public nsIXSLTProcessor,
+ public nsIXSLTProcessorPrivate,
+ public nsIDocumentTransformer,
+ public nsStubMutationObserver,
+ public nsWrapperCache
+{
+public:
+ /**
+ * Creates a new txMozillaXSLTProcessor
+ */
+ txMozillaXSLTProcessor();
+
+ // nsISupports interface
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(txMozillaXSLTProcessor,
+ nsIXSLTProcessor)
+
+ // nsIXSLTProcessor interface
+ NS_DECL_NSIXSLTPROCESSOR
+
+ // nsIXSLTProcessorPrivate interface
+ NS_DECL_NSIXSLTPROCESSORPRIVATE
+
+ // nsIDocumentTransformer interface
+ NS_IMETHOD SetTransformObserver(nsITransformObserver* aObserver) override;
+ NS_IMETHOD LoadStyleSheet(nsIURI* aUri, nsIDocument* aLoaderDocument) override;
+ NS_IMETHOD SetSourceContentModel(nsIDocument* aDocument,
+ const nsTArray<nsCOMPtr<nsIContent>>& aSource) override;
+ NS_IMETHOD CancelLoads() override {return NS_OK;}
+ NS_IMETHOD AddXSLTParamNamespace(const nsString& aPrefix,
+ const nsString& aNamespace) override;
+ NS_IMETHOD AddXSLTParam(const nsString& aName,
+ const nsString& aNamespace,
+ const nsString& aSelect,
+ const nsString& aValue,
+ nsIDOMNode* aContext) override;
+
+ // 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
+
+ // nsWrapperCache
+ virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+
+ // WebIDL
+ nsISupports*
+ GetParentObject() const
+ {
+ return mOwner;
+ }
+
+ static already_AddRefed<txMozillaXSLTProcessor>
+ Constructor(const mozilla::dom::GlobalObject& aGlobal,
+ mozilla::ErrorResult& aRv);
+
+ void ImportStylesheet(nsINode& stylesheet,
+ mozilla::ErrorResult& aRv);
+ already_AddRefed<mozilla::dom::DocumentFragment>
+ TransformToFragment(nsINode& source, nsIDocument& docVal, mozilla::ErrorResult& aRv);
+ already_AddRefed<nsIDocument>
+ TransformToDocument(nsINode& source, mozilla::ErrorResult& aRv);
+
+ void SetParameter(JSContext* aCx,
+ const nsAString& aNamespaceURI,
+ const nsAString& aLocalName,
+ JS::Handle<JS::Value> aValue,
+ mozilla::ErrorResult& aRv);
+ nsIVariant* GetParameter(const nsAString& aNamespaceURI,
+ const nsAString& aLocalName,
+ mozilla::ErrorResult& aRv);
+ void RemoveParameter(const nsAString& aNamespaceURI,
+ const nsAString& aLocalName,
+ mozilla::ErrorResult& aRv)
+ {
+ aRv = RemoveParameter(aNamespaceURI, aLocalName);
+ }
+
+ uint32_t Flags()
+ {
+ uint32_t flags;
+ GetFlags(&flags);
+ return flags;
+ }
+
+ nsresult setStylesheet(txStylesheet* aStylesheet);
+ void reportError(nsresult aResult, const char16_t *aErrorText,
+ const char16_t *aSourceText);
+
+ nsINode *GetSourceContentModel()
+ {
+ return mSource;
+ }
+
+ nsresult TransformToDoc(nsIDOMDocument **aResult,
+ bool aCreateDataDocument);
+
+ bool IsLoadDisabled()
+ {
+ return (mFlags & DISABLE_ALL_LOADS) != 0;
+ }
+
+ static nsresult Startup();
+ static void Shutdown();
+
+private:
+ explicit txMozillaXSLTProcessor(nsISupports* aOwner);
+ /**
+ * Default destructor for txMozillaXSLTProcessor
+ */
+ ~txMozillaXSLTProcessor();
+
+ nsresult DoTransform();
+ void notifyError();
+ nsresult ensureStylesheet();
+
+ nsCOMPtr<nsISupports> mOwner;
+
+ RefPtr<txStylesheet> mStylesheet;
+ nsIDocument* mStylesheetDocument; // weak
+ nsCOMPtr<nsIContent> mEmbeddedStylesheetRoot;
+
+ nsCOMPtr<nsINode> mSource;
+ nsresult mTransformResult;
+ nsresult mCompileResult;
+ nsString mErrorText, mSourceText;
+ nsCOMPtr<nsITransformObserver> mObserver;
+ txOwningExpandedNameMap<txIGlobalParameter> mVariables;
+ txNamespaceMap mParamNamespaceMap;
+ RefPtr<txResultRecycler> mRecycler;
+
+ uint32_t mFlags;
+};
+
+extern nsresult TX_LoadSheet(nsIURI* aUri, txMozillaXSLTProcessor* aProcessor,
+ nsIDocument* aLoaderDocument,
+ mozilla::net::ReferrerPolicy aReferrerPolicy);
+
+extern nsresult TX_CompileStylesheet(nsINode* aNode,
+ txMozillaXSLTProcessor* aProcessor,
+ txStylesheet** aStylesheet);
+
+#endif
diff --git a/dom/xslt/xslt/txNodeSorter.cpp b/dom/xslt/xslt/txNodeSorter.cpp
new file mode 100644
index 000000000..cf1d61f6d
--- /dev/null
+++ b/dom/xslt/xslt/txNodeSorter.cpp
@@ -0,0 +1,260 @@
+/* -*- 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 "txNodeSorter.h"
+#include "txExecutionState.h"
+#include "txXPathResultComparator.h"
+#include "nsGkAtoms.h"
+#include "txNodeSetContext.h"
+#include "txExpr.h"
+#include "txStringUtils.h"
+#include "prmem.h"
+#include "nsQuickSort.h"
+
+/*
+ * Sorts Nodes as specified by the W3C XSLT 1.0 Recommendation
+ */
+
+txNodeSorter::txNodeSorter() : mNKeys(0)
+{
+}
+
+txNodeSorter::~txNodeSorter()
+{
+ txListIterator iter(&mSortKeys);
+ while (iter.hasNext()) {
+ SortKey* key = (SortKey*)iter.next();
+ delete key->mComparator;
+ delete key;
+ }
+}
+
+nsresult
+txNodeSorter::addSortElement(Expr* aSelectExpr, Expr* aLangExpr,
+ Expr* aDataTypeExpr, Expr* aOrderExpr,
+ Expr* aCaseOrderExpr, txIEvalContext* aContext)
+{
+ nsAutoPtr<SortKey> key(new SortKey);
+ nsresult rv = NS_OK;
+
+ // Select
+ key->mExpr = aSelectExpr;
+
+ // Order
+ bool ascending = true;
+ if (aOrderExpr) {
+ nsAutoString attrValue;
+ rv = aOrderExpr->evaluateToString(aContext, attrValue);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (TX_StringEqualsAtom(attrValue, nsGkAtoms::descending)) {
+ ascending = false;
+ }
+ else if (!TX_StringEqualsAtom(attrValue, nsGkAtoms::ascending)) {
+ // XXX ErrorReport: unknown value for order attribute
+ return NS_ERROR_XSLT_BAD_VALUE;
+ }
+ }
+
+
+ // Create comparator depending on datatype
+ nsAutoString dataType;
+ if (aDataTypeExpr) {
+ rv = aDataTypeExpr->evaluateToString(aContext, dataType);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ if (!aDataTypeExpr || TX_StringEqualsAtom(dataType, nsGkAtoms::text)) {
+ // Text comparator
+
+ // Language
+ nsAutoString lang;
+ if (aLangExpr) {
+ rv = aLangExpr->evaluateToString(aContext, lang);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // Case-order
+ bool upperFirst = false;
+ if (aCaseOrderExpr) {
+ nsAutoString attrValue;
+
+ rv = aCaseOrderExpr->evaluateToString(aContext, attrValue);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (TX_StringEqualsAtom(attrValue, nsGkAtoms::upperFirst)) {
+ upperFirst = true;
+ }
+ else if (!TX_StringEqualsAtom(attrValue,
+ nsGkAtoms::lowerFirst)) {
+ // XXX ErrorReport: unknown value for case-order attribute
+ return NS_ERROR_XSLT_BAD_VALUE;
+ }
+ }
+
+ key->mComparator = new txResultStringComparator(ascending,
+ upperFirst,
+ lang);
+ }
+ else if (TX_StringEqualsAtom(dataType, nsGkAtoms::number)) {
+ // Number comparator
+ key->mComparator = new txResultNumberComparator(ascending);
+ }
+ else {
+ // XXX ErrorReport: unknown data-type
+ return NS_ERROR_XSLT_BAD_VALUE;
+ }
+
+ // mSortKeys owns key now.
+ rv = mSortKeys.add(key);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ key.forget();
+ mNKeys++;
+
+ return NS_OK;
+}
+
+nsresult
+txNodeSorter::sortNodeSet(txNodeSet* aNodes, txExecutionState* aEs,
+ txNodeSet** aResult)
+{
+ if (mNKeys == 0 || aNodes->isEmpty()) {
+ NS_ADDREF(*aResult = aNodes);
+
+ return NS_OK;
+ }
+
+ *aResult = nullptr;
+
+ RefPtr<txNodeSet> sortedNodes;
+ nsresult rv = aEs->recycler()->getNodeSet(getter_AddRefs(sortedNodes));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ txNodeSetContext* evalContext = new txNodeSetContext(aNodes, aEs);
+ NS_ENSURE_TRUE(evalContext, NS_ERROR_OUT_OF_MEMORY);
+
+ rv = aEs->pushEvalContext(evalContext);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Create and set up memoryblock for sort-values and indexarray
+ uint32_t len = static_cast<uint32_t>(aNodes->size());
+
+ // Limit resource use to something sane.
+ uint32_t itemSize = sizeof(uint32_t) + mNKeys * sizeof(txObject*);
+ if (mNKeys > (UINT32_MAX - sizeof(uint32_t)) / sizeof(txObject*) ||
+ len >= UINT32_MAX / itemSize) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ void* mem = PR_Malloc(len * itemSize);
+ NS_ENSURE_TRUE(mem, NS_ERROR_OUT_OF_MEMORY);
+
+ uint32_t* indexes = static_cast<uint32_t*>(mem);
+ txObject** sortValues = reinterpret_cast<txObject**>(indexes + len);
+
+ uint32_t i;
+ for (i = 0; i < len; ++i) {
+ indexes[i] = i;
+ }
+ memset(sortValues, 0, len * mNKeys * sizeof(txObject*));
+
+ // Sort the indexarray
+ SortData sortData;
+ sortData.mNodeSorter = this;
+ sortData.mContext = evalContext;
+ sortData.mSortValues = sortValues;
+ sortData.mRv = NS_OK;
+ NS_QuickSort(indexes, len, sizeof(uint32_t), compareNodes, &sortData);
+
+ // Delete these here so we don't have to deal with them at every possible
+ // failurepoint
+ uint32_t numSortValues = len * mNKeys;
+ for (i = 0; i < numSortValues; ++i) {
+ delete sortValues[i];
+ }
+
+ if (NS_FAILED(sortData.mRv)) {
+ PR_Free(mem);
+ // The txExecutionState owns the evalcontext so no need to handle it
+ return sortData.mRv;
+ }
+
+ // Insert nodes in sorted order in new nodeset
+ for (i = 0; i < len; ++i) {
+ rv = sortedNodes->append(aNodes->get(indexes[i]));
+ if (NS_FAILED(rv)) {
+ PR_Free(mem);
+ // The txExecutionState owns the evalcontext so no need to handle it
+ return rv;
+ }
+ }
+
+ PR_Free(mem);
+ delete aEs->popEvalContext();
+
+ NS_ADDREF(*aResult = sortedNodes);
+
+ return NS_OK;
+}
+
+// static
+int
+txNodeSorter::compareNodes(const void* aIndexA, const void* aIndexB,
+ void* aSortData)
+{
+ SortData* sortData = static_cast<SortData*>(aSortData);
+ NS_ENSURE_SUCCESS(sortData->mRv, -1);
+
+ txListIterator iter(&sortData->mNodeSorter->mSortKeys);
+ uint32_t indexA = *static_cast<const uint32_t*>(aIndexA);
+ uint32_t indexB = *static_cast<const uint32_t*>(aIndexB);
+ txObject** sortValuesA = sortData->mSortValues +
+ indexA * sortData->mNodeSorter->mNKeys;
+ txObject** sortValuesB = sortData->mSortValues +
+ indexB * sortData->mNodeSorter->mNKeys;
+
+ unsigned int i;
+ // Step through each key until a difference is found
+ for (i = 0; i < sortData->mNodeSorter->mNKeys; ++i) {
+ SortKey* key = (SortKey*)iter.next();
+ // Lazy create sort values
+ if (!sortValuesA[i] &&
+ !calcSortValue(sortValuesA[i], key, sortData, indexA)) {
+ return -1;
+ }
+ if (!sortValuesB[i] &&
+ !calcSortValue(sortValuesB[i], key, sortData, indexB)) {
+ return -1;
+ }
+
+ // Compare node values
+ int compRes = key->mComparator->compareValues(sortValuesA[i],
+ sortValuesB[i]);
+ if (compRes != 0)
+ return compRes;
+ }
+ // All keys have the same value for these nodes
+
+ return indexA - indexB;
+}
+
+//static
+bool
+txNodeSorter::calcSortValue(txObject*& aSortValue, SortKey* aKey,
+ SortData* aSortData, uint32_t aNodeIndex)
+{
+ aSortData->mContext->setPosition(aNodeIndex + 1); // position is 1-based
+
+ nsresult rv = aKey->mComparator->createSortableValue(aKey->mExpr,
+ aSortData->mContext,
+ aSortValue);
+ if (NS_FAILED(rv)) {
+ aSortData->mRv = rv;
+ return false;
+ }
+
+ return true;
+}
diff --git a/dom/xslt/xslt/txNodeSorter.h b/dom/xslt/xslt/txNodeSorter.h
new file mode 100644
index 000000000..a3c2b73ab
--- /dev/null
+++ b/dom/xslt/xslt/txNodeSorter.h
@@ -0,0 +1,58 @@
+/* -*- 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_NODESORTER_H
+#define TRANSFRMX_NODESORTER_H
+
+#include "txCore.h"
+#include "txList.h"
+
+class Expr;
+class txExecutionState;
+class txNodeSet;
+class txObject;
+class txXPathResultComparator;
+class txIEvalContext;
+class txNodeSetContext;
+
+/*
+ * Sorts Nodes as specified by the W3C XSLT 1.0 Recommendation
+ */
+
+class txNodeSorter
+{
+public:
+ txNodeSorter();
+ ~txNodeSorter();
+
+ nsresult addSortElement(Expr* aSelectExpr, Expr* aLangExpr,
+ Expr* aDataTypeExpr, Expr* aOrderExpr,
+ Expr* aCaseOrderExpr, txIEvalContext* aContext);
+ nsresult sortNodeSet(txNodeSet* aNodes, txExecutionState* aEs,
+ txNodeSet** aResult);
+
+private:
+ struct SortData
+ {
+ txNodeSorter* mNodeSorter;
+ txNodeSetContext* mContext;
+ txObject** mSortValues;
+ nsresult mRv;
+ };
+ struct SortKey
+ {
+ Expr* mExpr;
+ txXPathResultComparator* mComparator;
+ };
+
+ static int compareNodes(const void* aIndexA, const void* aIndexB,
+ void* aSortData);
+ static bool calcSortValue(txObject*& aSortValue, SortKey* aKey,
+ SortData* aSortData, uint32_t aNodeIndex);
+ txList mSortKeys;
+ unsigned int mNKeys;
+};
+
+#endif
diff --git a/dom/xslt/xslt/txOutputFormat.cpp b/dom/xslt/xslt/txOutputFormat.cpp
new file mode 100644
index 000000000..dcf28f5cb
--- /dev/null
+++ b/dom/xslt/xslt/txOutputFormat.cpp
@@ -0,0 +1,132 @@
+/* -*- 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 "txOutputFormat.h"
+#include "txXMLUtils.h"
+#include "txExpandedName.h"
+
+txOutputFormat::txOutputFormat() : mMethod(eMethodNotSet),
+ mOmitXMLDeclaration(eNotSet),
+ mStandalone(eNotSet),
+ mIndent(eNotSet)
+{
+}
+
+txOutputFormat::~txOutputFormat()
+{
+ txListIterator iter(&mCDATASectionElements);
+ while (iter.hasNext())
+ delete (txExpandedName*)iter.next();
+}
+
+void txOutputFormat::reset()
+{
+ mMethod = eMethodNotSet;
+ mVersion.Truncate();
+ if (mEncoding.IsEmpty())
+ mOmitXMLDeclaration = eNotSet;
+ mStandalone = eNotSet;
+ mPublicId.Truncate();
+ mSystemId.Truncate();
+ txListIterator iter(&mCDATASectionElements);
+ while (iter.hasNext())
+ delete (txExpandedName*)iter.next();
+ mIndent = eNotSet;
+ mMediaType.Truncate();
+}
+
+void txOutputFormat::merge(txOutputFormat& aOutputFormat)
+{
+ if (mMethod == eMethodNotSet)
+ mMethod = aOutputFormat.mMethod;
+
+ if (mVersion.IsEmpty())
+ mVersion = aOutputFormat.mVersion;
+
+ if (mEncoding.IsEmpty())
+ mEncoding = aOutputFormat.mEncoding;
+
+ if (mOmitXMLDeclaration == eNotSet)
+ mOmitXMLDeclaration = aOutputFormat.mOmitXMLDeclaration;
+
+ if (mStandalone == eNotSet)
+ mStandalone = aOutputFormat.mStandalone;
+
+ if (mPublicId.IsEmpty())
+ mPublicId = aOutputFormat.mPublicId;
+
+ if (mSystemId.IsEmpty())
+ mSystemId = aOutputFormat.mSystemId;
+
+ txListIterator iter(&aOutputFormat.mCDATASectionElements);
+ txExpandedName* qName;
+ while ((qName = (txExpandedName*)iter.next())) {
+ mCDATASectionElements.add(qName);
+ // XXX We need txList.clear()
+ iter.remove();
+ }
+
+ if (mIndent == eNotSet)
+ mIndent = aOutputFormat.mIndent;
+
+ if (mMediaType.IsEmpty())
+ mMediaType = aOutputFormat.mMediaType;
+}
+
+void txOutputFormat::setFromDefaults()
+{
+ switch (mMethod) {
+ case eMethodNotSet:
+ {
+ mMethod = eXMLOutput;
+ MOZ_FALLTHROUGH;
+ }
+ case eXMLOutput:
+ {
+ if (mVersion.IsEmpty())
+ mVersion.AppendLiteral("1.0");
+
+ if (mEncoding.IsEmpty())
+ mEncoding.AppendLiteral("UTF-8");
+
+ if (mOmitXMLDeclaration == eNotSet)
+ mOmitXMLDeclaration = eFalse;
+
+ if (mIndent == eNotSet)
+ mIndent = eFalse;
+
+ if (mMediaType.IsEmpty())
+ mMediaType.AppendLiteral("text/xml");
+
+ break;
+ }
+ case eHTMLOutput:
+ {
+ if (mVersion.IsEmpty())
+ mVersion.AppendLiteral("4.0");
+
+ if (mEncoding.IsEmpty())
+ mEncoding.AppendLiteral("UTF-8");
+
+ if (mIndent == eNotSet)
+ mIndent = eTrue;
+
+ if (mMediaType.IsEmpty())
+ mMediaType.AppendLiteral("text/html");
+
+ break;
+ }
+ case eTextOutput:
+ {
+ if (mEncoding.IsEmpty())
+ mEncoding.AppendLiteral("UTF-8");
+
+ if (mMediaType.IsEmpty())
+ mMediaType.AppendLiteral("text/plain");
+
+ break;
+ }
+ }
+}
diff --git a/dom/xslt/xslt/txOutputFormat.h b/dom/xslt/xslt/txOutputFormat.h
new file mode 100644
index 000000000..6ff4e22ca
--- /dev/null
+++ b/dom/xslt/xslt/txOutputFormat.h
@@ -0,0 +1,73 @@
+/* -*- 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_OUTPUTFORMAT_H
+#define TRANSFRMX_OUTPUTFORMAT_H
+
+#include "txList.h"
+#include "nsString.h"
+
+enum txOutputMethod {
+ eMethodNotSet,
+ eXMLOutput,
+ eHTMLOutput,
+ eTextOutput
+};
+
+enum txThreeState {
+ eNotSet,
+ eFalse,
+ eTrue
+};
+
+class txOutputFormat {
+public:
+ txOutputFormat();
+ ~txOutputFormat();
+
+ // "Unset" all values
+ void reset();
+
+ // Merges in the values of aOutputFormat, members that already
+ // have a value in this txOutputFormat will not be changed.
+ void merge(txOutputFormat& aOutputFormat);
+
+ // Sets members that have no value to their default value.
+ void setFromDefaults();
+
+ // The XSLT output method, which can be "xml", "html", or "text"
+ txOutputMethod mMethod;
+
+ // The xml version number that should be used when serializing
+ // xml documents
+ nsString mVersion;
+
+ // The XML character encoding that should be used when serializing
+ // xml documents
+ nsString mEncoding;
+
+ // Signals if we should output an XML declaration
+ txThreeState mOmitXMLDeclaration;
+
+ // Signals if we should output a standalone document declaration
+ txThreeState mStandalone;
+
+ // The public Id for creating a DOCTYPE
+ nsString mPublicId;
+
+ // The System Id for creating a DOCTYPE
+ nsString mSystemId;
+
+ // The elements whose text node children should be output as CDATA
+ txList mCDATASectionElements;
+
+ // Signals if output should be indented
+ txThreeState mIndent;
+
+ // The media type of the output
+ nsString mMediaType;
+};
+
+#endif
diff --git a/dom/xslt/xslt/txPatternOptimizer.cpp b/dom/xslt/xslt/txPatternOptimizer.cpp
new file mode 100644
index 000000000..633676ba4
--- /dev/null
+++ b/dom/xslt/xslt/txPatternOptimizer.cpp
@@ -0,0 +1,76 @@
+/* -*- 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 "txPatternOptimizer.h"
+#include "txXSLTPatterns.h"
+
+nsresult
+txPatternOptimizer::optimize(txPattern* aInPattern, txPattern** aOutPattern)
+{
+ *aOutPattern = nullptr;
+ nsresult rv = NS_OK;
+
+ // First optimize sub expressions
+ uint32_t i = 0;
+ Expr* subExpr;
+ while ((subExpr = aInPattern->getSubExprAt(i))) {
+ Expr* newExpr = nullptr;
+ rv = mXPathOptimizer.optimize(subExpr, &newExpr);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (newExpr) {
+ delete subExpr;
+ aInPattern->setSubExprAt(i, newExpr);
+ }
+
+ ++i;
+ }
+
+ // Then optimize sub patterns
+ txPattern* subPattern;
+ i = 0;
+ while ((subPattern = aInPattern->getSubPatternAt(i))) {
+ txPattern* newPattern = nullptr;
+ rv = optimize(subPattern, &newPattern);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (newPattern) {
+ delete subPattern;
+ aInPattern->setSubPatternAt(i, newPattern);
+ }
+
+ ++i;
+ }
+
+ // Finally see if current pattern can be optimized
+ switch (aInPattern->getType()) {
+ case txPattern::STEP_PATTERN:
+ return optimizeStep(aInPattern, aOutPattern);
+
+ default:
+ break;
+ }
+
+ return NS_OK;
+}
+
+
+nsresult
+txPatternOptimizer::optimizeStep(txPattern* aInPattern,
+ txPattern** aOutPattern)
+{
+ txStepPattern* step = static_cast<txStepPattern*>(aInPattern);
+
+ // 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;
+}
diff --git a/dom/xslt/xslt/txPatternOptimizer.h b/dom/xslt/xslt/txPatternOptimizer.h
new file mode 100644
index 000000000..58ec95375
--- /dev/null
+++ b/dom/xslt/xslt/txPatternOptimizer.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 txPatternOptimizer_h__
+#define txPatternOptimizer_h__
+
+#include "txXPathOptimizer.h"
+
+class txPattern;
+
+class txPatternOptimizer
+{
+public:
+ /**
+ * Optimize the given pattern.
+ * @param aInPattern Pattern to optimize.
+ * @param aOutPattern Resulting pattern, null if optimization didn't
+ * result in a new pattern.
+ */
+ nsresult optimize(txPattern* aInPattern, txPattern** aOutPattern);
+
+private:
+
+ // Helper methods for optimizing specific classes
+ nsresult optimizeStep(txPattern* aInPattern, txPattern** aOutPattern);
+
+ txXPathOptimizer mXPathOptimizer;
+};
+
+#endif //txPatternOptimizer_h__
diff --git a/dom/xslt/xslt/txPatternParser.cpp b/dom/xslt/xslt/txPatternParser.cpp
new file mode 100644
index 000000000..f902aa22c
--- /dev/null
+++ b/dom/xslt/xslt/txPatternParser.cpp
@@ -0,0 +1,307 @@
+/* -*- 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 "txPatternParser.h"
+#include "txExprLexer.h"
+#include "nsGkAtoms.h"
+#include "nsError.h"
+#include "txStringUtils.h"
+#include "txXSLTPatterns.h"
+#include "txStylesheetCompiler.h"
+#include "txPatternOptimizer.h"
+
+
+nsresult txPatternParser::createPattern(const nsAFlatString& aPattern,
+ txIParseContext* aContext,
+ txPattern** aResult)
+{
+ txExprLexer lexer;
+ nsresult rv = lexer.parse(aPattern);
+ if (NS_FAILED(rv)) {
+ // XXX error report parsing error
+ return rv;
+ }
+ nsAutoPtr<txPattern> pattern;
+ rv = createUnionPattern(lexer, aContext, *getter_Transfers(pattern));
+ if (NS_FAILED(rv)) {
+ // XXX error report parsing error
+ return rv;
+ }
+
+ txPatternOptimizer optimizer;
+ txPattern* newPattern = nullptr;
+ rv = optimizer.optimize(pattern, &newPattern);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ *aResult = newPattern ? newPattern : pattern.forget();
+
+ return NS_OK;
+}
+
+nsresult txPatternParser::createUnionPattern(txExprLexer& aLexer,
+ txIParseContext* aContext,
+ txPattern*& aPattern)
+{
+ nsresult rv = NS_OK;
+ txPattern* locPath = 0;
+
+ rv = createLocPathPattern(aLexer, aContext, locPath);
+ if (NS_FAILED(rv))
+ return rv;
+
+ Token::Type type = aLexer.peek()->mType;
+ if (type == Token::END) {
+ aPattern = locPath;
+ return NS_OK;
+ }
+
+ if (type != Token::UNION_OP) {
+ delete locPath;
+ return NS_ERROR_XPATH_PARSE_FAILURE;
+ }
+
+ txUnionPattern* unionPattern = new txUnionPattern();
+ rv = unionPattern->addPattern(locPath);
+#if 0 // XXX addPattern can't fail yet, it doesn't check for mem
+ if (NS_FAILED(rv)) {
+ delete unionPattern;
+ delete locPath;
+ return rv;
+ }
+#endif
+
+ aLexer.nextToken();
+ do {
+ rv = createLocPathPattern(aLexer, aContext, locPath);
+ if (NS_FAILED(rv)) {
+ delete unionPattern;
+ return rv;
+ }
+ rv = unionPattern->addPattern(locPath);
+#if 0 // XXX addPattern can't fail yet, it doesn't check for mem
+ if (NS_FAILED(rv)) {
+ delete unionPattern;
+ delete locPath;
+ return rv;
+ }
+#endif
+ type = aLexer.nextToken()->mType;
+ } while (type == Token::UNION_OP);
+
+ if (type != Token::END) {
+ delete unionPattern;
+ return NS_ERROR_XPATH_PARSE_FAILURE;
+ }
+
+ aPattern = unionPattern;
+ return NS_OK;
+}
+
+nsresult txPatternParser::createLocPathPattern(txExprLexer& aLexer,
+ txIParseContext* aContext,
+ txPattern*& aPattern)
+{
+ nsresult rv = NS_OK;
+
+ bool isChild = true;
+ bool isAbsolute = false;
+ txPattern* stepPattern = 0;
+ txLocPathPattern* pathPattern = 0;
+
+ Token::Type type = aLexer.peek()->mType;
+ switch (type) {
+ case Token::ANCESTOR_OP:
+ isChild = false;
+ isAbsolute = true;
+ aLexer.nextToken();
+ break;
+ case Token::PARENT_OP:
+ aLexer.nextToken();
+ isAbsolute = true;
+ if (aLexer.peek()->mType == Token::END ||
+ aLexer.peek()->mType == Token::UNION_OP) {
+ aPattern = new txRootPattern();
+ return NS_OK;
+ }
+ break;
+ case Token::FUNCTION_NAME_AND_PAREN:
+ // id(Literal) or key(Literal, Literal)
+ {
+ nsCOMPtr<nsIAtom> nameAtom =
+ NS_Atomize(aLexer.nextToken()->Value());
+ if (nameAtom == nsGkAtoms::id) {
+ rv = createIdPattern(aLexer, stepPattern);
+ }
+ else if (nameAtom == nsGkAtoms::key) {
+ rv = createKeyPattern(aLexer, aContext, stepPattern);
+ }
+ if (NS_FAILED(rv))
+ return rv;
+ }
+ break;
+ default:
+ break;
+ }
+ if (!stepPattern) {
+ rv = createStepPattern(aLexer, aContext, stepPattern);
+ if (NS_FAILED(rv))
+ return rv;
+ }
+
+ type = aLexer.peek()->mType;
+ if (!isAbsolute && type != Token::PARENT_OP
+ && type != Token::ANCESTOR_OP) {
+ aPattern = stepPattern;
+ return NS_OK;
+ }
+
+ pathPattern = new txLocPathPattern();
+ if (isAbsolute) {
+ txRootPattern* root = new txRootPattern();
+#ifdef TX_TO_STRING
+ root->setSerialize(false);
+#endif
+
+ rv = pathPattern->addStep(root, isChild);
+ if (NS_FAILED(rv)) {
+ delete stepPattern;
+ delete pathPattern;
+ delete root;
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+
+ rv = pathPattern->addStep(stepPattern, isChild);
+ if (NS_FAILED(rv)) {
+ delete stepPattern;
+ delete pathPattern;
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ stepPattern = 0; // stepPattern is part of pathPattern now
+
+ while (type == Token::PARENT_OP || type == Token::ANCESTOR_OP) {
+ isChild = type == Token::PARENT_OP;
+ aLexer.nextToken();
+ rv = createStepPattern(aLexer, aContext, stepPattern);
+ if (NS_FAILED(rv)) {
+ delete pathPattern;
+ return rv;
+ }
+ rv = pathPattern->addStep(stepPattern, isChild);
+ if (NS_FAILED(rv)) {
+ delete stepPattern;
+ delete pathPattern;
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ stepPattern = 0; // stepPattern is part of pathPattern now
+ type = aLexer.peek()->mType;
+ }
+ aPattern = pathPattern;
+ return rv;
+}
+
+nsresult txPatternParser::createIdPattern(txExprLexer& aLexer,
+ txPattern*& aPattern)
+{
+ // check for '(' Literal ')'
+ if (aLexer.peek()->mType != Token::LITERAL)
+ return NS_ERROR_XPATH_PARSE_FAILURE;
+ const nsDependentSubstring& value =
+ aLexer.nextToken()->Value();
+ if (aLexer.nextToken()->mType != Token::R_PAREN)
+ return NS_ERROR_XPATH_PARSE_FAILURE;
+ aPattern = new txIdPattern(value);
+ return NS_OK;
+}
+
+nsresult txPatternParser::createKeyPattern(txExprLexer& aLexer,
+ txIParseContext* aContext,
+ txPattern*& aPattern)
+{
+ // check for '(' Literal, Literal ')'
+ if (aLexer.peek()->mType != Token::LITERAL)
+ return NS_ERROR_XPATH_PARSE_FAILURE;
+ const nsDependentSubstring& key =
+ aLexer.nextToken()->Value();
+ if (aLexer.nextToken()->mType != Token::COMMA &&
+ aLexer.peek()->mType != Token::LITERAL)
+ return NS_ERROR_XPATH_PARSE_FAILURE;
+ const nsDependentSubstring& value =
+ aLexer.nextToken()->Value();
+ if (aLexer.nextToken()->mType != Token::R_PAREN)
+ return NS_ERROR_XPATH_PARSE_FAILURE;
+
+ if (!aContext->allowed(txIParseContext::KEY_FUNCTION))
+ return NS_ERROR_XSLT_CALL_TO_KEY_NOT_ALLOWED;
+
+ const char16_t* colon;
+ if (!XMLUtils::isValidQName(PromiseFlatString(key), &colon))
+ return NS_ERROR_XPATH_PARSE_FAILURE;
+ nsCOMPtr<nsIAtom> prefix, localName;
+ int32_t namespaceID;
+ nsresult rv = resolveQName(key, getter_AddRefs(prefix), aContext,
+ getter_AddRefs(localName), namespaceID);
+ if (NS_FAILED(rv))
+ return rv;
+
+ aPattern = new txKeyPattern(prefix, localName, namespaceID, value);
+ return NS_OK;
+}
+
+nsresult txPatternParser::createStepPattern(txExprLexer& aLexer,
+ txIParseContext* aContext,
+ txPattern*& aPattern)
+{
+ nsresult rv = NS_OK;
+ bool isAttr = false;
+ Token* tok = aLexer.peek();
+ if (tok->mType == Token::AXIS_IDENTIFIER) {
+ if (TX_StringEqualsAtom(tok->Value(), nsGkAtoms::attribute)) {
+ isAttr = true;
+ }
+ else if (!TX_StringEqualsAtom(tok->Value(), nsGkAtoms::child)) {
+ // all done already for CHILD_AXIS, for all others
+ // XXX report unexpected axis error
+ return NS_ERROR_XPATH_PARSE_FAILURE;
+ }
+ aLexer.nextToken();
+ }
+ else if (tok->mType == Token::AT_SIGN) {
+ aLexer.nextToken();
+ isAttr = true;
+ }
+
+ txNodeTest* nodeTest;
+ if (aLexer.peek()->mType == Token::CNAME) {
+ tok = aLexer.nextToken();
+
+ // resolve QName
+ nsCOMPtr<nsIAtom> prefix, lName;
+ int32_t nspace;
+ rv = resolveQName(tok->Value(), getter_AddRefs(prefix), aContext,
+ getter_AddRefs(lName), nspace, true);
+ if (NS_FAILED(rv)) {
+ // XXX error report namespace resolve failed
+ return rv;
+ }
+
+ uint16_t nodeType = isAttr ?
+ (uint16_t)txXPathNodeType::ATTRIBUTE_NODE :
+ (uint16_t)txXPathNodeType::ELEMENT_NODE;
+ nodeTest = new txNameTest(prefix, lName, nspace, nodeType);
+ }
+ else {
+ rv = createNodeTypeTest(aLexer, &nodeTest);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ nsAutoPtr<txStepPattern> step(new txStepPattern(nodeTest, isAttr));
+ rv = parsePredicates(step, aLexer, aContext);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aPattern = step.forget();
+
+ return NS_OK;
+}
diff --git a/dom/xslt/xslt/txPatternParser.h b/dom/xslt/xslt/txPatternParser.h
new file mode 100644
index 000000000..b654a6342
--- /dev/null
+++ b/dom/xslt/xslt/txPatternParser.h
@@ -0,0 +1,37 @@
+/* -*- 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_PATTERNPARSER_H
+#define TX_PATTERNPARSER_H
+
+#include "txXSLTPatterns.h"
+#include "txExprParser.h"
+
+class txStylesheetCompilerState;
+
+class txPatternParser : public txExprParser
+{
+public:
+ static nsresult createPattern(const nsAFlatString& aPattern,
+ txIParseContext* aContext,
+ txPattern** aResult);
+protected:
+ static nsresult createUnionPattern(txExprLexer& aLexer,
+ txIParseContext* aContext,
+ txPattern*& aPattern);
+ static nsresult createLocPathPattern(txExprLexer& aLexer,
+ txIParseContext* aContext,
+ txPattern*& aPattern);
+ static nsresult createIdPattern(txExprLexer& aLexer,
+ txPattern*& aPattern);
+ static nsresult createKeyPattern(txExprLexer& aLexer,
+ txIParseContext* aContext,
+ txPattern*& aPattern);
+ static nsresult createStepPattern(txExprLexer& aLexer,
+ txIParseContext* aContext,
+ txPattern*& aPattern);
+};
+
+#endif // TX_PATTERNPARSER_H
diff --git a/dom/xslt/xslt/txRtfHandler.cpp b/dom/xslt/xslt/txRtfHandler.cpp
new file mode 100644
index 000000000..3c73e676e
--- /dev/null
+++ b/dom/xslt/xslt/txRtfHandler.cpp
@@ -0,0 +1,79 @@
+/* -*- 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 "txRtfHandler.h"
+#include "mozilla/Move.h"
+
+using mozilla::Move;
+
+txResultTreeFragment::txResultTreeFragment(nsAutoPtr<txResultBuffer>&& aBuffer)
+ : txAExprResult(nullptr),
+ mBuffer(Move(aBuffer))
+{
+}
+
+short txResultTreeFragment::getResultType()
+{
+ return RESULT_TREE_FRAGMENT;
+}
+
+void
+txResultTreeFragment::stringValue(nsString& aResult)
+{
+ if (!mBuffer) {
+ return;
+ }
+
+ aResult.Append(mBuffer->mStringValue);
+}
+
+const nsString*
+txResultTreeFragment::stringValuePointer()
+{
+ return mBuffer ? &mBuffer->mStringValue : nullptr;
+}
+
+bool txResultTreeFragment::booleanValue()
+{
+ return true;
+}
+
+double txResultTreeFragment::numberValue()
+{
+ if (!mBuffer) {
+ return 0;
+ }
+
+ return txDouble::toDouble(mBuffer->mStringValue);
+}
+
+nsresult txResultTreeFragment::flushToHandler(txAXMLEventHandler* aHandler)
+{
+ if (!mBuffer) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return mBuffer->flushToHandler(aHandler);
+}
+
+nsresult
+txRtfHandler::getAsRTF(txAExprResult** aResult)
+{
+ *aResult = new txResultTreeFragment(Move(mBuffer));
+ NS_ADDREF(*aResult);
+ return NS_OK;
+}
+
+nsresult
+txRtfHandler::endDocument(nsresult aResult)
+{
+ return NS_OK;
+}
+
+nsresult
+txRtfHandler::startDocument()
+{
+ return NS_OK;
+}
diff --git a/dom/xslt/xslt/txRtfHandler.h b/dom/xslt/xslt/txRtfHandler.h
new file mode 100644
index 000000000..3212a6bff
--- /dev/null
+++ b/dom/xslt/xslt/txRtfHandler.h
@@ -0,0 +1,48 @@
+/* -*- 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 txRtfHandler_h___
+#define txRtfHandler_h___
+
+#include "mozilla/Attributes.h"
+#include "txBufferingHandler.h"
+#include "txExprResult.h"
+#include "txXPathNode.h"
+
+class txResultTreeFragment : public txAExprResult
+{
+public:
+ explicit txResultTreeFragment(nsAutoPtr<txResultBuffer>&& aBuffer);
+
+ TX_DECL_EXPRRESULT
+
+ nsresult flushToHandler(txAXMLEventHandler* aHandler);
+
+ void setNode(const txXPathNode* aNode)
+ {
+ NS_ASSERTION(!mNode, "Already converted!");
+
+ mNode = aNode;
+ }
+ const txXPathNode *getNode() const
+ {
+ return mNode;
+ }
+
+private:
+ nsAutoPtr<txResultBuffer> mBuffer;
+ nsAutoPtr<const txXPathNode> mNode;
+};
+
+class txRtfHandler : public txBufferingHandler
+{
+public:
+ nsresult getAsRTF(txAExprResult** aResult);
+
+ nsresult endDocument(nsresult aResult) override;
+ nsresult startDocument() override;
+};
+
+#endif /* txRtfHandler_h___ */
diff --git a/dom/xslt/xslt/txStylesheet.cpp b/dom/xslt/xslt/txStylesheet.cpp
new file mode 100644
index 000000000..b680da01f
--- /dev/null
+++ b/dom/xslt/xslt/txStylesheet.cpp
@@ -0,0 +1,591 @@
+/* -*- 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 "mozilla/Move.h"
+
+#include "txStylesheet.h"
+#include "txExpr.h"
+#include "txXSLTPatterns.h"
+#include "txToplevelItems.h"
+#include "txInstructions.h"
+#include "txXSLTFunctions.h"
+#include "txLog.h"
+#include "txKey.h"
+#include "txXPathTreeWalker.h"
+
+using mozilla::LogLevel;
+using mozilla::Move;
+
+txStylesheet::txStylesheet()
+ : mRootFrame(nullptr)
+{
+}
+
+nsresult
+txStylesheet::init()
+{
+ mRootFrame = new ImportFrame;
+
+ // Create default templates
+ // element/root template
+ mContainerTemplate = new txPushParams;
+
+ nsAutoPtr<txNodeTest> nt(new txNodeTypeTest(txNodeTypeTest::NODE_TYPE));
+ nsAutoPtr<Expr> nodeExpr(new LocationStep(nt, LocationStep::CHILD_AXIS));
+ nt.forget();
+
+ txPushNewContext* pushContext = new txPushNewContext(Move(nodeExpr));
+ mContainerTemplate->mNext = pushContext;
+
+ txApplyDefaultElementTemplate* applyTemplates =
+ new txApplyDefaultElementTemplate;
+ pushContext->mNext = applyTemplates;
+
+ txLoopNodeSet* loopNodeSet = new txLoopNodeSet(applyTemplates);
+ applyTemplates->mNext = loopNodeSet;
+
+ txPopParams* popParams = new txPopParams;
+ pushContext->mBailTarget = loopNodeSet->mNext = popParams;
+
+ popParams->mNext = new txReturn();
+
+ // attribute/textnode template
+ nt = new txNodeTypeTest(txNodeTypeTest::NODE_TYPE);
+ nodeExpr = new LocationStep(nt, LocationStep::SELF_AXIS);
+ nt.forget();
+
+ mCharactersTemplate = new txValueOf(Move(nodeExpr), false);
+ mCharactersTemplate->mNext = new txReturn();
+
+ // pi/comment/namespace template
+ mEmptyTemplate = new txReturn();
+
+ return NS_OK;
+}
+
+txStylesheet::~txStylesheet()
+{
+ // Delete all ImportFrames
+ delete mRootFrame;
+ txListIterator frameIter(&mImportFrames);
+ while (frameIter.hasNext()) {
+ delete static_cast<ImportFrame*>(frameIter.next());
+ }
+
+ txListIterator instrIter(&mTemplateInstructions);
+ while (instrIter.hasNext()) {
+ delete static_cast<txInstruction*>(instrIter.next());
+ }
+
+ // We can't make the map own its values because then we wouldn't be able
+ // to merge attributesets of the same name
+ txExpandedNameMap<txInstruction>::iterator attrSetIter(mAttributeSets);
+ while (attrSetIter.next()) {
+ delete attrSetIter.value();
+ }
+}
+
+txInstruction*
+txStylesheet::findTemplate(const txXPathNode& aNode,
+ const txExpandedName& aMode,
+ txIMatchContext* aContext,
+ ImportFrame* aImportedBy,
+ ImportFrame** aImportFrame)
+{
+ NS_ASSERTION(aImportFrame, "missing ImportFrame pointer");
+
+ *aImportFrame = nullptr;
+ txInstruction* matchTemplate = nullptr;
+ ImportFrame* endFrame = nullptr;
+ txListIterator frameIter(&mImportFrames);
+
+ if (aImportedBy) {
+ ImportFrame* curr = static_cast<ImportFrame*>(frameIter.next());
+ while (curr != aImportedBy) {
+ curr = static_cast<ImportFrame*>(frameIter.next());
+ }
+ endFrame = aImportedBy->mFirstNotImported;
+ }
+
+#if defined(TX_TO_STRING)
+ txPattern* match = 0;
+#endif
+
+ ImportFrame* frame;
+ while (!matchTemplate &&
+ (frame = static_cast<ImportFrame*>(frameIter.next())) &&
+ frame != endFrame) {
+
+ // get templatelist for this mode
+ nsTArray<MatchableTemplate>* templates =
+ frame->mMatchableTemplates.get(aMode);
+
+ if (templates) {
+ // Find template with highest priority
+ uint32_t i, len = templates->Length();
+ for (i = 0; i < len && !matchTemplate; ++i) {
+ MatchableTemplate& templ = (*templates)[i];
+ if (templ.mMatch->matches(aNode, aContext)) {
+ matchTemplate = templ.mFirstInstruction;
+ *aImportFrame = frame;
+#if defined(TX_TO_STRING)
+ match = templ.mMatch;
+#endif
+ }
+ }
+ }
+ }
+
+ if (MOZ_LOG_TEST(txLog::xslt, LogLevel::Debug)) {
+ nsAutoString mode, nodeName;
+ if (aMode.mLocalName) {
+ aMode.mLocalName->ToString(mode);
+ }
+ txXPathNodeUtils::getNodeName(aNode, nodeName);
+ if (matchTemplate) {
+ nsAutoString matchAttr;
+#ifdef TX_TO_STRING
+ match->toString(matchAttr);
+#endif
+ MOZ_LOG(txLog::xslt, LogLevel::Debug,
+ ("MatchTemplate, Pattern %s, Mode %s, Node %s\n",
+ NS_LossyConvertUTF16toASCII(matchAttr).get(),
+ NS_LossyConvertUTF16toASCII(mode).get(),
+ NS_LossyConvertUTF16toASCII(nodeName).get()));
+ }
+ else {
+ MOZ_LOG(txLog::xslt, LogLevel::Debug,
+ ("No match, Node %s, Mode %s\n",
+ NS_LossyConvertUTF16toASCII(nodeName).get(),
+ NS_LossyConvertUTF16toASCII(mode).get()));
+ }
+ }
+
+ if (!matchTemplate) {
+ // Test for these first since a node can be both a text node
+ // and a root (if it is orphaned)
+ if (txXPathNodeUtils::isAttribute(aNode) ||
+ txXPathNodeUtils::isText(aNode)) {
+ matchTemplate = mCharactersTemplate;
+ }
+ else if (txXPathNodeUtils::isElement(aNode) ||
+ txXPathNodeUtils::isRoot(aNode)) {
+ matchTemplate = mContainerTemplate;
+ }
+ else {
+ matchTemplate = mEmptyTemplate;
+ }
+ }
+
+ return matchTemplate;
+}
+
+txDecimalFormat*
+txStylesheet::getDecimalFormat(const txExpandedName& aName)
+{
+ return mDecimalFormats.get(aName);
+}
+
+txInstruction*
+txStylesheet::getAttributeSet(const txExpandedName& aName)
+{
+ return mAttributeSets.get(aName);
+}
+
+txInstruction*
+txStylesheet::getNamedTemplate(const txExpandedName& aName)
+{
+ return mNamedTemplates.get(aName);
+}
+
+txOutputFormat*
+txStylesheet::getOutputFormat()
+{
+ return &mOutputFormat;
+}
+
+txStylesheet::GlobalVariable*
+txStylesheet::getGlobalVariable(const txExpandedName& aName)
+{
+ return mGlobalVariables.get(aName);
+}
+
+const txOwningExpandedNameMap<txXSLKey>&
+txStylesheet::getKeyMap()
+{
+ return mKeys;
+}
+
+bool
+txStylesheet::isStripSpaceAllowed(const txXPathNode& aNode, txIMatchContext* aContext)
+{
+ int32_t frameCount = mStripSpaceTests.Length();
+ if (frameCount == 0) {
+ return false;
+ }
+
+ txXPathTreeWalker walker(aNode);
+
+ if (txXPathNodeUtils::isText(walker.getCurrentPosition()) &&
+ (!txXPathNodeUtils::isWhitespace(aNode) || !walker.moveToParent())) {
+ return false;
+ }
+
+ const txXPathNode& node = walker.getCurrentPosition();
+
+ if (!txXPathNodeUtils::isElement(node)) {
+ return false;
+ }
+
+ // check Whitespace stipping handling list against given Node
+ int32_t i;
+ for (i = 0; i < frameCount; ++i) {
+ txStripSpaceTest* sst = mStripSpaceTests[i];
+ if (sst->matches(node, aContext)) {
+ return sst->stripsSpace() && !XMLUtils::getXMLSpacePreserve(node);
+ }
+ }
+
+ return false;
+}
+
+nsresult
+txStylesheet::doneCompiling()
+{
+ nsresult rv = NS_OK;
+ // Collect all importframes into a single ordered list
+ txListIterator frameIter(&mImportFrames);
+ rv = frameIter.addAfter(mRootFrame);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mRootFrame = nullptr;
+ frameIter.next();
+ rv = addFrames(frameIter);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Loop through importframes in decreasing-precedence-order and process
+ // all items
+ frameIter.reset();
+ ImportFrame* frame;
+ while ((frame = static_cast<ImportFrame*>(frameIter.next()))) {
+ nsTArray<txStripSpaceTest*> frameStripSpaceTests;
+
+ txListIterator itemIter(&frame->mToplevelItems);
+ itemIter.resetToEnd();
+ txToplevelItem* item;
+ while ((item = static_cast<txToplevelItem*>(itemIter.previous()))) {
+ switch (item->getType()) {
+ case txToplevelItem::attributeSet:
+ {
+ rv = addAttributeSet(static_cast<txAttributeSetItem*>
+ (item));
+ NS_ENSURE_SUCCESS(rv, rv);
+ break;
+ }
+ case txToplevelItem::dummy:
+ case txToplevelItem::import:
+ {
+ break;
+ }
+ case txToplevelItem::output:
+ {
+ mOutputFormat.merge(static_cast<txOutputItem*>(item)->mFormat);
+ break;
+ }
+ case txToplevelItem::stripSpace:
+ {
+ rv = addStripSpace(static_cast<txStripSpaceItem*>(item),
+ frameStripSpaceTests);
+ NS_ENSURE_SUCCESS(rv, rv);
+ break;
+ }
+ case txToplevelItem::templ:
+ {
+ rv = addTemplate(static_cast<txTemplateItem*>(item),
+ frame);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ break;
+ }
+ case txToplevelItem::variable:
+ {
+ rv = addGlobalVariable(static_cast<txVariableItem*>
+ (item));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ break;
+ }
+ }
+ delete item;
+ itemIter.remove(); //remove() moves to the previous
+ itemIter.next();
+ }
+ if (!mStripSpaceTests.AppendElements(frameStripSpaceTests)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ frameStripSpaceTests.Clear();
+ }
+
+ if (!mDecimalFormats.get(txExpandedName())) {
+ nsAutoPtr<txDecimalFormat> format(new txDecimalFormat);
+ rv = mDecimalFormats.add(txExpandedName(), format);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ format.forget();
+ }
+
+ return NS_OK;
+}
+
+nsresult
+txStylesheet::addTemplate(txTemplateItem* aTemplate,
+ ImportFrame* aImportFrame)
+{
+ NS_ASSERTION(aTemplate, "missing template");
+
+ txInstruction* instr = aTemplate->mFirstInstruction;
+ nsresult rv = mTemplateInstructions.add(instr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // mTemplateInstructions now owns the instructions
+ aTemplate->mFirstInstruction.forget();
+
+ if (!aTemplate->mName.isNull()) {
+ rv = mNamedTemplates.add(aTemplate->mName, instr);
+ NS_ENSURE_TRUE(NS_SUCCEEDED(rv) || rv == NS_ERROR_XSLT_ALREADY_SET,
+ rv);
+ }
+
+ if (!aTemplate->mMatch) {
+ // This is no error, see section 6 Named Templates
+
+ return NS_OK;
+ }
+
+ // get the txList for the right mode
+ nsTArray<MatchableTemplate>* templates =
+ aImportFrame->mMatchableTemplates.get(aTemplate->mMode);
+
+ if (!templates) {
+ nsAutoPtr< nsTArray<MatchableTemplate> > newList(
+ new nsTArray<MatchableTemplate>);
+ rv = aImportFrame->mMatchableTemplates.set(aTemplate->mMode, newList);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ templates = newList.forget();
+ }
+
+ // Add the simple patterns to the list of matchable templates, according
+ // to default priority
+ nsAutoPtr<txPattern> simple = Move(aTemplate->mMatch);
+ nsAutoPtr<txPattern> unionPattern;
+ if (simple->getType() == txPattern::UNION_PATTERN) {
+ unionPattern = Move(simple);
+ simple = unionPattern->getSubPatternAt(0);
+ unionPattern->setSubPatternAt(0, nullptr);
+ }
+
+ uint32_t unionPos = 1; // only used when unionPattern is set
+ while (simple) {
+ double priority = aTemplate->mPrio;
+ if (mozilla::IsNaN(priority)) {
+ priority = simple->getDefaultPriority();
+ NS_ASSERTION(!mozilla::IsNaN(priority),
+ "simple pattern without default priority");
+ }
+
+ uint32_t i, len = templates->Length();
+ for (i = 0; i < len; ++i) {
+ if (priority > (*templates)[i].mPriority) {
+ break;
+ }
+ }
+
+ MatchableTemplate* nt = templates->InsertElementAt(i);
+ NS_ENSURE_TRUE(nt, NS_ERROR_OUT_OF_MEMORY);
+
+ nt->mFirstInstruction = instr;
+ nt->mMatch = Move(simple);
+ nt->mPriority = priority;
+
+ if (unionPattern) {
+ simple = unionPattern->getSubPatternAt(unionPos);
+ if (simple) {
+ unionPattern->setSubPatternAt(unionPos, nullptr);
+ }
+ ++unionPos;
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult
+txStylesheet::addFrames(txListIterator& aInsertIter)
+{
+ ImportFrame* frame = static_cast<ImportFrame*>(aInsertIter.current());
+ nsresult rv = NS_OK;
+ txListIterator iter(&frame->mToplevelItems);
+ txToplevelItem* item;
+ while ((item = static_cast<txToplevelItem*>(iter.next()))) {
+ if (item->getType() == txToplevelItem::import) {
+ txImportItem* import = static_cast<txImportItem*>(item);
+ import->mFrame->mFirstNotImported =
+ static_cast<ImportFrame*>(aInsertIter.next());
+ rv = aInsertIter.addBefore(import->mFrame);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ import->mFrame.forget();
+ aInsertIter.previous();
+ rv = addFrames(aInsertIter);
+ NS_ENSURE_SUCCESS(rv, rv);
+ aInsertIter.previous();
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult
+txStylesheet::addStripSpace(txStripSpaceItem* aStripSpaceItem,
+ nsTArray<txStripSpaceTest*>& aFrameStripSpaceTests)
+{
+ int32_t testCount = aStripSpaceItem->mStripSpaceTests.Length();
+ for (; testCount > 0; --testCount) {
+ txStripSpaceTest* sst = aStripSpaceItem->mStripSpaceTests[testCount-1];
+ double priority = sst->getDefaultPriority();
+ int32_t i, frameCount = aFrameStripSpaceTests.Length();
+ for (i = 0; i < frameCount; ++i) {
+ if (aFrameStripSpaceTests[i]->getDefaultPriority() < priority) {
+ break;
+ }
+ }
+ if (!aFrameStripSpaceTests.InsertElementAt(i, sst)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ aStripSpaceItem->mStripSpaceTests.RemoveElementAt(testCount-1);
+ }
+
+ return NS_OK;
+}
+
+nsresult
+txStylesheet::addAttributeSet(txAttributeSetItem* aAttributeSetItem)
+{
+ nsresult rv = NS_OK;
+ txInstruction* oldInstr = mAttributeSets.get(aAttributeSetItem->mName);
+ if (!oldInstr) {
+ rv = mAttributeSets.add(aAttributeSetItem->mName,
+ aAttributeSetItem->mFirstInstruction);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aAttributeSetItem->mFirstInstruction.forget();
+
+ return NS_OK;
+ }
+
+ // We need to prepend the new instructions before the existing ones.
+ txInstruction* instr = aAttributeSetItem->mFirstInstruction;
+ txInstruction* lastNonReturn = nullptr;
+ while (instr->mNext) {
+ lastNonReturn = instr;
+ instr = instr->mNext;
+ }
+
+ if (!lastNonReturn) {
+ // The new attributeset is empty, so lets just ignore it.
+ return NS_OK;
+ }
+
+ rv = mAttributeSets.set(aAttributeSetItem->mName,
+ aAttributeSetItem->mFirstInstruction);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aAttributeSetItem->mFirstInstruction.forget();
+
+ lastNonReturn->mNext = oldInstr; // ...and link up the old instructions.
+
+ return NS_OK;
+}
+
+nsresult
+txStylesheet::addGlobalVariable(txVariableItem* aVariable)
+{
+ if (mGlobalVariables.get(aVariable->mName)) {
+ return NS_OK;
+ }
+ nsAutoPtr<GlobalVariable> var(
+ new GlobalVariable(Move(aVariable->mValue),
+ Move(aVariable->mFirstInstruction),
+ aVariable->mIsParam));
+ nsresult rv = mGlobalVariables.add(aVariable->mName, var);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ var.forget();
+
+ return NS_OK;
+
+}
+
+nsresult
+txStylesheet::addKey(const txExpandedName& aName,
+ nsAutoPtr<txPattern> aMatch, nsAutoPtr<Expr> aUse)
+{
+ nsresult rv = NS_OK;
+
+ txXSLKey* xslKey = mKeys.get(aName);
+ if (!xslKey) {
+ xslKey = new txXSLKey(aName);
+ rv = mKeys.add(aName, xslKey);
+ if (NS_FAILED(rv)) {
+ delete xslKey;
+ return rv;
+ }
+ }
+ if (!xslKey->addKey(Move(aMatch), Move(aUse))) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ return NS_OK;
+}
+
+nsresult
+txStylesheet::addDecimalFormat(const txExpandedName& aName,
+ nsAutoPtr<txDecimalFormat>&& aFormat)
+{
+ txDecimalFormat* existing = mDecimalFormats.get(aName);
+ if (existing) {
+ NS_ENSURE_TRUE(existing->isEqual(aFormat),
+ NS_ERROR_XSLT_PARSE_FAILURE);
+
+ return NS_OK;
+ }
+
+ nsresult rv = mDecimalFormats.add(aName, aFormat);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aFormat.forget();
+
+ return NS_OK;
+}
+
+txStylesheet::ImportFrame::~ImportFrame()
+{
+ txListIterator tlIter(&mToplevelItems);
+ while (tlIter.hasNext()) {
+ delete static_cast<txToplevelItem*>(tlIter.next());
+ }
+}
+
+txStylesheet::GlobalVariable::GlobalVariable(nsAutoPtr<Expr>&& aExpr,
+ nsAutoPtr<txInstruction>&& aInstr,
+ bool aIsParam)
+ : mExpr(Move(aExpr)),
+ mFirstInstruction(Move(aInstr)),
+ mIsParam(aIsParam)
+{
+}
diff --git a/dom/xslt/xslt/txStylesheet.h b/dom/xslt/xslt/txStylesheet.h
new file mode 100644
index 000000000..978527a13
--- /dev/null
+++ b/dom/xslt/xslt/txStylesheet.h
@@ -0,0 +1,204 @@
+/* -*- 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_TXSTYLESHEET_H
+#define TX_TXSTYLESHEET_H
+
+#include "txOutputFormat.h"
+#include "txExpandedNameMap.h"
+#include "txList.h"
+#include "txXSLTPatterns.h"
+#include "nsISupportsImpl.h"
+
+class txInstruction;
+class txTemplateItem;
+class txVariableItem;
+class txStripSpaceItem;
+class txAttributeSetItem;
+class txDecimalFormat;
+class txStripSpaceTest;
+class txXSLKey;
+
+class txStylesheet final
+{
+public:
+ class ImportFrame;
+ class GlobalVariable;
+ friend class txStylesheetCompilerState;
+ // To be able to do some cleaning up in destructor
+ friend class ImportFrame;
+
+ txStylesheet();
+ nsresult init();
+
+ NS_INLINE_DECL_REFCOUNTING(txStylesheet)
+
+ txInstruction* findTemplate(const txXPathNode& aNode,
+ const txExpandedName& aMode,
+ txIMatchContext* aContext,
+ ImportFrame* aImportedBy,
+ ImportFrame** aImportFrame);
+ txDecimalFormat* getDecimalFormat(const txExpandedName& aName);
+ txInstruction* getAttributeSet(const txExpandedName& aName);
+ txInstruction* getNamedTemplate(const txExpandedName& aName);
+ txOutputFormat* getOutputFormat();
+ GlobalVariable* getGlobalVariable(const txExpandedName& aName);
+ const txOwningExpandedNameMap<txXSLKey>& getKeyMap();
+ bool isStripSpaceAllowed(const txXPathNode& aNode,
+ txIMatchContext* aContext);
+
+ /**
+ * Called by the stylesheet compiler once all stylesheets has been read.
+ */
+ nsresult doneCompiling();
+
+ /**
+ * Add a key to the stylesheet
+ */
+ nsresult addKey(const txExpandedName& aName, nsAutoPtr<txPattern> aMatch,
+ nsAutoPtr<Expr> aUse);
+
+ /**
+ * Add a decimal-format to the stylesheet
+ */
+ nsresult addDecimalFormat(const txExpandedName& aName,
+ nsAutoPtr<txDecimalFormat>&& aFormat);
+
+ struct MatchableTemplate {
+ txInstruction* mFirstInstruction;
+ nsAutoPtr<txPattern> mMatch;
+ double mPriority;
+ };
+
+ /**
+ * Contain information that is import precedence dependant.
+ */
+ class ImportFrame {
+ public:
+ ImportFrame()
+ : mFirstNotImported(nullptr)
+ {
+ }
+ ~ImportFrame();
+
+ // List of toplevel items
+ txList mToplevelItems;
+
+ // Map of template modes
+ txOwningExpandedNameMap< nsTArray<MatchableTemplate> > mMatchableTemplates;
+
+ // ImportFrame which is the first one *not* imported by this frame
+ ImportFrame* mFirstNotImported;
+ };
+
+ class GlobalVariable : public txObject {
+ public:
+ GlobalVariable(nsAutoPtr<Expr>&& aExpr,
+ nsAutoPtr<txInstruction>&& aFirstInstruction,
+ bool aIsParam);
+
+ nsAutoPtr<Expr> mExpr;
+ nsAutoPtr<txInstruction> mFirstInstruction;
+ bool mIsParam;
+ };
+
+private:
+ // Private destructor, to discourage deletion outside of Release():
+ ~txStylesheet();
+
+ nsresult addTemplate(txTemplateItem* aTemplate, ImportFrame* aImportFrame);
+ nsresult addGlobalVariable(txVariableItem* aVariable);
+ nsresult addFrames(txListIterator& aInsertIter);
+ nsresult addStripSpace(txStripSpaceItem* aStripSpaceItem,
+ nsTArray<txStripSpaceTest*>& aFrameStripSpaceTests);
+ nsresult addAttributeSet(txAttributeSetItem* aAttributeSetItem);
+
+ // List of ImportFrames
+ txList mImportFrames;
+
+ // output format
+ txOutputFormat mOutputFormat;
+
+ // List of first instructions of templates. This is the owner of all
+ // instructions used in templates
+ txList mTemplateInstructions;
+
+ // Root importframe
+ ImportFrame* mRootFrame;
+
+ // Named templates
+ txExpandedNameMap<txInstruction> mNamedTemplates;
+
+ // Map with all decimal-formats
+ txOwningExpandedNameMap<txDecimalFormat> mDecimalFormats;
+
+ // Map with all named attribute sets
+ txExpandedNameMap<txInstruction> mAttributeSets;
+
+ // Map with all global variables and parameters
+ txOwningExpandedNameMap<GlobalVariable> mGlobalVariables;
+
+ // Map with all keys
+ txOwningExpandedNameMap<txXSLKey> mKeys;
+
+ // Array of all txStripSpaceTests, sorted in acending order
+ nsTArray<nsAutoPtr<txStripSpaceTest> > mStripSpaceTests;
+
+ // Default templates
+ nsAutoPtr<txInstruction> mContainerTemplate;
+ nsAutoPtr<txInstruction> mCharactersTemplate;
+ nsAutoPtr<txInstruction> mEmptyTemplate;
+};
+
+
+/**
+ * txStripSpaceTest holds both an txNameTest and a bool for use in
+ * whitespace stripping.
+ */
+class txStripSpaceTest {
+public:
+ txStripSpaceTest(nsIAtom* aPrefix, nsIAtom* aLocalName, int32_t aNSID,
+ bool stripSpace)
+ : mNameTest(aPrefix, aLocalName, aNSID, txXPathNodeType::ELEMENT_NODE),
+ mStrips(stripSpace)
+ {
+ }
+
+ bool matches(const txXPathNode& aNode, txIMatchContext* aContext) {
+ return mNameTest.matches(aNode, aContext);
+ }
+
+ bool stripsSpace() {
+ return mStrips;
+ }
+
+ double getDefaultPriority() {
+ return mNameTest.getDefaultPriority();
+ }
+
+protected:
+ txNameTest mNameTest;
+ bool mStrips;
+};
+
+/**
+ * Value of a global parameter
+ */
+class txIGlobalParameter
+{
+public:
+ txIGlobalParameter()
+ {
+ MOZ_COUNT_CTOR(txIGlobalParameter);
+ }
+ virtual ~txIGlobalParameter()
+ {
+ MOZ_COUNT_DTOR(txIGlobalParameter);
+ }
+ virtual nsresult getValue(txAExprResult** aValue) = 0;
+};
+
+
+#endif //TX_TXSTYLESHEET_H
diff --git a/dom/xslt/xslt/txStylesheetCompileHandlers.cpp b/dom/xslt/xslt/txStylesheetCompileHandlers.cpp
new file mode 100644
index 000000000..4d451e3c3
--- /dev/null
+++ b/dom/xslt/xslt/txStylesheetCompileHandlers.cpp
@@ -0,0 +1,2944 @@
+/* -*- 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 "mozilla/Move.h"
+
+#include "txStylesheetCompiler.h"
+#include "txStylesheetCompileHandlers.h"
+#include "nsWhitespaceTokenizer.h"
+#include "txInstructions.h"
+#include "nsGkAtoms.h"
+#include "txCore.h"
+#include "txStringUtils.h"
+#include "txStylesheet.h"
+#include "txToplevelItems.h"
+#include "txPatternParser.h"
+#include "txNamespaceMap.h"
+#include "txURIUtils.h"
+#include "txXSLTFunctions.h"
+
+using namespace mozilla;
+
+txHandlerTable* gTxIgnoreHandler = 0;
+txHandlerTable* gTxRootHandler = 0;
+txHandlerTable* gTxEmbedHandler = 0;
+txHandlerTable* gTxTopHandler = 0;
+txHandlerTable* gTxTemplateHandler = 0;
+txHandlerTable* gTxTextHandler = 0;
+txHandlerTable* gTxApplyTemplatesHandler = 0;
+txHandlerTable* gTxCallTemplateHandler = 0;
+txHandlerTable* gTxVariableHandler = 0;
+txHandlerTable* gTxForEachHandler = 0;
+txHandlerTable* gTxTopVariableHandler = 0;
+txHandlerTable* gTxChooseHandler = 0;
+txHandlerTable* gTxParamHandler = 0;
+txHandlerTable* gTxImportHandler = 0;
+txHandlerTable* gTxAttributeSetHandler = 0;
+txHandlerTable* gTxFallbackHandler = 0;
+
+static nsresult
+txFnStartLRE(int32_t aNamespaceID,
+ nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ txStylesheetCompilerState& aState);
+static nsresult
+txFnEndLRE(txStylesheetCompilerState& aState);
+
+
+#define TX_RETURN_IF_WHITESPACE(_str, _state) \
+ do { \
+ if (!_state.mElementContext->mPreserveWhitespace && \
+ XMLUtils::isWhitespace(PromiseFlatString(_str))) { \
+ return NS_OK; \
+ } \
+ } while(0)
+
+
+static nsresult
+getStyleAttr(txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ int32_t aNamespace,
+ nsIAtom* aName,
+ bool aRequired,
+ txStylesheetAttr** aAttr)
+{
+ int32_t i;
+ for (i = 0; i < aAttrCount; ++i) {
+ txStylesheetAttr* attr = aAttributes + i;
+ if (attr->mNamespaceID == aNamespace &&
+ attr->mLocalName == aName) {
+ attr->mLocalName = nullptr;
+ *aAttr = attr;
+
+ return NS_OK;
+ }
+ }
+ *aAttr = nullptr;
+
+ if (aRequired) {
+ // XXX ErrorReport: missing required attribute
+ return NS_ERROR_XSLT_PARSE_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+static nsresult
+parseUseAttrSets(txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ bool aInXSLTNS,
+ txStylesheetCompilerState& aState)
+{
+ txStylesheetAttr* attr = nullptr;
+ nsresult rv = getStyleAttr(aAttributes, aAttrCount,
+ aInXSLTNS ? kNameSpaceID_XSLT
+ : kNameSpaceID_None,
+ nsGkAtoms::useAttributeSets, false,
+ &attr);
+ if (!attr) {
+ return rv;
+ }
+
+ nsWhitespaceTokenizer tok(attr->mValue);
+ while (tok.hasMoreTokens()) {
+ txExpandedName name;
+ rv = name.init(tok.nextToken(), aState.mElementContext->mMappings,
+ false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoPtr<txInstruction> instr(new txInsertAttrSet(name));
+ rv = aState.addInstruction(Move(instr));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ return NS_OK;
+}
+
+static nsresult
+parseExcludeResultPrefixes(txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ int32_t aNamespaceID)
+{
+ txStylesheetAttr* attr = nullptr;
+ nsresult rv = getStyleAttr(aAttributes, aAttrCount, aNamespaceID,
+ nsGkAtoms::excludeResultPrefixes, false,
+ &attr);
+ if (!attr) {
+ return rv;
+ }
+
+ // XXX Needs to be implemented.
+
+ return NS_OK;
+}
+
+static nsresult
+getQNameAttr(txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ nsIAtom* aName,
+ bool aRequired,
+ txStylesheetCompilerState& aState,
+ txExpandedName& aExpName)
+{
+ aExpName.reset();
+ txStylesheetAttr* attr = nullptr;
+ nsresult rv = getStyleAttr(aAttributes, aAttrCount, kNameSpaceID_None,
+ aName, aRequired, &attr);
+ if (!attr) {
+ return rv;
+ }
+
+ rv = aExpName.init(attr->mValue, aState.mElementContext->mMappings,
+ false);
+ if (!aRequired && NS_FAILED(rv) && aState.fcp()) {
+ aExpName.reset();
+ rv = NS_OK;
+ }
+
+ return rv;
+}
+
+static nsresult
+getExprAttr(txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ nsIAtom* aName,
+ bool aRequired,
+ txStylesheetCompilerState& aState,
+ nsAutoPtr<Expr>& aExpr)
+{
+ aExpr = nullptr;
+ txStylesheetAttr* attr = nullptr;
+ nsresult rv = getStyleAttr(aAttributes, aAttrCount, kNameSpaceID_None,
+ aName, aRequired, &attr);
+ if (!attr) {
+ return rv;
+ }
+
+ rv = txExprParser::createExpr(attr->mValue, &aState,
+ getter_Transfers(aExpr));
+ if (NS_FAILED(rv) && aState.ignoreError(rv)) {
+ // use default value in fcp for not required exprs
+ if (aRequired) {
+ aExpr = new txErrorExpr(
+#ifdef TX_TO_STRING
+ attr->mValue
+#endif
+ );
+ }
+ else {
+ aExpr = nullptr;
+ }
+ return NS_OK;
+ }
+
+ return rv;
+}
+
+static nsresult
+getAVTAttr(txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ nsIAtom* aName,
+ bool aRequired,
+ txStylesheetCompilerState& aState,
+ nsAutoPtr<Expr>& aAVT)
+{
+ aAVT = nullptr;
+ txStylesheetAttr* attr = nullptr;
+ nsresult rv = getStyleAttr(aAttributes, aAttrCount, kNameSpaceID_None,
+ aName, aRequired, &attr);
+ if (!attr) {
+ return rv;
+ }
+
+ rv = txExprParser::createAVT(attr->mValue, &aState,
+ getter_Transfers(aAVT));
+ if (NS_FAILED(rv) && aState.fcp()) {
+ // use default value in fcp for not required exprs
+ if (aRequired) {
+ aAVT = new txErrorExpr(
+#ifdef TX_TO_STRING
+ attr->mValue
+#endif
+ );
+ }
+ else {
+ aAVT = nullptr;
+ }
+ return NS_OK;
+ }
+
+ return rv;
+}
+
+static nsresult
+getPatternAttr(txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ nsIAtom* aName,
+ bool aRequired,
+ txStylesheetCompilerState& aState,
+ nsAutoPtr<txPattern>& aPattern)
+{
+ aPattern = nullptr;
+ txStylesheetAttr* attr = nullptr;
+ nsresult rv = getStyleAttr(aAttributes, aAttrCount, kNameSpaceID_None,
+ aName, aRequired, &attr);
+ if (!attr) {
+ return rv;
+ }
+
+ rv = txPatternParser::createPattern(attr->mValue, &aState,
+ getter_Transfers(aPattern));
+ if (NS_FAILED(rv) && (aRequired || !aState.ignoreError(rv))) {
+ // XXX ErrorReport: XSLT-Pattern parse failure
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+static nsresult
+getNumberAttr(txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ nsIAtom* aName,
+ bool aRequired,
+ txStylesheetCompilerState& aState,
+ double& aNumber)
+{
+ aNumber = UnspecifiedNaN<double>();
+ txStylesheetAttr* attr = nullptr;
+ nsresult rv = getStyleAttr(aAttributes, aAttrCount, kNameSpaceID_None,
+ aName, aRequired, &attr);
+ if (!attr) {
+ return rv;
+ }
+
+ aNumber = txDouble::toDouble(attr->mValue);
+ if (mozilla::IsNaN(aNumber) && (aRequired || !aState.fcp())) {
+ // XXX ErrorReport: number parse failure
+ return NS_ERROR_XSLT_PARSE_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+static nsresult
+getAtomAttr(txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ nsIAtom* aName,
+ bool aRequired,
+ txStylesheetCompilerState& aState,
+ nsIAtom** aAtom)
+{
+ *aAtom = nullptr;
+ txStylesheetAttr* attr = nullptr;
+ nsresult rv = getStyleAttr(aAttributes, aAttrCount, kNameSpaceID_None,
+ aName, aRequired, &attr);
+ if (!attr) {
+ return rv;
+ }
+
+ *aAtom = NS_Atomize(attr->mValue).take();
+ NS_ENSURE_TRUE(*aAtom, NS_ERROR_OUT_OF_MEMORY);
+
+ return NS_OK;
+}
+
+static nsresult
+getYesNoAttr(txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ nsIAtom* aName,
+ bool aRequired,
+ txStylesheetCompilerState& aState,
+ txThreeState& aRes)
+{
+ aRes = eNotSet;
+ nsCOMPtr<nsIAtom> atom;
+ nsresult rv = getAtomAttr(aAttributes, aAttrCount, aName, aRequired,
+ aState, getter_AddRefs(atom));
+ if (!atom) {
+ return rv;
+ }
+
+ if (atom == nsGkAtoms::yes) {
+ aRes = eTrue;
+ }
+ else if (atom == nsGkAtoms::no) {
+ aRes = eFalse;
+ }
+ else if (aRequired || !aState.fcp()) {
+ // XXX ErrorReport: unknown values
+ return NS_ERROR_XSLT_PARSE_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+static nsresult
+getCharAttr(txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ nsIAtom* aName,
+ bool aRequired,
+ txStylesheetCompilerState& aState,
+ char16_t& aChar)
+{
+ // Don't reset aChar since it contains the default value
+ txStylesheetAttr* attr = nullptr;
+ nsresult rv = getStyleAttr(aAttributes, aAttrCount, kNameSpaceID_None,
+ aName, aRequired, &attr);
+ if (!attr) {
+ return rv;
+ }
+
+ if (attr->mValue.Length() == 1) {
+ aChar = attr->mValue.CharAt(0);
+ }
+ else if (aRequired || !aState.fcp()) {
+ // XXX ErrorReport: not a character
+ return NS_ERROR_XSLT_PARSE_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+
+/**
+ * Ignore and error handlers
+ */
+static nsresult
+txFnTextIgnore(const nsAString& aStr, txStylesheetCompilerState& aState)
+{
+ return NS_OK;
+}
+
+static nsresult
+txFnTextError(const nsAString& aStr, txStylesheetCompilerState& aState)
+{
+ TX_RETURN_IF_WHITESPACE(aStr, aState);
+
+ return NS_ERROR_XSLT_PARSE_FAILURE;
+}
+
+void
+clearAttributes(txStylesheetAttr* aAttributes,
+ int32_t aAttrCount)
+{
+ int32_t i;
+ for (i = 0; i < aAttrCount; ++i) {
+ aAttributes[i].mLocalName = nullptr;
+ }
+}
+
+static nsresult
+txFnStartElementIgnore(int32_t aNamespaceID,
+ nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ txStylesheetCompilerState& aState)
+{
+ if (!aState.fcp()) {
+ clearAttributes(aAttributes, aAttrCount);
+ }
+
+ return NS_OK;
+}
+
+static nsresult
+txFnEndElementIgnore(txStylesheetCompilerState& aState)
+{
+ return NS_OK;
+}
+
+static nsresult
+txFnStartElementSetIgnore(int32_t aNamespaceID,
+ nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ txStylesheetCompilerState& aState)
+{
+ if (!aState.fcp()) {
+ clearAttributes(aAttributes, aAttrCount);
+ }
+
+ return aState.pushHandlerTable(gTxIgnoreHandler);
+}
+
+static nsresult
+txFnEndElementSetIgnore(txStylesheetCompilerState& aState)
+{
+ aState.popHandlerTable();
+ return NS_OK;
+}
+
+static nsresult
+txFnStartElementError(int32_t aNamespaceID,
+ nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ txStylesheetCompilerState& aState)
+{
+ return NS_ERROR_XSLT_PARSE_FAILURE;
+}
+
+static nsresult
+txFnEndElementError(txStylesheetCompilerState& aState)
+{
+ NS_ERROR("txFnEndElementError shouldn't be called");
+ return NS_ERROR_XSLT_PARSE_FAILURE;
+}
+
+
+/**
+ * Root handlers
+ */
+static nsresult
+txFnStartStylesheet(int32_t aNamespaceID,
+ nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ txStylesheetCompilerState& aState)
+{
+ // extension-element-prefixes is handled in
+ // txStylesheetCompiler::startElementInternal
+
+ txStylesheetAttr* attr;
+ nsresult rv = getStyleAttr(aAttributes, aAttrCount, kNameSpaceID_None,
+ nsGkAtoms::id, false, &attr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = parseExcludeResultPrefixes(aAttributes, aAttrCount, kNameSpaceID_None);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = getStyleAttr(aAttributes, aAttrCount, kNameSpaceID_None,
+ nsGkAtoms::version, true, &attr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return aState.pushHandlerTable(gTxImportHandler);
+}
+
+static nsresult
+txFnEndStylesheet(txStylesheetCompilerState& aState)
+{
+ aState.popHandlerTable();
+ return NS_OK;
+}
+
+static nsresult
+txFnStartElementContinueTopLevel(int32_t aNamespaceID,
+ nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ txStylesheetCompilerState& aState)
+{
+ aState.mHandlerTable = gTxTopHandler;
+
+ return NS_XSLT_GET_NEW_HANDLER;
+}
+
+static nsresult
+txFnStartLREStylesheet(int32_t aNamespaceID,
+ nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ txStylesheetCompilerState& aState)
+{
+ txStylesheetAttr* attr;
+ nsresult rv = getStyleAttr(aAttributes, aAttrCount, kNameSpaceID_XSLT,
+ nsGkAtoms::version, true, &attr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ txExpandedName nullExpr;
+ double prio = UnspecifiedNaN<double>();
+
+ nsAutoPtr<txPattern> match(new txRootPattern());
+ nsAutoPtr<txTemplateItem> templ(new txTemplateItem(Move(match), nullExpr,
+ nullExpr, prio));
+ aState.openInstructionContainer(templ);
+ rv = aState.addToplevelItem(templ);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ templ.forget();
+
+ rv = aState.pushHandlerTable(gTxTemplateHandler);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return txFnStartLRE(aNamespaceID, aLocalName, aPrefix, aAttributes,
+ aAttrCount, aState);
+}
+
+static nsresult
+txFnEndLREStylesheet(txStylesheetCompilerState& aState)
+{
+ nsresult rv = txFnEndLRE(aState);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aState.popHandlerTable();
+
+ nsAutoPtr<txInstruction> instr(new txReturn());
+ rv = aState.addInstruction(Move(instr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aState.closeInstructionContainer();
+
+ return NS_OK;
+}
+
+static nsresult
+txFnStartEmbed(int32_t aNamespaceID,
+ nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ txStylesheetCompilerState& aState)
+{
+ if (!aState.handleEmbeddedSheet()) {
+ return NS_OK;
+ }
+ if (aNamespaceID != kNameSpaceID_XSLT ||
+ (aLocalName != nsGkAtoms::stylesheet &&
+ aLocalName != nsGkAtoms::transform)) {
+ return NS_ERROR_XSLT_PARSE_FAILURE;
+ }
+ return txFnStartStylesheet(aNamespaceID, aLocalName, aPrefix,
+ aAttributes, aAttrCount, aState);
+}
+
+static nsresult
+txFnEndEmbed(txStylesheetCompilerState& aState)
+{
+ if (!aState.handleEmbeddedSheet()) {
+ return NS_OK;
+ }
+ nsresult rv = txFnEndStylesheet(aState);
+ aState.doneEmbedding();
+ return rv;
+}
+
+
+/**
+ * Top handlers
+ */
+static nsresult
+txFnStartOtherTop(int32_t aNamespaceID,
+ nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ txStylesheetCompilerState& aState)
+{
+ if (aNamespaceID == kNameSpaceID_None ||
+ (aNamespaceID == kNameSpaceID_XSLT && !aState.fcp())) {
+ return NS_ERROR_XSLT_PARSE_FAILURE;
+ }
+
+ return aState.pushHandlerTable(gTxIgnoreHandler);
+}
+
+static nsresult
+txFnEndOtherTop(txStylesheetCompilerState& aState)
+{
+ aState.popHandlerTable();
+ return NS_OK;
+}
+
+
+// xsl:attribute-set
+static nsresult
+txFnStartAttributeSet(int32_t aNamespaceID,
+ nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ txStylesheetCompilerState& aState)
+{
+ nsresult rv = NS_OK;
+ txExpandedName name;
+ rv = getQNameAttr(aAttributes, aAttrCount, nsGkAtoms::name, true,
+ aState, name);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoPtr<txAttributeSetItem> attrSet(new txAttributeSetItem(name));
+ aState.openInstructionContainer(attrSet);
+
+ rv = aState.addToplevelItem(attrSet);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ attrSet.forget();
+
+ rv = parseUseAttrSets(aAttributes, aAttrCount, false, aState);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return aState.pushHandlerTable(gTxAttributeSetHandler);
+}
+
+static nsresult
+txFnEndAttributeSet(txStylesheetCompilerState& aState)
+{
+ aState.popHandlerTable();
+
+ nsAutoPtr<txInstruction> instr(new txReturn());
+ nsresult rv = aState.addInstruction(Move(instr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aState.closeInstructionContainer();
+
+ return NS_OK;
+}
+
+
+// xsl:decimal-format
+static nsresult
+txFnStartDecimalFormat(int32_t aNamespaceID,
+ nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ txStylesheetCompilerState& aState)
+{
+ nsresult rv = NS_OK;
+ txExpandedName name;
+ rv = getQNameAttr(aAttributes, aAttrCount, nsGkAtoms::name, false,
+ aState, name);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoPtr<txDecimalFormat> format(new txDecimalFormat);
+ rv = getCharAttr(aAttributes, aAttrCount, nsGkAtoms::decimalSeparator,
+ false, aState, format->mDecimalSeparator);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = getCharAttr(aAttributes, aAttrCount, nsGkAtoms::groupingSeparator,
+ false, aState, format->mGroupingSeparator);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ txStylesheetAttr* attr = nullptr;
+ rv = getStyleAttr(aAttributes, aAttrCount, kNameSpaceID_None,
+ nsGkAtoms::infinity, false, &attr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (attr) {
+ format->mInfinity = attr->mValue;
+ }
+
+ rv = getCharAttr(aAttributes, aAttrCount, nsGkAtoms::minusSign,
+ false, aState, format->mMinusSign);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = getStyleAttr(aAttributes, aAttrCount, kNameSpaceID_None,
+ nsGkAtoms::NaN, false, &attr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (attr) {
+ format->mNaN = attr->mValue;
+ }
+
+ rv = getCharAttr(aAttributes, aAttrCount, nsGkAtoms::percent,
+ false, aState, format->mPercent);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = getCharAttr(aAttributes, aAttrCount, nsGkAtoms::perMille,
+ false, aState, format->mPerMille);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = getCharAttr(aAttributes, aAttrCount, nsGkAtoms::zeroDigit,
+ false, aState, format->mZeroDigit);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = getCharAttr(aAttributes, aAttrCount, nsGkAtoms::digit,
+ false, aState, format->mDigit);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = getCharAttr(aAttributes, aAttrCount, nsGkAtoms::patternSeparator,
+ false, aState, format->mPatternSeparator);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = aState.mStylesheet->addDecimalFormat(name, Move(format));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return aState.pushHandlerTable(gTxIgnoreHandler);
+}
+
+static nsresult
+txFnEndDecimalFormat(txStylesheetCompilerState& aState)
+{
+ aState.popHandlerTable();
+
+ return NS_OK;
+}
+
+// xsl:import
+static nsresult
+txFnStartImport(int32_t aNamespaceID,
+ nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ txStylesheetCompilerState& aState)
+{
+ nsAutoPtr<txImportItem> import(new txImportItem);
+ import->mFrame = new txStylesheet::ImportFrame;
+ nsresult rv = aState.addToplevelItem(import);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ txImportItem* importPtr = import.forget();
+
+ txStylesheetAttr* attr = nullptr;
+ rv = getStyleAttr(aAttributes, aAttrCount, kNameSpaceID_None,
+ nsGkAtoms::href, true, &attr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString absUri;
+ URIUtils::resolveHref(attr->mValue, aState.mElementContext->mBaseURI,
+ absUri);
+ rv = aState.loadImportedStylesheet(absUri, importPtr->mFrame);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return aState.pushHandlerTable(gTxIgnoreHandler);
+}
+
+static nsresult
+txFnEndImport(txStylesheetCompilerState& aState)
+{
+ aState.popHandlerTable();
+
+ return NS_OK;
+}
+
+// xsl:include
+static nsresult
+txFnStartInclude(int32_t aNamespaceID,
+ nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ txStylesheetCompilerState& aState)
+{
+ txStylesheetAttr* attr = nullptr;
+ nsresult rv = getStyleAttr(aAttributes, aAttrCount, kNameSpaceID_None,
+ nsGkAtoms::href, true, &attr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString absUri;
+ URIUtils::resolveHref(attr->mValue, aState.mElementContext->mBaseURI,
+ absUri);
+ rv = aState.loadIncludedStylesheet(absUri);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return aState.pushHandlerTable(gTxIgnoreHandler);
+}
+
+static nsresult
+txFnEndInclude(txStylesheetCompilerState& aState)
+{
+ aState.popHandlerTable();
+
+ return NS_OK;
+}
+
+// xsl:key
+static nsresult
+txFnStartKey(int32_t aNamespaceID,
+ nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ txStylesheetCompilerState& aState)
+{
+ nsresult rv = NS_OK;
+ txExpandedName name;
+ rv = getQNameAttr(aAttributes, aAttrCount, nsGkAtoms::name, true,
+ aState, name);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aState.mDisAllowed = txIParseContext::KEY_FUNCTION;
+
+ nsAutoPtr<txPattern> match;
+ rv = getPatternAttr(aAttributes, aAttrCount, nsGkAtoms::match, true,
+ aState, match);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoPtr<Expr> use;
+ rv = getExprAttr(aAttributes, aAttrCount, nsGkAtoms::use, true,
+ aState, use);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aState.mDisAllowed = 0;
+
+ rv = aState.mStylesheet->addKey(name, Move(match), Move(use));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return aState.pushHandlerTable(gTxIgnoreHandler);
+}
+
+static nsresult
+txFnEndKey(txStylesheetCompilerState& aState)
+{
+ aState.popHandlerTable();
+
+ return NS_OK;
+}
+
+// xsl:namespace-alias
+static nsresult
+txFnStartNamespaceAlias(int32_t aNamespaceID,
+ nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ txStylesheetCompilerState& aState)
+{
+ txStylesheetAttr* attr = nullptr;
+ nsresult rv = getStyleAttr(aAttributes, aAttrCount, kNameSpaceID_None,
+ nsGkAtoms::stylesheetPrefix, true, &attr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = getStyleAttr(aAttributes, aAttrCount, kNameSpaceID_None,
+ nsGkAtoms::resultPrefix, true, &attr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // XXX Needs to be implemented.
+
+ return aState.pushHandlerTable(gTxIgnoreHandler);
+}
+
+static nsresult
+txFnEndNamespaceAlias(txStylesheetCompilerState& aState)
+{
+ aState.popHandlerTable();
+
+ return NS_OK;
+}
+
+// xsl:output
+static nsresult
+txFnStartOutput(int32_t aNamespaceID,
+ nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ txStylesheetCompilerState& aState)
+{
+ nsresult rv = NS_OK;
+
+ nsAutoPtr<txOutputItem> item(new txOutputItem);
+
+ txExpandedName methodExpName;
+ rv = getQNameAttr(aAttributes, aAttrCount, nsGkAtoms::method, false,
+ aState, methodExpName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!methodExpName.isNull()) {
+ if (methodExpName.mNamespaceID != kNameSpaceID_None) {
+ // The spec doesn't say what to do here so we'll just ignore the
+ // value. We could possibly warn.
+ }
+ else if (methodExpName.mLocalName == nsGkAtoms::html) {
+ item->mFormat.mMethod = eHTMLOutput;
+ }
+ else if (methodExpName.mLocalName == nsGkAtoms::text) {
+ item->mFormat.mMethod = eTextOutput;
+ }
+ else if (methodExpName.mLocalName == nsGkAtoms::xml) {
+ item->mFormat.mMethod = eXMLOutput;
+ }
+ else {
+ return NS_ERROR_XSLT_PARSE_FAILURE;
+ }
+ }
+
+ txStylesheetAttr* attr = nullptr;
+ getStyleAttr(aAttributes, aAttrCount, kNameSpaceID_None,
+ nsGkAtoms::version, false, &attr);
+ if (attr) {
+ item->mFormat.mVersion = attr->mValue;
+ }
+
+ getStyleAttr(aAttributes, aAttrCount, kNameSpaceID_None,
+ nsGkAtoms::encoding, false, &attr);
+ if (attr) {
+ item->mFormat.mEncoding = attr->mValue;
+ }
+
+ rv = getYesNoAttr(aAttributes, aAttrCount,
+ nsGkAtoms::omitXmlDeclaration, false, aState,
+ item->mFormat.mOmitXMLDeclaration);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = getYesNoAttr(aAttributes, aAttrCount,
+ nsGkAtoms::standalone, false, aState,
+ item->mFormat.mStandalone);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ getStyleAttr(aAttributes, aAttrCount, kNameSpaceID_None,
+ nsGkAtoms::doctypePublic, false, &attr);
+ if (attr) {
+ item->mFormat.mPublicId = attr->mValue;
+ }
+
+ getStyleAttr(aAttributes, aAttrCount, kNameSpaceID_None,
+ nsGkAtoms::doctypeSystem, false, &attr);
+ if (attr) {
+ item->mFormat.mSystemId = attr->mValue;
+ }
+
+ getStyleAttr(aAttributes, aAttrCount, kNameSpaceID_None,
+ nsGkAtoms::cdataSectionElements, false, &attr);
+ if (attr) {
+ nsWhitespaceTokenizer tokens(attr->mValue);
+ while (tokens.hasMoreTokens()) {
+ nsAutoPtr<txExpandedName> qname(new txExpandedName());
+ rv = qname->init(tokens.nextToken(),
+ aState.mElementContext->mMappings, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = item->mFormat.mCDATASectionElements.add(qname);
+ NS_ENSURE_SUCCESS(rv, rv);
+ qname.forget();
+ }
+ }
+
+ rv = getYesNoAttr(aAttributes, aAttrCount,
+ nsGkAtoms::indent, false, aState,
+ item->mFormat.mIndent);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ getStyleAttr(aAttributes, aAttrCount, kNameSpaceID_None,
+ nsGkAtoms::mediaType, false, &attr);
+ if (attr) {
+ item->mFormat.mMediaType = attr->mValue;
+ }
+
+ rv = aState.addToplevelItem(item);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ item.forget();
+
+ return aState.pushHandlerTable(gTxIgnoreHandler);
+}
+
+static nsresult
+txFnEndOutput(txStylesheetCompilerState& aState)
+{
+ aState.popHandlerTable();
+
+ return NS_OK;
+}
+
+// xsl:strip-space/xsl:preserve-space
+static nsresult
+txFnStartStripSpace(int32_t aNamespaceID,
+ nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ txStylesheetCompilerState& aState)
+{
+ txStylesheetAttr* attr = nullptr;
+ nsresult rv = getStyleAttr(aAttributes, aAttrCount, kNameSpaceID_None,
+ nsGkAtoms::elements, true, &attr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool strip = aLocalName == nsGkAtoms::stripSpace;
+
+ nsAutoPtr<txStripSpaceItem> stripItem(new txStripSpaceItem);
+ nsWhitespaceTokenizer tokenizer(attr->mValue);
+ while (tokenizer.hasMoreTokens()) {
+ const nsASingleFragmentString& name = tokenizer.nextToken();
+ int32_t ns = kNameSpaceID_None;
+ nsCOMPtr<nsIAtom> prefix, localName;
+ rv = XMLUtils::splitQName(name, getter_AddRefs(prefix),
+ getter_AddRefs(localName));
+ if (NS_FAILED(rv)) {
+ // check for "*" or "prefix:*"
+ uint32_t length = name.Length();
+ const char16_t* c;
+ name.BeginReading(c);
+ if (length == 2 || c[length-1] != '*') {
+ // these can't work
+ return NS_ERROR_XSLT_PARSE_FAILURE;
+ }
+ if (length > 1) {
+ // Check for a valid prefix, that is, the returned prefix
+ // should be empty and the real prefix is returned in
+ // localName.
+ if (c[length-2] != ':') {
+ return NS_ERROR_XSLT_PARSE_FAILURE;
+ }
+ rv = XMLUtils::splitQName(StringHead(name, length - 2),
+ getter_AddRefs(prefix),
+ getter_AddRefs(localName));
+ if (NS_FAILED(rv) || prefix) {
+ // bad chars or two ':'
+ return NS_ERROR_XSLT_PARSE_FAILURE;
+ }
+ prefix = localName;
+ }
+ localName = nsGkAtoms::_asterisk;
+ }
+ if (prefix) {
+ ns = aState.mElementContext->mMappings->lookupNamespace(prefix);
+ NS_ENSURE_TRUE(ns != kNameSpaceID_Unknown, NS_ERROR_FAILURE);
+ }
+ nsAutoPtr<txStripSpaceTest> sst(new txStripSpaceTest(prefix, localName,
+ ns, strip));
+ rv = stripItem->addStripSpaceTest(sst);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ sst.forget();
+ }
+
+ rv = aState.addToplevelItem(stripItem);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ stripItem.forget();
+
+ return aState.pushHandlerTable(gTxIgnoreHandler);
+}
+
+static nsresult
+txFnEndStripSpace(txStylesheetCompilerState& aState)
+{
+ aState.popHandlerTable();
+
+ return NS_OK;
+}
+
+// xsl:template
+static nsresult
+txFnStartTemplate(int32_t aNamespaceID,
+ nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ txStylesheetCompilerState& aState)
+{
+ nsresult rv = NS_OK;
+ txExpandedName name;
+ rv = getQNameAttr(aAttributes, aAttrCount, nsGkAtoms::name, false,
+ aState, name);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ txExpandedName mode;
+ rv = getQNameAttr(aAttributes, aAttrCount, nsGkAtoms::mode, false,
+ aState, mode);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ double prio = UnspecifiedNaN<double>();
+ rv = getNumberAttr(aAttributes, aAttrCount, nsGkAtoms::priority,
+ false, aState, prio);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoPtr<txPattern> match;
+ rv = getPatternAttr(aAttributes, aAttrCount, nsGkAtoms::match,
+ name.isNull(), aState, match);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoPtr<txTemplateItem> templ(new txTemplateItem(Move(match), name, mode,
+ prio));
+ aState.openInstructionContainer(templ);
+ rv = aState.addToplevelItem(templ);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ templ.forget();
+
+ return aState.pushHandlerTable(gTxParamHandler);
+}
+
+static nsresult
+txFnEndTemplate(txStylesheetCompilerState& aState)
+{
+ aState.popHandlerTable();
+
+ nsAutoPtr<txInstruction> instr(new txReturn());
+ nsresult rv = aState.addInstruction(Move(instr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aState.closeInstructionContainer();
+
+ return NS_OK;
+}
+
+// xsl:variable, xsl:param
+static nsresult
+txFnStartTopVariable(int32_t aNamespaceID,
+ nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ txStylesheetCompilerState& aState)
+{
+ nsresult rv = NS_OK;
+ txExpandedName name;
+ rv = getQNameAttr(aAttributes, aAttrCount, nsGkAtoms::name, true,
+ aState, name);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoPtr<Expr> select;
+ rv = getExprAttr(aAttributes, aAttrCount, nsGkAtoms::select, false,
+ aState, select);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoPtr<txVariableItem> var(
+ new txVariableItem(name, Move(select),
+ aLocalName == nsGkAtoms::param));
+ aState.openInstructionContainer(var);
+ rv = aState.pushPtr(var, aState.eVariableItem);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (var->mValue) {
+ // XXX should be gTxErrorHandler?
+ rv = aState.pushHandlerTable(gTxIgnoreHandler);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ else {
+ rv = aState.pushHandlerTable(gTxTopVariableHandler);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ rv = aState.addToplevelItem(var);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ var.forget();
+
+ return NS_OK;
+}
+
+static nsresult
+txFnEndTopVariable(txStylesheetCompilerState& aState)
+{
+ txHandlerTable* prev = aState.mHandlerTable;
+ aState.popHandlerTable();
+ txVariableItem* var =
+ static_cast<txVariableItem*>(aState.popPtr(aState.eVariableItem));
+
+ if (prev == gTxTopVariableHandler) {
+ // No children were found.
+ NS_ASSERTION(!var->mValue,
+ "There shouldn't be a select-expression here");
+ var->mValue = new txLiteralExpr(EmptyString());
+ }
+ else if (!var->mValue) {
+ // If we don't have a select-expression there mush be children.
+ nsAutoPtr<txInstruction> instr(new txReturn());
+ nsresult rv = aState.addInstruction(Move(instr));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ aState.closeInstructionContainer();
+
+ return NS_OK;
+}
+
+static nsresult
+txFnStartElementStartTopVar(int32_t aNamespaceID,
+ nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ txStylesheetCompilerState& aState)
+{
+ aState.mHandlerTable = gTxTemplateHandler;
+
+ return NS_XSLT_GET_NEW_HANDLER;
+}
+
+static nsresult
+txFnTextStartTopVar(const nsAString& aStr, txStylesheetCompilerState& aState)
+{
+ TX_RETURN_IF_WHITESPACE(aStr, aState);
+
+ aState.mHandlerTable = gTxTemplateHandler;
+
+ return NS_XSLT_GET_NEW_HANDLER;
+}
+
+/**
+ * Template Handlers
+ */
+
+/*
+ LRE
+
+ txStartLREElement
+ txInsertAttrSet one for each qname in xsl:use-attribute-sets
+ txLREAttribute one for each attribute
+ [children]
+ txEndElement
+*/
+static nsresult
+txFnStartLRE(int32_t aNamespaceID,
+ nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ txStylesheetCompilerState& aState)
+{
+ nsresult rv = NS_OK;
+
+ nsAutoPtr<txInstruction> instr(new txStartLREElement(aNamespaceID,
+ aLocalName, aPrefix));
+ rv = aState.addInstruction(Move(instr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = parseExcludeResultPrefixes(aAttributes, aAttrCount, kNameSpaceID_XSLT);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = parseUseAttrSets(aAttributes, aAttrCount, true, aState);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ txStylesheetAttr* attr = nullptr;
+ int32_t i;
+ for (i = 0; i < aAttrCount; ++i) {
+ attr = aAttributes + i;
+
+ if (attr->mNamespaceID == kNameSpaceID_XSLT) {
+ if (attr->mLocalName == nsGkAtoms::version) {
+ attr->mLocalName = nullptr;
+ }
+
+ continue;
+ }
+
+ nsAutoPtr<Expr> avt;
+ rv = txExprParser::createAVT(attr->mValue, &aState,
+ getter_Transfers(avt));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ instr = new txLREAttribute(attr->mNamespaceID, attr->mLocalName,
+ attr->mPrefix, Move(avt));
+ rv = aState.addInstruction(Move(instr));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return NS_OK;
+}
+
+static nsresult
+txFnEndLRE(txStylesheetCompilerState& aState)
+{
+ nsAutoPtr<txInstruction> instr(new txEndElement);
+ nsresult rv = aState.addInstruction(Move(instr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+/*
+ "LRE text"
+
+ txText
+*/
+static nsresult
+txFnText(const nsAString& aStr, txStylesheetCompilerState& aState)
+{
+ TX_RETURN_IF_WHITESPACE(aStr, aState);
+
+ nsAutoPtr<txInstruction> instr(new txText(aStr, false));
+ nsresult rv = aState.addInstruction(Move(instr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+/*
+ xsl:apply-imports
+
+ txApplyImports
+*/
+static nsresult
+txFnStartApplyImports(int32_t aNamespaceID,
+ nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ txStylesheetCompilerState& aState)
+{
+ nsresult rv = NS_OK;
+
+ nsAutoPtr<txInstruction> instr(new txApplyImports);
+ rv = aState.addInstruction(Move(instr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return aState.pushHandlerTable(gTxIgnoreHandler);
+}
+
+static nsresult
+txFnEndApplyImports(txStylesheetCompilerState& aState)
+{
+ aState.popHandlerTable();
+
+ return NS_OK;
+}
+
+/*
+ xsl:apply-templates
+
+ txPushParams
+ [params]
+ txPushNewContext -+ (holds <xsl:sort>s)
+ txApplyTemplate <-+ |
+ txLoopNodeSet -+ |
+ txPopParams <-+
+*/
+static nsresult
+txFnStartApplyTemplates(int32_t aNamespaceID,
+ nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ txStylesheetCompilerState& aState)
+{
+ nsresult rv = NS_OK;
+
+ nsAutoPtr<txInstruction> instr(new txPushParams);
+ rv = aState.addInstruction(Move(instr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ txExpandedName mode;
+ rv = getQNameAttr(aAttributes, aAttrCount, nsGkAtoms::mode, false,
+ aState, mode);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ instr = new txApplyTemplates(mode);
+ rv = aState.pushObject(instr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ instr.forget();
+
+ nsAutoPtr<Expr> select;
+ rv = getExprAttr(aAttributes, aAttrCount, nsGkAtoms::select, false,
+ aState, select);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!select) {
+ nsAutoPtr<txNodeTest> nt(
+ new txNodeTypeTest(txNodeTypeTest::NODE_TYPE));
+ select = new LocationStep(nt, LocationStep::CHILD_AXIS);
+ nt.forget();
+ }
+
+ nsAutoPtr<txPushNewContext> pushcontext( new txPushNewContext(Move(select)));
+ rv = aState.pushSorter(pushcontext);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = aState.pushObject(pushcontext);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ pushcontext.forget();
+
+ return aState.pushHandlerTable(gTxApplyTemplatesHandler);
+}
+
+static nsresult
+txFnEndApplyTemplates(txStylesheetCompilerState& aState)
+{
+ aState.popHandlerTable();
+
+ txPushNewContext* pushcontext =
+ static_cast<txPushNewContext*>(aState.popObject());
+ nsAutoPtr<txInstruction> instr(pushcontext); // txPushNewContext
+ nsresult rv = aState.addInstruction(Move(instr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aState.popSorter();
+
+ instr = static_cast<txInstruction*>(aState.popObject()); // txApplyTemplates
+ nsAutoPtr<txLoopNodeSet> loop(new txLoopNodeSet(instr));
+ rv = aState.addInstruction(Move(instr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ instr = loop.forget();
+ rv = aState.addInstruction(Move(instr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ instr = new txPopParams;
+ pushcontext->mBailTarget = instr;
+ rv = aState.addInstruction(Move(instr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+/*
+ xsl:attribute
+
+ txPushStringHandler
+ [children]
+ txAttribute
+*/
+static nsresult
+txFnStartAttribute(int32_t aNamespaceID,
+ nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ txStylesheetCompilerState& aState)
+{
+ nsresult rv = NS_OK;
+
+ nsAutoPtr<txInstruction> instr(new txPushStringHandler(true));
+ rv = aState.addInstruction(Move(instr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoPtr<Expr> name;
+ rv = getAVTAttr(aAttributes, aAttrCount, nsGkAtoms::name, true,
+ aState, name);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoPtr<Expr> nspace;
+ rv = getAVTAttr(aAttributes, aAttrCount, nsGkAtoms::_namespace, false,
+ aState, nspace);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ instr = new txAttribute(Move(name), Move(nspace),
+ aState.mElementContext->mMappings);
+ rv = aState.pushObject(instr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ instr.forget();
+
+ // We need to push the template-handler since the current might be
+ // the attributeset-handler
+ return aState.pushHandlerTable(gTxTemplateHandler);
+}
+
+static nsresult
+txFnEndAttribute(txStylesheetCompilerState& aState)
+{
+ aState.popHandlerTable();
+ nsAutoPtr<txInstruction> instr(static_cast<txInstruction*>
+ (aState.popObject()));
+ nsresult rv = aState.addInstruction(Move(instr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+/*
+ xsl:call-template
+
+ txPushParams
+ [params]
+ txCallTemplate
+ txPopParams
+*/
+static nsresult
+txFnStartCallTemplate(int32_t aNamespaceID,
+ nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ txStylesheetCompilerState& aState)
+{
+ nsresult rv = NS_OK;
+
+ nsAutoPtr<txInstruction> instr(new txPushParams);
+ rv = aState.addInstruction(Move(instr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ txExpandedName name;
+ rv = getQNameAttr(aAttributes, aAttrCount, nsGkAtoms::name, true,
+ aState, name);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ instr = new txCallTemplate(name);
+ rv = aState.pushObject(instr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ instr.forget();
+
+ return aState.pushHandlerTable(gTxCallTemplateHandler);
+}
+
+static nsresult
+txFnEndCallTemplate(txStylesheetCompilerState& aState)
+{
+ aState.popHandlerTable();
+
+ // txCallTemplate
+ nsAutoPtr<txInstruction> instr(static_cast<txInstruction*>(aState.popObject()));
+ nsresult rv = aState.addInstruction(Move(instr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ instr = new txPopParams;
+ rv = aState.addInstruction(Move(instr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+/*
+ xsl:choose
+
+ txCondotionalGoto --+ \
+ [children] | | one for each xsl:when
+ txGoTo --+ | /
+ | |
+ txCondotionalGoto | <-+ --+
+ [children] | |
+ txGoTo --+ |
+ | |
+ [children] | <-+ for the xsl:otherwise, if there is one
+ <-+
+*/
+static nsresult
+txFnStartChoose(int32_t aNamespaceID,
+ nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ txStylesheetCompilerState& aState)
+{
+ nsresult rv = aState.pushChooseGotoList();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return aState.pushHandlerTable(gTxChooseHandler);
+}
+
+static nsresult
+txFnEndChoose(txStylesheetCompilerState& aState)
+{
+ nsresult rv = NS_OK;
+ aState.popHandlerTable();
+ txListIterator iter(aState.mChooseGotoList);
+ txGoTo* gotoinstr;
+ while ((gotoinstr = static_cast<txGoTo*>(iter.next()))) {
+ rv = aState.addGotoTarget(&gotoinstr->mTarget);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ aState.popChooseGotoList();
+
+ return NS_OK;
+}
+
+/*
+ xsl:comment
+
+ txPushStringHandler
+ [children]
+ txComment
+*/
+static nsresult
+txFnStartComment(int32_t aNamespaceID,
+ nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ txStylesheetCompilerState& aState)
+{
+ nsAutoPtr<txInstruction> instr(new txPushStringHandler(true));
+ nsresult rv = aState.addInstruction(Move(instr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+static nsresult
+txFnEndComment(txStylesheetCompilerState& aState)
+{
+ nsAutoPtr<txInstruction> instr(new txComment);
+ nsresult rv = aState.addInstruction(Move(instr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+/*
+ xsl:copy
+
+ txCopy -+
+ txInsertAttrSet | one for each qname in use-attribute-sets
+ [children] |
+ txEndElement |
+ <-+
+*/
+static nsresult
+txFnStartCopy(int32_t aNamespaceID,
+ nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ txStylesheetCompilerState& aState)
+{
+ nsAutoPtr<txCopy> copy(new txCopy);
+ nsresult rv = aState.pushPtr(copy, aState.eCopy);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoPtr<txInstruction> instr(copy.forget());
+ rv = aState.addInstruction(Move(instr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = parseUseAttrSets(aAttributes, aAttrCount, false, aState);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+static nsresult
+txFnEndCopy(txStylesheetCompilerState& aState)
+{
+ nsAutoPtr<txInstruction> instr(new txEndElement);
+ nsresult rv = aState.addInstruction(Move(instr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ txCopy* copy = static_cast<txCopy*>(aState.popPtr(aState.eCopy));
+ rv = aState.addGotoTarget(&copy->mBailTarget);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+/*
+ xsl:copy-of
+
+ txCopyOf
+*/
+static nsresult
+txFnStartCopyOf(int32_t aNamespaceID,
+ nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ txStylesheetCompilerState& aState)
+{
+ nsresult rv = NS_OK;
+
+ nsAutoPtr<Expr> select;
+ rv = getExprAttr(aAttributes, aAttrCount, nsGkAtoms::select, true,
+ aState, select);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoPtr<txInstruction> instr(new txCopyOf(Move(select)));
+ rv = aState.addInstruction(Move(instr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return aState.pushHandlerTable(gTxIgnoreHandler);
+}
+
+static nsresult
+txFnEndCopyOf(txStylesheetCompilerState& aState)
+{
+ aState.popHandlerTable();
+ return NS_OK;
+}
+
+/*
+ xsl:element
+
+ txStartElement
+ txInsertAttrSet one for each qname in use-attribute-sets
+ [children]
+ txEndElement
+*/
+static nsresult
+txFnStartElement(int32_t aNamespaceID,
+ nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ txStylesheetCompilerState& aState)
+{
+ nsresult rv = NS_OK;
+
+ nsAutoPtr<Expr> name;
+ rv = getAVTAttr(aAttributes, aAttrCount, nsGkAtoms::name, true,
+ aState, name);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoPtr<Expr> nspace;
+ rv = getAVTAttr(aAttributes, aAttrCount, nsGkAtoms::_namespace, false,
+ aState, nspace);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoPtr<txInstruction> instr(
+ new txStartElement(Move(name), Move(nspace),
+ aState.mElementContext->mMappings));
+ rv = aState.addInstruction(Move(instr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = parseUseAttrSets(aAttributes, aAttrCount, false, aState);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+static nsresult
+txFnEndElement(txStylesheetCompilerState& aState)
+{
+ nsAutoPtr<txInstruction> instr(new txEndElement);
+ nsresult rv = aState.addInstruction(Move(instr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+/*
+ xsl:fallback
+
+ [children]
+*/
+static nsresult
+txFnStartFallback(int32_t aNamespaceID,
+ nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ txStylesheetCompilerState& aState)
+{
+ aState.mSearchingForFallback = false;
+
+ return aState.pushHandlerTable(gTxTemplateHandler);
+}
+
+static nsresult
+txFnEndFallback(txStylesheetCompilerState& aState)
+{
+ aState.popHandlerTable();
+
+ NS_ASSERTION(!aState.mSearchingForFallback,
+ "bad nesting of unknown-instruction and fallback handlers");
+ return NS_OK;
+}
+
+/*
+ xsl:for-each
+
+ txPushNewContext -+ (holds <xsl:sort>s)
+ txPushNullTemplateRule <-+ |
+ [children] | |
+ txLoopNodeSet -+ |
+ <-+
+*/
+static nsresult
+txFnStartForEach(int32_t aNamespaceID,
+ nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ txStylesheetCompilerState& aState)
+{
+ nsresult rv = NS_OK;
+
+ nsAutoPtr<Expr> select;
+ rv = getExprAttr(aAttributes, aAttrCount, nsGkAtoms::select, true,
+ aState, select);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoPtr<txPushNewContext> pushcontext(new txPushNewContext(Move(select)));
+ rv = aState.pushPtr(pushcontext, aState.ePushNewContext);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = aState.pushSorter(pushcontext);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoPtr<txInstruction> instr(pushcontext.forget());
+ rv = aState.addInstruction(Move(instr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ instr = new txPushNullTemplateRule;
+ rv = aState.pushPtr(instr, aState.ePushNullTemplateRule);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = aState.addInstruction(Move(instr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return aState.pushHandlerTable(gTxForEachHandler);
+}
+
+static nsresult
+txFnEndForEach(txStylesheetCompilerState& aState)
+{
+ aState.popHandlerTable();
+
+ // This is a txPushNullTemplateRule
+ txInstruction* pnullrule =
+ static_cast<txInstruction*>(aState.popPtr(aState.ePushNullTemplateRule));
+
+ nsAutoPtr<txInstruction> instr(new txLoopNodeSet(pnullrule));
+ nsresult rv = aState.addInstruction(Move(instr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aState.popSorter();
+ txPushNewContext* pushcontext =
+ static_cast<txPushNewContext*>(aState.popPtr(aState.ePushNewContext));
+ aState.addGotoTarget(&pushcontext->mBailTarget);
+
+ return NS_OK;
+}
+
+static nsresult
+txFnStartElementContinueTemplate(int32_t aNamespaceID,
+ nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ txStylesheetCompilerState& aState)
+{
+ aState.mHandlerTable = gTxTemplateHandler;
+
+ return NS_XSLT_GET_NEW_HANDLER;
+}
+
+static nsresult
+txFnTextContinueTemplate(const nsAString& aStr,
+ txStylesheetCompilerState& aState)
+{
+ TX_RETURN_IF_WHITESPACE(aStr, aState);
+
+ aState.mHandlerTable = gTxTemplateHandler;
+
+ return NS_XSLT_GET_NEW_HANDLER;
+}
+
+/*
+ xsl:if
+
+ txConditionalGoto -+
+ [children] |
+ <-+
+*/
+static nsresult
+txFnStartIf(int32_t aNamespaceID,
+ nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ txStylesheetCompilerState& aState)
+{
+ nsresult rv = NS_OK;
+
+ nsAutoPtr<Expr> test;
+ rv = getExprAttr(aAttributes, aAttrCount, nsGkAtoms::test, true,
+ aState, test);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoPtr<txConditionalGoto> condGoto(new txConditionalGoto(Move(test),
+ nullptr));
+ rv = aState.pushPtr(condGoto, aState.eConditionalGoto);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoPtr<txInstruction> instr(condGoto.forget());
+ rv = aState.addInstruction(Move(instr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+static nsresult
+txFnEndIf(txStylesheetCompilerState& aState)
+{
+ txConditionalGoto* condGoto =
+ static_cast<txConditionalGoto*>(aState.popPtr(aState.eConditionalGoto));
+ return aState.addGotoTarget(&condGoto->mTarget);
+}
+
+/*
+ xsl:message
+
+ txPushStringHandler
+ [children]
+ txMessage
+*/
+static nsresult
+txFnStartMessage(int32_t aNamespaceID,
+ nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ txStylesheetCompilerState& aState)
+{
+ nsAutoPtr<txInstruction> instr(new txPushStringHandler(false));
+ nsresult rv = aState.addInstruction(Move(instr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ txThreeState term;
+ rv = getYesNoAttr(aAttributes, aAttrCount, nsGkAtoms::terminate,
+ false, aState, term);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ instr = new txMessage(term == eTrue);
+ rv = aState.pushObject(instr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ instr.forget();
+
+ return NS_OK;
+}
+
+static nsresult
+txFnEndMessage(txStylesheetCompilerState& aState)
+{
+ nsAutoPtr<txInstruction> instr(static_cast<txInstruction*>(aState.popObject()));
+ nsresult rv = aState.addInstruction(Move(instr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+/*
+ xsl:number
+
+ txNumber
+*/
+static nsresult
+txFnStartNumber(int32_t aNamespaceID,
+ nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ txStylesheetCompilerState& aState)
+{
+ nsresult rv = NS_OK;
+
+ nsCOMPtr<nsIAtom> levelAtom;
+ rv = getAtomAttr(aAttributes, aAttrCount, nsGkAtoms::level, false,
+ aState, getter_AddRefs(levelAtom));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ txXSLTNumber::LevelType level = txXSLTNumber::eLevelSingle;
+ if (levelAtom == nsGkAtoms::multiple) {
+ level = txXSLTNumber::eLevelMultiple;
+ }
+ else if (levelAtom == nsGkAtoms::any) {
+ level = txXSLTNumber::eLevelAny;
+ }
+ else if (levelAtom && levelAtom != nsGkAtoms::single && !aState.fcp()) {
+ return NS_ERROR_XSLT_PARSE_FAILURE;
+ }
+
+ nsAutoPtr<txPattern> count;
+ rv = getPatternAttr(aAttributes, aAttrCount, nsGkAtoms::count, false,
+ aState, count);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoPtr<txPattern> from;
+ rv = getPatternAttr(aAttributes, aAttrCount, nsGkAtoms::from, false,
+ aState, from);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoPtr<Expr> value;
+ rv = getExprAttr(aAttributes, aAttrCount, nsGkAtoms::value, false,
+ aState, value);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoPtr<Expr> format;
+ rv = getAVTAttr(aAttributes, aAttrCount, nsGkAtoms::format, false,
+ aState, format);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoPtr<Expr> lang;
+ rv = getAVTAttr(aAttributes, aAttrCount, nsGkAtoms::lang, false,
+ aState, lang);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoPtr<Expr> letterValue;
+ rv = getAVTAttr(aAttributes, aAttrCount, nsGkAtoms::letterValue, false,
+ aState, letterValue);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoPtr<Expr> groupingSeparator;
+ rv = getAVTAttr(aAttributes, aAttrCount, nsGkAtoms::groupingSeparator,
+ false, aState, groupingSeparator);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoPtr<Expr> groupingSize;
+ rv = getAVTAttr(aAttributes, aAttrCount, nsGkAtoms::groupingSize,
+ false, aState, groupingSize);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoPtr<txInstruction> instr(new txNumber(level, Move(count), Move(from),
+ Move(value), Move(format),
+ Move(groupingSeparator),
+ Move(groupingSize)));
+ rv = aState.addInstruction(Move(instr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return aState.pushHandlerTable(gTxIgnoreHandler);
+}
+
+static nsresult
+txFnEndNumber(txStylesheetCompilerState& aState)
+{
+ aState.popHandlerTable();
+
+ return NS_OK;
+}
+
+/*
+ xsl:otherwise
+
+ (see xsl:choose)
+*/
+static nsresult
+txFnStartOtherwise(int32_t aNamespaceID,
+ nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ txStylesheetCompilerState& aState)
+{
+ return aState.pushHandlerTable(gTxTemplateHandler);
+}
+
+static nsresult
+txFnEndOtherwise(txStylesheetCompilerState& aState)
+{
+ aState.popHandlerTable();
+ aState.mHandlerTable = gTxIgnoreHandler; // XXX should be gTxErrorHandler
+
+ return NS_OK;
+}
+
+/*
+ xsl:param
+
+ txCheckParam --+
+ txPushRTFHandler | --- (for RTF-parameters)
+ [children] | /
+ txSetVariable |
+ <-+
+*/
+static nsresult
+txFnStartParam(int32_t aNamespaceID,
+ nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ txStylesheetCompilerState& aState)
+{
+ nsresult rv = NS_OK;
+
+ txExpandedName name;
+ rv = getQNameAttr(aAttributes, aAttrCount, nsGkAtoms::name, true,
+ aState, name);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoPtr<txCheckParam> checkParam(new txCheckParam(name));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = aState.pushPtr(checkParam, aState.eCheckParam);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoPtr<txInstruction> instr(checkParam.forget());
+ rv = aState.addInstruction(Move(instr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoPtr<Expr> select;
+ rv = getExprAttr(aAttributes, aAttrCount, nsGkAtoms::select, false,
+ aState, select);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoPtr<txSetVariable> var(new txSetVariable(name, Move(select)));
+ if (var->mValue) {
+ // XXX should be gTxErrorHandler?
+ rv = aState.pushHandlerTable(gTxIgnoreHandler);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ else {
+ rv = aState.pushHandlerTable(gTxVariableHandler);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ rv = aState.pushObject(var);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ var.forget();
+
+ return NS_OK;
+}
+
+static nsresult
+txFnEndParam(txStylesheetCompilerState& aState)
+{
+ nsAutoPtr<txSetVariable> var(static_cast<txSetVariable*>
+ (aState.popObject()));
+ txHandlerTable* prev = aState.mHandlerTable;
+ aState.popHandlerTable();
+
+ if (prev == gTxVariableHandler) {
+ // No children were found.
+ NS_ASSERTION(!var->mValue,
+ "There shouldn't be a select-expression here");
+ var->mValue = new txLiteralExpr(EmptyString());
+ }
+
+ nsresult rv = aState.addVariable(var->mName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoPtr<txInstruction> instr(var.forget());
+ rv = aState.addInstruction(Move(instr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ txCheckParam* checkParam =
+ static_cast<txCheckParam*>(aState.popPtr(aState.eCheckParam));
+ aState.addGotoTarget(&checkParam->mBailTarget);
+
+ return NS_OK;
+}
+
+/*
+ xsl:processing-instruction
+
+ txPushStringHandler
+ [children]
+ txProcessingInstruction
+*/
+static nsresult
+txFnStartPI(int32_t aNamespaceID,
+ nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ txStylesheetCompilerState& aState)
+{
+ nsAutoPtr<txInstruction> instr(new txPushStringHandler(true));
+ nsresult rv = aState.addInstruction(Move(instr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoPtr<Expr> name;
+ rv = getAVTAttr(aAttributes, aAttrCount, nsGkAtoms::name, true,
+ aState, name);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ instr = new txProcessingInstruction(Move(name));
+ rv = aState.pushObject(instr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ instr.forget();
+
+ return NS_OK;
+}
+
+static nsresult
+txFnEndPI(txStylesheetCompilerState& aState)
+{
+ nsAutoPtr<txInstruction> instr(static_cast<txInstruction*>
+ (aState.popObject()));
+ nsresult rv = aState.addInstruction(Move(instr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+/*
+ xsl:sort
+
+ (no instructions)
+*/
+static nsresult
+txFnStartSort(int32_t aNamespaceID,
+ nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ txStylesheetCompilerState& aState)
+{
+ nsresult rv = NS_OK;
+
+ nsAutoPtr<Expr> select;
+ rv = getExprAttr(aAttributes, aAttrCount, nsGkAtoms::select, false,
+ aState, select);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!select) {
+ nsAutoPtr<txNodeTest> nt(
+ new txNodeTypeTest(txNodeTypeTest::NODE_TYPE));
+
+ select = new LocationStep(nt, LocationStep::SELF_AXIS);
+
+ nt.forget();
+ }
+
+ nsAutoPtr<Expr> lang;
+ rv = getAVTAttr(aAttributes, aAttrCount, nsGkAtoms::lang, false,
+ aState, lang);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoPtr<Expr> dataType;
+ rv = getAVTAttr(aAttributes, aAttrCount, nsGkAtoms::dataType, false,
+ aState, dataType);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoPtr<Expr> order;
+ rv = getAVTAttr(aAttributes, aAttrCount, nsGkAtoms::order, false,
+ aState, order);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoPtr<Expr> caseOrder;
+ rv = getAVTAttr(aAttributes, aAttrCount, nsGkAtoms::caseOrder, false,
+ aState, caseOrder);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = aState.mSorter->addSort(Move(select), Move(lang), Move(dataType),
+ Move(order), Move(caseOrder));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return aState.pushHandlerTable(gTxIgnoreHandler);
+}
+
+static nsresult
+txFnEndSort(txStylesheetCompilerState& aState)
+{
+ aState.popHandlerTable();
+
+ return NS_OK;
+}
+
+/*
+ xsl:text
+
+ [children] (only txText)
+*/
+static nsresult
+txFnStartText(int32_t aNamespaceID,
+ nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ txStylesheetCompilerState& aState)
+{
+ NS_ASSERTION(!aState.mDOE, "nested d-o-e elements should not happen");
+
+ nsresult rv = NS_OK;
+ txThreeState doe;
+ rv = getYesNoAttr(aAttributes, aAttrCount,
+ nsGkAtoms::disableOutputEscaping, false, aState,
+ doe);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aState.mDOE = doe == eTrue;
+
+ return aState.pushHandlerTable(gTxTextHandler);
+}
+
+static nsresult
+txFnEndText(txStylesheetCompilerState& aState)
+{
+ aState.mDOE = false;
+ aState.popHandlerTable();
+ return NS_OK;
+}
+
+static nsresult
+txFnTextText(const nsAString& aStr, txStylesheetCompilerState& aState)
+{
+ nsAutoPtr<txInstruction> instr(new txText(aStr, aState.mDOE));
+ nsresult rv = aState.addInstruction(Move(instr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+/*
+ xsl:value-of
+
+ txValueOf
+*/
+static nsresult
+txFnStartValueOf(int32_t aNamespaceID,
+ nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ txStylesheetCompilerState& aState)
+{
+ nsresult rv = NS_OK;
+
+ txThreeState doe;
+ rv = getYesNoAttr(aAttributes, aAttrCount,
+ nsGkAtoms::disableOutputEscaping, false, aState,
+ doe);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoPtr<Expr> select;
+ rv = getExprAttr(aAttributes, aAttrCount, nsGkAtoms::select, true,
+ aState, select);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoPtr<txInstruction> instr(new txValueOf(Move(select), doe == eTrue));
+ rv = aState.addInstruction(Move(instr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return aState.pushHandlerTable(gTxIgnoreHandler);
+}
+
+static nsresult
+txFnEndValueOf(txStylesheetCompilerState& aState)
+{
+ aState.popHandlerTable();
+ return NS_OK;
+}
+
+/*
+ xsl:variable
+
+ txPushRTFHandler --- (for RTF-parameters)
+ [children] /
+ txSetVariable
+*/
+static nsresult
+txFnStartVariable(int32_t aNamespaceID,
+ nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ txStylesheetCompilerState& aState)
+{
+ nsresult rv = NS_OK;
+
+ txExpandedName name;
+ rv = getQNameAttr(aAttributes, aAttrCount, nsGkAtoms::name, true,
+ aState, name);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoPtr<Expr> select;
+ rv = getExprAttr(aAttributes, aAttrCount, nsGkAtoms::select, false,
+ aState, select);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoPtr<txSetVariable> var(new txSetVariable(name, Move(select)));
+ if (var->mValue) {
+ // XXX should be gTxErrorHandler?
+ rv = aState.pushHandlerTable(gTxIgnoreHandler);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ else {
+ rv = aState.pushHandlerTable(gTxVariableHandler);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ rv = aState.pushObject(var);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ var.forget();
+
+ return NS_OK;
+}
+
+static nsresult
+txFnEndVariable(txStylesheetCompilerState& aState)
+{
+ nsAutoPtr<txSetVariable> var(static_cast<txSetVariable*>
+ (aState.popObject()));
+
+ txHandlerTable* prev = aState.mHandlerTable;
+ aState.popHandlerTable();
+
+ if (prev == gTxVariableHandler) {
+ // No children were found.
+ NS_ASSERTION(!var->mValue,
+ "There shouldn't be a select-expression here");
+ var->mValue = new txLiteralExpr(EmptyString());
+ }
+
+ nsresult rv = aState.addVariable(var->mName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoPtr<txInstruction> instr(var.forget());
+ rv = aState.addInstruction(Move(instr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+static nsresult
+txFnStartElementStartRTF(int32_t aNamespaceID,
+ nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ txStylesheetCompilerState& aState)
+{
+ nsAutoPtr<txInstruction> instr(new txPushRTFHandler);
+ nsresult rv = aState.addInstruction(Move(instr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aState.mHandlerTable = gTxTemplateHandler;
+
+ return NS_XSLT_GET_NEW_HANDLER;
+}
+
+static nsresult
+txFnTextStartRTF(const nsAString& aStr, txStylesheetCompilerState& aState)
+{
+ TX_RETURN_IF_WHITESPACE(aStr, aState);
+
+ nsAutoPtr<txInstruction> instr(new txPushRTFHandler);
+ nsresult rv = aState.addInstruction(Move(instr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aState.mHandlerTable = gTxTemplateHandler;
+
+ return NS_XSLT_GET_NEW_HANDLER;
+}
+
+/*
+ xsl:when
+
+ (see xsl:choose)
+*/
+static nsresult
+txFnStartWhen(int32_t aNamespaceID,
+ nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ txStylesheetCompilerState& aState)
+{
+ nsresult rv = NS_OK;
+
+ nsAutoPtr<Expr> test;
+ rv = getExprAttr(aAttributes, aAttrCount, nsGkAtoms::test, true,
+ aState, test);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoPtr<txConditionalGoto> condGoto(new txConditionalGoto(Move(test),
+ nullptr));
+ rv = aState.pushPtr(condGoto, aState.eConditionalGoto);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoPtr<txInstruction> instr(condGoto.forget());
+ rv = aState.addInstruction(Move(instr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return aState.pushHandlerTable(gTxTemplateHandler);
+}
+
+static nsresult
+txFnEndWhen(txStylesheetCompilerState& aState)
+{
+ aState.popHandlerTable();
+ nsAutoPtr<txGoTo> gotoinstr(new txGoTo(nullptr));
+ nsresult rv = aState.mChooseGotoList->add(gotoinstr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoPtr<txInstruction> instr(gotoinstr.forget());
+ rv = aState.addInstruction(Move(instr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ txConditionalGoto* condGoto =
+ static_cast<txConditionalGoto*>(aState.popPtr(aState.eConditionalGoto));
+ rv = aState.addGotoTarget(&condGoto->mTarget);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+/*
+ xsl:with-param
+
+ txPushRTFHandler -- for RTF-parameters
+ [children] /
+ txSetParam
+*/
+static nsresult
+txFnStartWithParam(int32_t aNamespaceID,
+ nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ txStylesheetCompilerState& aState)
+{
+ nsresult rv = NS_OK;
+
+ txExpandedName name;
+ rv = getQNameAttr(aAttributes, aAttrCount, nsGkAtoms::name, true,
+ aState, name);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoPtr<Expr> select;
+ rv = getExprAttr(aAttributes, aAttrCount, nsGkAtoms::select, false,
+ aState, select);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoPtr<txSetParam> var(new txSetParam(name, Move(select)));
+ if (var->mValue) {
+ // XXX should be gTxErrorHandler?
+ rv = aState.pushHandlerTable(gTxIgnoreHandler);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ else {
+ rv = aState.pushHandlerTable(gTxVariableHandler);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ rv = aState.pushObject(var);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ var.forget();
+
+ return NS_OK;
+}
+
+static nsresult
+txFnEndWithParam(txStylesheetCompilerState& aState)
+{
+ nsAutoPtr<txSetParam> var(static_cast<txSetParam*>(aState.popObject()));
+ txHandlerTable* prev = aState.mHandlerTable;
+ aState.popHandlerTable();
+
+ if (prev == gTxVariableHandler) {
+ // No children were found.
+ NS_ASSERTION(!var->mValue,
+ "There shouldn't be a select-expression here");
+ var->mValue = new txLiteralExpr(EmptyString());
+ }
+
+ nsAutoPtr<txInstruction> instr(var.forget());
+ nsresult rv = aState.addInstruction(Move(instr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+/*
+ Unknown instruction
+
+ [fallbacks] if one or more xsl:fallbacks are found
+ or
+ txErrorInstruction otherwise
+*/
+static nsresult
+txFnStartUnknownInstruction(int32_t aNamespaceID,
+ nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ txStylesheetCompilerState& aState)
+{
+ NS_ASSERTION(!aState.mSearchingForFallback,
+ "bad nesting of unknown-instruction and fallback handlers");
+
+ if (aNamespaceID == kNameSpaceID_XSLT && !aState.fcp()) {
+ return NS_ERROR_XSLT_PARSE_FAILURE;
+ }
+
+ aState.mSearchingForFallback = true;
+
+ return aState.pushHandlerTable(gTxFallbackHandler);
+}
+
+static nsresult
+txFnEndUnknownInstruction(txStylesheetCompilerState& aState)
+{
+ aState.popHandlerTable();
+
+ if (aState.mSearchingForFallback) {
+ nsAutoPtr<txInstruction> instr(new txErrorInstruction());
+ nsresult rv = aState.addInstruction(Move(instr));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ aState.mSearchingForFallback = false;
+
+ return NS_OK;
+}
+
+/**
+ * Table Datas
+ */
+
+struct txHandlerTableData {
+ txElementHandler mOtherHandler;
+ txElementHandler mLREHandler;
+ HandleTextFn mTextHandler;
+};
+
+const txHandlerTableData gTxIgnoreTableData = {
+ // Other
+ { 0, 0, txFnStartElementIgnore, txFnEndElementIgnore },
+ // LRE
+ { 0, 0, txFnStartElementIgnore, txFnEndElementIgnore },
+ // Text
+ txFnTextIgnore
+};
+
+const txElementHandler gTxRootElementHandlers[] = {
+ { kNameSpaceID_XSLT, "stylesheet", txFnStartStylesheet, txFnEndStylesheet },
+ { kNameSpaceID_XSLT, "transform", txFnStartStylesheet, txFnEndStylesheet }
+};
+
+const txHandlerTableData gTxRootTableData = {
+ // Other
+ { 0, 0, txFnStartElementError, txFnEndElementError },
+ // LRE
+ { 0, 0, txFnStartLREStylesheet, txFnEndLREStylesheet },
+ // Text
+ txFnTextError
+};
+
+const txHandlerTableData gTxEmbedTableData = {
+ // Other
+ { 0, 0, txFnStartEmbed, txFnEndEmbed },
+ // LRE
+ { 0, 0, txFnStartEmbed, txFnEndEmbed },
+ // Text
+ txFnTextIgnore
+};
+
+const txElementHandler gTxTopElementHandlers[] = {
+ { kNameSpaceID_XSLT, "attribute-set", txFnStartAttributeSet, txFnEndAttributeSet },
+ { kNameSpaceID_XSLT, "decimal-format", txFnStartDecimalFormat, txFnEndDecimalFormat },
+ { kNameSpaceID_XSLT, "include", txFnStartInclude, txFnEndInclude },
+ { kNameSpaceID_XSLT, "key", txFnStartKey, txFnEndKey },
+ { kNameSpaceID_XSLT, "namespace-alias", txFnStartNamespaceAlias,
+ txFnEndNamespaceAlias },
+ { kNameSpaceID_XSLT, "output", txFnStartOutput, txFnEndOutput },
+ { kNameSpaceID_XSLT, "param", txFnStartTopVariable, txFnEndTopVariable },
+ { kNameSpaceID_XSLT, "preserve-space", txFnStartStripSpace, txFnEndStripSpace },
+ { kNameSpaceID_XSLT, "strip-space", txFnStartStripSpace, txFnEndStripSpace },
+ { kNameSpaceID_XSLT, "template", txFnStartTemplate, txFnEndTemplate },
+ { kNameSpaceID_XSLT, "variable", txFnStartTopVariable, txFnEndTopVariable }
+};
+
+const txHandlerTableData gTxTopTableData = {
+ // Other
+ { 0, 0, txFnStartOtherTop, txFnEndOtherTop },
+ // LRE
+ { 0, 0, txFnStartOtherTop, txFnEndOtherTop },
+ // Text
+ txFnTextIgnore
+};
+
+const txElementHandler gTxTemplateElementHandlers[] = {
+ { kNameSpaceID_XSLT, "apply-imports", txFnStartApplyImports, txFnEndApplyImports },
+ { kNameSpaceID_XSLT, "apply-templates", txFnStartApplyTemplates, txFnEndApplyTemplates },
+ { kNameSpaceID_XSLT, "attribute", txFnStartAttribute, txFnEndAttribute },
+ { kNameSpaceID_XSLT, "call-template", txFnStartCallTemplate, txFnEndCallTemplate },
+ { kNameSpaceID_XSLT, "choose", txFnStartChoose, txFnEndChoose },
+ { kNameSpaceID_XSLT, "comment", txFnStartComment, txFnEndComment },
+ { kNameSpaceID_XSLT, "copy", txFnStartCopy, txFnEndCopy },
+ { kNameSpaceID_XSLT, "copy-of", txFnStartCopyOf, txFnEndCopyOf },
+ { kNameSpaceID_XSLT, "element", txFnStartElement, txFnEndElement },
+ { kNameSpaceID_XSLT, "fallback", txFnStartElementSetIgnore, txFnEndElementSetIgnore },
+ { kNameSpaceID_XSLT, "for-each", txFnStartForEach, txFnEndForEach },
+ { kNameSpaceID_XSLT, "if", txFnStartIf, txFnEndIf },
+ { kNameSpaceID_XSLT, "message", txFnStartMessage, txFnEndMessage },
+ { kNameSpaceID_XSLT, "number", txFnStartNumber, txFnEndNumber },
+ { kNameSpaceID_XSLT, "processing-instruction", txFnStartPI, txFnEndPI },
+ { kNameSpaceID_XSLT, "text", txFnStartText, txFnEndText },
+ { kNameSpaceID_XSLT, "value-of", txFnStartValueOf, txFnEndValueOf },
+ { kNameSpaceID_XSLT, "variable", txFnStartVariable, txFnEndVariable }
+};
+
+const txHandlerTableData gTxTemplateTableData = {
+ // Other
+ { 0, 0, txFnStartUnknownInstruction, txFnEndUnknownInstruction },
+ // LRE
+ { 0, 0, txFnStartLRE, txFnEndLRE },
+ // Text
+ txFnText
+};
+
+const txHandlerTableData gTxTextTableData = {
+ // Other
+ { 0, 0, txFnStartElementError, txFnEndElementError },
+ // LRE
+ { 0, 0, txFnStartElementError, txFnEndElementError },
+ // Text
+ txFnTextText
+};
+
+const txElementHandler gTxApplyTemplatesElementHandlers[] = {
+ { kNameSpaceID_XSLT, "sort", txFnStartSort, txFnEndSort },
+ { kNameSpaceID_XSLT, "with-param", txFnStartWithParam, txFnEndWithParam }
+};
+
+const txHandlerTableData gTxApplyTemplatesTableData = {
+ // Other
+ { 0, 0, txFnStartElementSetIgnore, txFnEndElementSetIgnore }, // should this be error?
+ // LRE
+ { 0, 0, txFnStartElementSetIgnore, txFnEndElementSetIgnore },
+ // Text
+ txFnTextIgnore
+};
+
+const txElementHandler gTxCallTemplateElementHandlers[] = {
+ { kNameSpaceID_XSLT, "with-param", txFnStartWithParam, txFnEndWithParam }
+};
+
+const txHandlerTableData gTxCallTemplateTableData = {
+ // Other
+ { 0, 0, txFnStartElementSetIgnore, txFnEndElementSetIgnore }, // should this be error?
+ // LRE
+ { 0, 0, txFnStartElementSetIgnore, txFnEndElementSetIgnore },
+ // Text
+ txFnTextIgnore
+};
+
+const txHandlerTableData gTxVariableTableData = {
+ // Other
+ { 0, 0, txFnStartElementStartRTF, 0 },
+ // LRE
+ { 0, 0, txFnStartElementStartRTF, 0 },
+ // Text
+ txFnTextStartRTF
+};
+
+const txElementHandler gTxForEachElementHandlers[] = {
+ { kNameSpaceID_XSLT, "sort", txFnStartSort, txFnEndSort }
+};
+
+const txHandlerTableData gTxForEachTableData = {
+ // Other
+ { 0, 0, txFnStartElementContinueTemplate, 0 },
+ // LRE
+ { 0, 0, txFnStartElementContinueTemplate, 0 },
+ // Text
+ txFnTextContinueTemplate
+};
+
+const txHandlerTableData gTxTopVariableTableData = {
+ // Other
+ { 0, 0, txFnStartElementStartTopVar, 0 },
+ // LRE
+ { 0, 0, txFnStartElementStartTopVar, 0 },
+ // Text
+ txFnTextStartTopVar
+};
+
+const txElementHandler gTxChooseElementHandlers[] = {
+ { kNameSpaceID_XSLT, "otherwise", txFnStartOtherwise, txFnEndOtherwise },
+ { kNameSpaceID_XSLT, "when", txFnStartWhen, txFnEndWhen }
+};
+
+const txHandlerTableData gTxChooseTableData = {
+ // Other
+ { 0, 0, txFnStartElementError, 0 },
+ // LRE
+ { 0, 0, txFnStartElementError, 0 },
+ // Text
+ txFnTextError
+};
+
+const txElementHandler gTxParamElementHandlers[] = {
+ { kNameSpaceID_XSLT, "param", txFnStartParam, txFnEndParam }
+};
+
+const txHandlerTableData gTxParamTableData = {
+ // Other
+ { 0, 0, txFnStartElementContinueTemplate, 0 },
+ // LRE
+ { 0, 0, txFnStartElementContinueTemplate, 0 },
+ // Text
+ txFnTextContinueTemplate
+};
+
+const txElementHandler gTxImportElementHandlers[] = {
+ { kNameSpaceID_XSLT, "import", txFnStartImport, txFnEndImport }
+};
+
+const txHandlerTableData gTxImportTableData = {
+ // Other
+ { 0, 0, txFnStartElementContinueTopLevel, 0 },
+ // LRE
+ { 0, 0, txFnStartOtherTop, txFnEndOtherTop }, // XXX what should we do here?
+ // Text
+ txFnTextIgnore // XXX what should we do here?
+};
+
+const txElementHandler gTxAttributeSetElementHandlers[] = {
+ { kNameSpaceID_XSLT, "attribute", txFnStartAttribute, txFnEndAttribute }
+};
+
+const txHandlerTableData gTxAttributeSetTableData = {
+ // Other
+ { 0, 0, txFnStartElementError, 0 },
+ // LRE
+ { 0, 0, txFnStartElementError, 0 },
+ // Text
+ txFnTextError
+};
+
+const txElementHandler gTxFallbackElementHandlers[] = {
+ { kNameSpaceID_XSLT, "fallback", txFnStartFallback, txFnEndFallback }
+};
+
+const txHandlerTableData gTxFallbackTableData = {
+ // Other
+ { 0, 0, txFnStartElementSetIgnore, txFnEndElementSetIgnore },
+ // LRE
+ { 0, 0, txFnStartElementSetIgnore, txFnEndElementSetIgnore },
+ // Text
+ txFnTextIgnore
+};
+
+
+
+/**
+ * txHandlerTable
+ */
+txHandlerTable::txHandlerTable(const HandleTextFn aTextHandler,
+ const txElementHandler* aLREHandler,
+ const txElementHandler* aOtherHandler)
+ : mTextHandler(aTextHandler),
+ mLREHandler(aLREHandler),
+ mOtherHandler(aOtherHandler)
+{
+}
+
+nsresult
+txHandlerTable::init(const txElementHandler* aHandlers, uint32_t aCount)
+{
+ nsresult rv = NS_OK;
+
+ uint32_t i;
+ for (i = 0; i < aCount; ++i) {
+ nsCOMPtr<nsIAtom> nameAtom = NS_Atomize(aHandlers->mLocalName);
+ txExpandedName name(aHandlers->mNamespaceID, nameAtom);
+ rv = mHandlers.add(name, aHandlers);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ ++aHandlers;
+ }
+ return NS_OK;
+}
+
+const txElementHandler*
+txHandlerTable::find(int32_t aNamespaceID, nsIAtom* aLocalName)
+{
+ txExpandedName name(aNamespaceID, aLocalName);
+ const txElementHandler* handler = mHandlers.get(name);
+ if (!handler) {
+ handler = mOtherHandler;
+ }
+ return handler;
+}
+
+#define INIT_HANDLER(_name) \
+ gTx##_name##Handler = \
+ new txHandlerTable(gTx##_name##TableData.mTextHandler, \
+ &gTx##_name##TableData.mLREHandler, \
+ &gTx##_name##TableData.mOtherHandler); \
+ if (!gTx##_name##Handler) \
+ return false
+
+#define INIT_HANDLER_WITH_ELEMENT_HANDLERS(_name) \
+ INIT_HANDLER(_name); \
+ \
+ rv = gTx##_name##Handler->init(gTx##_name##ElementHandlers, \
+ ArrayLength(gTx##_name##ElementHandlers)); \
+ if (NS_FAILED(rv)) \
+ return false
+
+#define SHUTDOWN_HANDLER(_name) \
+ delete gTx##_name##Handler; \
+ gTx##_name##Handler = nullptr
+
+// static
+bool
+txHandlerTable::init()
+{
+ nsresult rv = NS_OK;
+
+ INIT_HANDLER_WITH_ELEMENT_HANDLERS(Root);
+ INIT_HANDLER(Embed);
+ INIT_HANDLER_WITH_ELEMENT_HANDLERS(Top);
+ INIT_HANDLER(Ignore);
+ INIT_HANDLER_WITH_ELEMENT_HANDLERS(Template);
+ INIT_HANDLER(Text);
+ INIT_HANDLER_WITH_ELEMENT_HANDLERS(ApplyTemplates);
+ INIT_HANDLER_WITH_ELEMENT_HANDLERS(CallTemplate);
+ INIT_HANDLER(Variable);
+ INIT_HANDLER_WITH_ELEMENT_HANDLERS(ForEach);
+ INIT_HANDLER(TopVariable);
+ INIT_HANDLER_WITH_ELEMENT_HANDLERS(Choose);
+ INIT_HANDLER_WITH_ELEMENT_HANDLERS(Param);
+ INIT_HANDLER_WITH_ELEMENT_HANDLERS(Import);
+ INIT_HANDLER_WITH_ELEMENT_HANDLERS(AttributeSet);
+ INIT_HANDLER_WITH_ELEMENT_HANDLERS(Fallback);
+
+ return true;
+}
+
+// static
+void
+txHandlerTable::shutdown()
+{
+ SHUTDOWN_HANDLER(Root);
+ SHUTDOWN_HANDLER(Embed);
+ SHUTDOWN_HANDLER(Top);
+ SHUTDOWN_HANDLER(Ignore);
+ SHUTDOWN_HANDLER(Template);
+ SHUTDOWN_HANDLER(Text);
+ SHUTDOWN_HANDLER(ApplyTemplates);
+ SHUTDOWN_HANDLER(CallTemplate);
+ SHUTDOWN_HANDLER(Variable);
+ SHUTDOWN_HANDLER(ForEach);
+ SHUTDOWN_HANDLER(TopVariable);
+ SHUTDOWN_HANDLER(Choose);
+ SHUTDOWN_HANDLER(Param);
+ SHUTDOWN_HANDLER(Import);
+ SHUTDOWN_HANDLER(AttributeSet);
+ SHUTDOWN_HANDLER(Fallback);
+}
diff --git a/dom/xslt/xslt/txStylesheetCompileHandlers.h b/dom/xslt/xslt/txStylesheetCompileHandlers.h
new file mode 100644
index 000000000..2ff71f720
--- /dev/null
+++ b/dom/xslt/xslt/txStylesheetCompileHandlers.h
@@ -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/. */
+
+#ifndef TRANSFRMX_TXSTYLESHEETCOMPILEHANDLERS_H
+#define TRANSFRMX_TXSTYLESHEETCOMPILEHANDLERS_H
+
+#include "nsError.h"
+#include "txNamespaceMap.h"
+#include "txExpandedNameMap.h"
+
+struct txStylesheetAttr;
+class txStylesheetCompilerState;
+
+typedef nsresult (*HandleStartFn) (int32_t aNamespaceID,
+ nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount,
+ txStylesheetCompilerState& aState);
+typedef nsresult (*HandleEndFn) (txStylesheetCompilerState& aState);
+typedef nsresult (*HandleTextFn) (const nsAString& aStr,
+ txStylesheetCompilerState& aState);
+
+struct txElementHandler {
+ int32_t mNamespaceID;
+ const char* mLocalName;
+ HandleStartFn mStartFunction;
+ HandleEndFn mEndFunction;
+};
+
+class txHandlerTable
+{
+public:
+ txHandlerTable(const HandleTextFn aTextHandler,
+ const txElementHandler* aLREHandler,
+ const txElementHandler* aOtherHandler);
+ nsresult init(const txElementHandler* aHandlers, uint32_t aCount);
+ const txElementHandler* find(int32_t aNamespaceID, nsIAtom* aLocalName);
+
+ const HandleTextFn mTextHandler;
+ const txElementHandler* const mLREHandler;
+
+ static bool init();
+ static void shutdown();
+
+private:
+ const txElementHandler* const mOtherHandler;
+ txExpandedNameMap<const txElementHandler> mHandlers;
+};
+
+extern txHandlerTable* gTxRootHandler;
+extern txHandlerTable* gTxEmbedHandler;
+
+#endif //TRANSFRMX_TXSTYLESHEETCOMPILEHANDLERS_H
diff --git a/dom/xslt/xslt/txStylesheetCompiler.cpp b/dom/xslt/xslt/txStylesheetCompiler.cpp
new file mode 100644
index 000000000..d22ba41ef
--- /dev/null
+++ b/dom/xslt/xslt/txStylesheetCompiler.cpp
@@ -0,0 +1,1135 @@
+/* -*- 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/Move.h"
+#include "mozilla/UniquePtr.h"
+
+#include "txStylesheetCompiler.h"
+#include "txStylesheetCompileHandlers.h"
+#include "nsGkAtoms.h"
+#include "txURIUtils.h"
+#include "nsWhitespaceTokenizer.h"
+#include "txStylesheet.h"
+#include "txInstructions.h"
+#include "txToplevelItems.h"
+#include "txExprParser.h"
+#include "txLog.h"
+#include "txPatternParser.h"
+#include "txStringUtils.h"
+#include "txXSLTFunctions.h"
+#include "nsICategoryManager.h"
+#include "nsServiceManagerUtils.h"
+#include "nsTArray.h"
+
+using namespace mozilla;
+using mozilla::net::ReferrerPolicy;
+
+txStylesheetCompiler::txStylesheetCompiler(const nsAString& aStylesheetURI,
+ ReferrerPolicy aReferrerPolicy,
+ txACompileObserver* aObserver)
+ : txStylesheetCompilerState(aObserver)
+{
+ mStatus = init(aStylesheetURI, aReferrerPolicy, nullptr, nullptr);
+}
+
+txStylesheetCompiler::txStylesheetCompiler(const nsAString& aStylesheetURI,
+ txStylesheet* aStylesheet,
+ txListIterator* aInsertPosition,
+ ReferrerPolicy aReferrerPolicy,
+ txACompileObserver* aObserver)
+ : txStylesheetCompilerState(aObserver)
+{
+ mStatus = init(aStylesheetURI, aReferrerPolicy, aStylesheet,
+ aInsertPosition);
+}
+
+void
+txStylesheetCompiler::setBaseURI(const nsString& aBaseURI)
+{
+ NS_ASSERTION(mObjectStack.size() == 1 && !mObjectStack.peek(),
+ "Execution already started");
+
+ if (NS_FAILED(mStatus)) {
+ return;
+ }
+
+ mElementContext->mBaseURI = aBaseURI;
+}
+
+nsresult
+txStylesheetCompiler::startElement(int32_t aNamespaceID, nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount)
+{
+ if (NS_FAILED(mStatus)) {
+ // ignore content after failure
+ // XXX reevaluate once expat stops on failure
+ return NS_OK;
+ }
+
+ nsresult rv = flushCharacters();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // look for new namespace mappings
+ bool hasOwnNamespaceMap = false;
+ int32_t i;
+ for (i = 0; i < aAttrCount; ++i) {
+ txStylesheetAttr* attr = aAttributes + i;
+ if (attr->mNamespaceID == kNameSpaceID_XMLNS) {
+ rv = ensureNewElementContext();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!hasOwnNamespaceMap) {
+ mElementContext->mMappings =
+ new txNamespaceMap(*mElementContext->mMappings);
+ hasOwnNamespaceMap = true;
+ }
+
+ if (attr->mLocalName == nsGkAtoms::xmlns) {
+ mElementContext->mMappings->mapNamespace(nullptr, attr->mValue);
+ }
+ else {
+ mElementContext->mMappings->
+ mapNamespace(attr->mLocalName, attr->mValue);
+ }
+ }
+ }
+
+ return startElementInternal(aNamespaceID, aLocalName, aPrefix,
+ aAttributes, aAttrCount);
+}
+
+nsresult
+txStylesheetCompiler::startElement(const char16_t *aName,
+ const char16_t **aAttrs,
+ int32_t aAttrCount)
+{
+ if (NS_FAILED(mStatus)) {
+ // ignore content after failure
+ // XXX reevaluate once expat stops on failure
+ return NS_OK;
+ }
+
+ nsresult rv = flushCharacters();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ UniquePtr<txStylesheetAttr[]> atts;
+ if (aAttrCount > 0) {
+ atts = MakeUnique<txStylesheetAttr[]>(aAttrCount);
+ }
+
+ bool hasOwnNamespaceMap = false;
+ int32_t i;
+ for (i = 0; i < aAttrCount; ++i) {
+ rv = XMLUtils::splitExpatName(aAttrs[i * 2],
+ getter_AddRefs(atts[i].mPrefix),
+ getter_AddRefs(atts[i].mLocalName),
+ &atts[i].mNamespaceID);
+ NS_ENSURE_SUCCESS(rv, rv);
+ atts[i].mValue.Append(aAttrs[i * 2 + 1]);
+
+ nsCOMPtr<nsIAtom> prefixToBind;
+ if (atts[i].mPrefix == nsGkAtoms::xmlns) {
+ prefixToBind = atts[i].mLocalName;
+ }
+ else if (atts[i].mNamespaceID == kNameSpaceID_XMLNS) {
+ prefixToBind = nsGkAtoms::_empty;
+ }
+
+ if (prefixToBind) {
+ rv = ensureNewElementContext();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!hasOwnNamespaceMap) {
+ mElementContext->mMappings =
+ new txNamespaceMap(*mElementContext->mMappings);
+ hasOwnNamespaceMap = true;
+ }
+
+ rv = mElementContext->mMappings->
+ mapNamespace(prefixToBind, atts[i].mValue);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ }
+
+ nsCOMPtr<nsIAtom> prefix, localname;
+ int32_t namespaceID;
+ rv = XMLUtils::splitExpatName(aName, getter_AddRefs(prefix),
+ getter_AddRefs(localname), &namespaceID);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return startElementInternal(namespaceID, localname, prefix, atts.get(),
+ aAttrCount);
+}
+
+nsresult
+txStylesheetCompiler::startElementInternal(int32_t aNamespaceID,
+ nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount)
+{
+ nsresult rv = NS_OK;
+ int32_t i;
+ for (i = mInScopeVariables.Length() - 1; i >= 0; --i) {
+ ++mInScopeVariables[i]->mLevel;
+ }
+
+ // Update the elementcontext if we have special attributes
+ for (i = 0; i < aAttrCount; ++i) {
+ txStylesheetAttr* attr = aAttributes + i;
+
+ // id
+ if (mEmbedStatus == eNeedEmbed &&
+ attr->mLocalName == nsGkAtoms::id &&
+ attr->mNamespaceID == kNameSpaceID_None &&
+ attr->mValue.Equals(mTarget)) {
+ // We found the right ID, signal to compile the
+ // embedded stylesheet.
+ mEmbedStatus = eInEmbed;
+ }
+
+ // xml:space
+ if (attr->mNamespaceID == kNameSpaceID_XML &&
+ attr->mLocalName == nsGkAtoms::space) {
+ rv = ensureNewElementContext();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (TX_StringEqualsAtom(attr->mValue, nsGkAtoms::preserve)) {
+ mElementContext->mPreserveWhitespace = true;
+ }
+ else if (TX_StringEqualsAtom(attr->mValue, nsGkAtoms::_default)) {
+ mElementContext->mPreserveWhitespace = false;
+ }
+ else {
+ return NS_ERROR_XSLT_PARSE_FAILURE;
+ }
+ }
+
+ // xml:base
+ if (attr->mNamespaceID == kNameSpaceID_XML &&
+ attr->mLocalName == nsGkAtoms::base &&
+ !attr->mValue.IsEmpty()) {
+ rv = ensureNewElementContext();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString uri;
+ URIUtils::resolveHref(attr->mValue, mElementContext->mBaseURI, uri);
+ mElementContext->mBaseURI = uri;
+ }
+
+ // extension-element-prefixes
+ if ((attr->mNamespaceID == kNameSpaceID_XSLT &&
+ attr->mLocalName == nsGkAtoms::extensionElementPrefixes &&
+ aNamespaceID != kNameSpaceID_XSLT) ||
+ (attr->mNamespaceID == kNameSpaceID_None &&
+ attr->mLocalName == nsGkAtoms::extensionElementPrefixes &&
+ aNamespaceID == kNameSpaceID_XSLT &&
+ (aLocalName == nsGkAtoms::stylesheet ||
+ aLocalName == nsGkAtoms::transform))) {
+ rv = ensureNewElementContext();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsWhitespaceTokenizer tok(attr->mValue);
+ while (tok.hasMoreTokens()) {
+ int32_t namespaceID = mElementContext->mMappings->
+ lookupNamespaceWithDefault(tok.nextToken());
+
+ if (namespaceID == kNameSpaceID_Unknown)
+ return NS_ERROR_XSLT_PARSE_FAILURE;
+
+ if (!mElementContext->mInstructionNamespaces.
+ AppendElement(namespaceID)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+
+ attr->mLocalName = nullptr;
+ }
+
+ // version
+ if ((attr->mNamespaceID == kNameSpaceID_XSLT &&
+ attr->mLocalName == nsGkAtoms::version &&
+ aNamespaceID != kNameSpaceID_XSLT) ||
+ (attr->mNamespaceID == kNameSpaceID_None &&
+ attr->mLocalName == nsGkAtoms::version &&
+ aNamespaceID == kNameSpaceID_XSLT &&
+ (aLocalName == nsGkAtoms::stylesheet ||
+ aLocalName == nsGkAtoms::transform))) {
+ rv = ensureNewElementContext();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (attr->mValue.EqualsLiteral("1.0")) {
+ mElementContext->mForwardsCompatibleParsing = false;
+ }
+ else {
+ mElementContext->mForwardsCompatibleParsing = true;
+ }
+ }
+ }
+
+ // Find the right elementhandler and execute it
+ bool isInstruction = false;
+ int32_t count = mElementContext->mInstructionNamespaces.Length();
+ for (i = 0; i < count; ++i) {
+ if (mElementContext->mInstructionNamespaces[i] == aNamespaceID) {
+ isInstruction = true;
+ break;
+ }
+ }
+
+ const txElementHandler* handler;
+ do {
+ handler = isInstruction ?
+ mHandlerTable->find(aNamespaceID, aLocalName) :
+ mHandlerTable->mLREHandler;
+
+ rv = (handler->mStartFunction)(aNamespaceID, aLocalName, aPrefix,
+ aAttributes, aAttrCount, *this);
+ } while (rv == NS_XSLT_GET_NEW_HANDLER);
+
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!fcp()) {
+ for (i = 0; i < aAttrCount; ++i) {
+ txStylesheetAttr& attr = aAttributes[i];
+ if (attr.mLocalName &&
+ (attr.mNamespaceID == kNameSpaceID_XSLT ||
+ (aNamespaceID == kNameSpaceID_XSLT &&
+ attr.mNamespaceID == kNameSpaceID_None))) {
+ // XXX ErrorReport: unknown attribute
+ return NS_ERROR_XSLT_PARSE_FAILURE;
+ }
+ }
+ }
+
+ rv = pushPtr(const_cast<txElementHandler*>(handler), eElementHandler);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mElementContext->mDepth++;
+
+ return NS_OK;
+}
+
+nsresult
+txStylesheetCompiler::endElement()
+{
+ if (NS_FAILED(mStatus)) {
+ // ignore content after failure
+ // XXX reevaluate once expat stops on failure
+ return NS_OK;
+ }
+
+ nsresult rv = flushCharacters();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ int32_t i;
+ for (i = mInScopeVariables.Length() - 1; i >= 0; --i) {
+ txInScopeVariable* var = mInScopeVariables[i];
+ if (!--(var->mLevel)) {
+ nsAutoPtr<txInstruction> instr(new txRemoveVariable(var->mName));
+ rv = addInstruction(Move(instr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mInScopeVariables.RemoveElementAt(i);
+ delete var;
+ }
+ }
+
+ const txElementHandler* handler =
+ const_cast<const txElementHandler*>
+ (static_cast<txElementHandler*>(popPtr(eElementHandler)));
+ rv = (handler->mEndFunction)(*this);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!--mElementContext->mDepth) {
+ // this will delete the old object
+ mElementContext = static_cast<txElementContext*>(popObject());
+ }
+
+ return NS_OK;
+}
+
+nsresult
+txStylesheetCompiler::characters(const nsAString& aStr)
+{
+ if (NS_FAILED(mStatus)) {
+ // ignore content after failure
+ // XXX reevaluate once expat stops on failure
+ return NS_OK;
+ }
+
+ mCharacters.Append(aStr);
+
+ return NS_OK;
+}
+
+nsresult
+txStylesheetCompiler::doneLoading()
+{
+ MOZ_LOG(txLog::xslt, LogLevel::Info,
+ ("Compiler::doneLoading: %s\n",
+ NS_LossyConvertUTF16toASCII(mStylesheetURI).get()));
+ if (NS_FAILED(mStatus)) {
+ return mStatus;
+ }
+
+ mDoneWithThisStylesheet = true;
+
+ return maybeDoneCompiling();
+}
+
+void
+txStylesheetCompiler::cancel(nsresult aError, const char16_t *aErrorText,
+ const char16_t *aParam)
+{
+ MOZ_LOG(txLog::xslt, LogLevel::Info,
+ ("Compiler::cancel: %s, module: %d, code %d\n",
+ NS_LossyConvertUTF16toASCII(mStylesheetURI).get(),
+ NS_ERROR_GET_MODULE(aError),
+ NS_ERROR_GET_CODE(aError)));
+ if (NS_SUCCEEDED(mStatus)) {
+ mStatus = aError;
+ }
+
+ if (mObserver) {
+ mObserver->onDoneCompiling(this, mStatus, aErrorText, aParam);
+ // This will ensure that we don't call onDoneCompiling twice. Also
+ // ensures that we don't keep the observer alive longer then necessary.
+ mObserver = nullptr;
+ }
+}
+
+txStylesheet*
+txStylesheetCompiler::getStylesheet()
+{
+ return mStylesheet;
+}
+
+nsresult
+txStylesheetCompiler::loadURI(const nsAString& aUri,
+ const nsAString& aReferrerUri,
+ ReferrerPolicy aReferrerPolicy,
+ txStylesheetCompiler* aCompiler)
+{
+ MOZ_LOG(txLog::xslt, LogLevel::Info,
+ ("Compiler::loadURI forwards %s thru %s\n",
+ NS_LossyConvertUTF16toASCII(aUri).get(),
+ NS_LossyConvertUTF16toASCII(mStylesheetURI).get()));
+ if (mStylesheetURI.Equals(aUri)) {
+ return NS_ERROR_XSLT_LOAD_RECURSION;
+ }
+ return mObserver ?
+ mObserver->loadURI(aUri, aReferrerUri, aReferrerPolicy, aCompiler) :
+ NS_ERROR_FAILURE;
+}
+
+void
+txStylesheetCompiler::onDoneCompiling(txStylesheetCompiler* aCompiler,
+ nsresult aResult,
+ const char16_t *aErrorText,
+ const char16_t *aParam)
+{
+ if (NS_FAILED(aResult)) {
+ cancel(aResult, aErrorText, aParam);
+ return;
+ }
+
+ mChildCompilerList.RemoveElement(aCompiler);
+
+ maybeDoneCompiling();
+}
+
+nsresult
+txStylesheetCompiler::flushCharacters()
+{
+ // Bail if we don't have any characters. The handler will detect
+ // ignoreable whitespace
+ if (mCharacters.IsEmpty()) {
+ return NS_OK;
+ }
+
+ nsresult rv = NS_OK;
+
+ do {
+ rv = (mHandlerTable->mTextHandler)(mCharacters, *this);
+ } while (rv == NS_XSLT_GET_NEW_HANDLER);
+
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mCharacters.Truncate();
+
+ return NS_OK;
+}
+
+nsresult
+txStylesheetCompiler::ensureNewElementContext()
+{
+ // Do we already have a new context?
+ if (!mElementContext->mDepth) {
+ return NS_OK;
+ }
+
+ nsAutoPtr<txElementContext>
+ context(new txElementContext(*mElementContext));
+ nsresult rv = pushObject(mElementContext);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mElementContext.forget();
+ mElementContext = Move(context);
+
+ return NS_OK;
+}
+
+nsresult
+txStylesheetCompiler::maybeDoneCompiling()
+{
+ if (!mDoneWithThisStylesheet || !mChildCompilerList.IsEmpty()) {
+ return NS_OK;
+ }
+
+ if (mIsTopCompiler) {
+ nsresult rv = mStylesheet->doneCompiling();
+ if (NS_FAILED(rv)) {
+ cancel(rv);
+ return rv;
+ }
+ }
+
+ if (mObserver) {
+ mObserver->onDoneCompiling(this, mStatus);
+ // This will ensure that we don't call onDoneCompiling twice. Also
+ // ensures that we don't keep the observer alive longer then necessary.
+ mObserver = nullptr;
+ }
+
+ return NS_OK;
+}
+
+/**
+ * txStylesheetCompilerState
+ */
+
+
+txStylesheetCompilerState::txStylesheetCompilerState(txACompileObserver* aObserver)
+ : mHandlerTable(nullptr),
+ mSorter(nullptr),
+ mDOE(false),
+ mSearchingForFallback(false),
+ mDisAllowed(0),
+ mObserver(aObserver),
+ mEmbedStatus(eNoEmbed),
+ mDoneWithThisStylesheet(false),
+ mNextInstrPtr(nullptr),
+ mToplevelIterator(nullptr)
+{
+ // Embedded stylesheets have another handler, which is set in
+ // txStylesheetCompiler::init if the baseURI has a fragment identifier.
+ mHandlerTable = gTxRootHandler;
+
+}
+
+nsresult
+txStylesheetCompilerState::init(const nsAString& aStylesheetURI,
+ ReferrerPolicy aReferrerPolicy,
+ txStylesheet* aStylesheet,
+ txListIterator* aInsertPosition)
+{
+ NS_ASSERTION(!aStylesheet || aInsertPosition,
+ "must provide insertposition if loading subsheet");
+ mStylesheetURI = aStylesheetURI;
+ mReferrerPolicy = aReferrerPolicy;
+ // Check for fragment identifier of an embedded stylesheet.
+ int32_t fragment = aStylesheetURI.FindChar('#') + 1;
+ if (fragment > 0) {
+ int32_t fragmentLength = aStylesheetURI.Length() - fragment;
+ if (fragmentLength > 0) {
+ // This is really an embedded stylesheet, not just a
+ // "url#". We may want to unescape the fragment.
+ mTarget = Substring(aStylesheetURI, (uint32_t)fragment,
+ fragmentLength);
+ mEmbedStatus = eNeedEmbed;
+ mHandlerTable = gTxEmbedHandler;
+ }
+ }
+ nsresult rv = NS_OK;
+ if (aStylesheet) {
+ mStylesheet = aStylesheet;
+ mToplevelIterator = *aInsertPosition;
+ mIsTopCompiler = false;
+ }
+ else {
+ mStylesheet = new txStylesheet;
+ rv = mStylesheet->init();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mToplevelIterator =
+ txListIterator(&mStylesheet->mRootFrame->mToplevelItems);
+ mToplevelIterator.next(); // go to the end of the list
+ mIsTopCompiler = true;
+ }
+
+ mElementContext = new txElementContext(aStylesheetURI);
+ NS_ENSURE_TRUE(mElementContext->mMappings, NS_ERROR_OUT_OF_MEMORY);
+
+ // Push the "old" txElementContext
+ rv = pushObject(0);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+
+txStylesheetCompilerState::~txStylesheetCompilerState()
+{
+ while (!mObjectStack.isEmpty()) {
+ delete popObject();
+ }
+
+ int32_t i;
+ for (i = mInScopeVariables.Length() - 1; i >= 0; --i) {
+ delete mInScopeVariables[i];
+ }
+}
+
+nsresult
+txStylesheetCompilerState::pushHandlerTable(txHandlerTable* aTable)
+{
+ nsresult rv = pushPtr(mHandlerTable, eHandlerTable);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mHandlerTable = aTable;
+
+ return NS_OK;
+}
+
+void
+txStylesheetCompilerState::popHandlerTable()
+{
+ mHandlerTable = static_cast<txHandlerTable*>(popPtr(eHandlerTable));
+}
+
+nsresult
+txStylesheetCompilerState::pushSorter(txPushNewContext* aSorter)
+{
+ nsresult rv = pushPtr(mSorter, ePushNewContext);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mSorter = aSorter;
+
+ return NS_OK;
+}
+
+void
+txStylesheetCompilerState::popSorter()
+{
+ mSorter = static_cast<txPushNewContext*>(popPtr(ePushNewContext));
+}
+
+nsresult
+txStylesheetCompilerState::pushChooseGotoList()
+{
+ nsresult rv = pushObject(mChooseGotoList);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mChooseGotoList.forget();
+ mChooseGotoList = new txList;
+
+ return NS_OK;
+}
+
+void
+txStylesheetCompilerState::popChooseGotoList()
+{
+ // this will delete the old value
+ mChooseGotoList = static_cast<txList*>(popObject());
+}
+
+nsresult
+txStylesheetCompilerState::pushObject(txObject* aObject)
+{
+ return mObjectStack.push(aObject);
+}
+
+txObject*
+txStylesheetCompilerState::popObject()
+{
+ return static_cast<txObject*>(mObjectStack.pop());
+}
+
+nsresult
+txStylesheetCompilerState::pushPtr(void* aPtr, enumStackType aType)
+{
+#ifdef TX_DEBUG_STACK
+ MOZ_LOG(txLog::xslt, LogLevel::Debug, ("pushPtr: 0x%x type %u\n", aPtr, aType));
+#endif
+ mTypeStack.AppendElement(aType);
+ return mOtherStack.push(aPtr);
+}
+
+void*
+txStylesheetCompilerState::popPtr(enumStackType aType)
+{
+ uint32_t stacklen = mTypeStack.Length();
+ if (stacklen == 0) {
+ NS_RUNTIMEABORT("Attempt to pop when type stack is empty");
+ }
+
+ enumStackType type = mTypeStack.ElementAt(stacklen - 1);
+ mTypeStack.RemoveElementAt(stacklen - 1);
+ void* value = mOtherStack.pop();
+
+#ifdef TX_DEBUG_STACK
+ MOZ_LOG(txLog::xslt, LogLevel::Debug, ("popPtr: 0x%x type %u requested %u\n", value, type, aType));
+#endif
+
+ if (type != aType) {
+ NS_RUNTIMEABORT("Expected type does not match top element type");
+ }
+
+ return value;
+}
+
+nsresult
+txStylesheetCompilerState::addToplevelItem(txToplevelItem* aItem)
+{
+ return mToplevelIterator.addBefore(aItem);
+}
+
+nsresult
+txStylesheetCompilerState::openInstructionContainer(txInstructionContainer* aContainer)
+{
+ NS_PRECONDITION(!mNextInstrPtr, "can't nest instruction-containers");
+
+ mNextInstrPtr = aContainer->mFirstInstruction.StartAssignment();
+ return NS_OK;
+}
+
+void
+txStylesheetCompilerState::closeInstructionContainer()
+{
+ NS_ASSERTION(mGotoTargetPointers.IsEmpty(),
+ "GotoTargets still exists, did you forget to add txReturn?");
+ mNextInstrPtr = 0;
+}
+
+nsresult
+txStylesheetCompilerState::addInstruction(nsAutoPtr<txInstruction>&& aInstruction)
+{
+ NS_PRECONDITION(mNextInstrPtr, "adding instruction outside container");
+
+ txInstruction* newInstr = aInstruction;
+
+ *mNextInstrPtr = aInstruction.forget();
+ mNextInstrPtr = newInstr->mNext.StartAssignment();
+
+ uint32_t i, count = mGotoTargetPointers.Length();
+ for (i = 0; i < count; ++i) {
+ *mGotoTargetPointers[i] = newInstr;
+ }
+ mGotoTargetPointers.Clear();
+
+ return NS_OK;
+}
+
+nsresult
+txStylesheetCompilerState::loadIncludedStylesheet(const nsAString& aURI)
+{
+ MOZ_LOG(txLog::xslt, LogLevel::Info,
+ ("CompilerState::loadIncludedStylesheet: %s\n",
+ NS_LossyConvertUTF16toASCII(aURI).get()));
+ if (mStylesheetURI.Equals(aURI)) {
+ return NS_ERROR_XSLT_LOAD_RECURSION;
+ }
+ NS_ENSURE_TRUE(mObserver, NS_ERROR_NOT_IMPLEMENTED);
+
+ nsAutoPtr<txToplevelItem> item(new txDummyItem);
+ NS_ENSURE_TRUE(item, NS_ERROR_OUT_OF_MEMORY);
+
+ nsresult rv = mToplevelIterator.addBefore(item);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ item.forget();
+
+ // step back to the dummy-item
+ mToplevelIterator.previous();
+
+ txACompileObserver* observer = static_cast<txStylesheetCompiler*>(this);
+
+ RefPtr<txStylesheetCompiler> compiler =
+ new txStylesheetCompiler(aURI, mStylesheet, &mToplevelIterator,
+ mReferrerPolicy, observer);
+ NS_ENSURE_TRUE(compiler, NS_ERROR_OUT_OF_MEMORY);
+
+ // step forward before calling the observer in case of syncronous loading
+ mToplevelIterator.next();
+
+ if (mChildCompilerList.AppendElement(compiler) == nullptr) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ rv = mObserver->loadURI(aURI, mStylesheetURI, mReferrerPolicy, compiler);
+ if (NS_FAILED(rv)) {
+ mChildCompilerList.RemoveElement(compiler);
+ }
+
+ return rv;
+}
+
+nsresult
+txStylesheetCompilerState::loadImportedStylesheet(const nsAString& aURI,
+ txStylesheet::ImportFrame* aFrame)
+{
+ MOZ_LOG(txLog::xslt, LogLevel::Info,
+ ("CompilerState::loadImportedStylesheet: %s\n",
+ NS_LossyConvertUTF16toASCII(aURI).get()));
+ if (mStylesheetURI.Equals(aURI)) {
+ return NS_ERROR_XSLT_LOAD_RECURSION;
+ }
+ NS_ENSURE_TRUE(mObserver, NS_ERROR_NOT_IMPLEMENTED);
+
+ txListIterator iter(&aFrame->mToplevelItems);
+ iter.next(); // go to the end of the list
+
+ txACompileObserver* observer = static_cast<txStylesheetCompiler*>(this);
+
+ RefPtr<txStylesheetCompiler> compiler =
+ new txStylesheetCompiler(aURI, mStylesheet, &iter, mReferrerPolicy,
+ observer);
+ NS_ENSURE_TRUE(compiler, NS_ERROR_OUT_OF_MEMORY);
+
+ if (mChildCompilerList.AppendElement(compiler) == nullptr) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ nsresult rv = mObserver->loadURI(aURI, mStylesheetURI, mReferrerPolicy,
+ compiler);
+ if (NS_FAILED(rv)) {
+ mChildCompilerList.RemoveElement(compiler);
+ }
+
+ return rv;
+}
+
+nsresult
+txStylesheetCompilerState::addGotoTarget(txInstruction** aTargetPointer)
+{
+ if (mGotoTargetPointers.AppendElement(aTargetPointer) == nullptr) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+txStylesheetCompilerState::addVariable(const txExpandedName& aName)
+{
+ txInScopeVariable* var = new txInScopeVariable(aName);
+ if (!mInScopeVariables.AppendElement(var)) {
+ delete var;
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+txStylesheetCompilerState::resolveNamespacePrefix(nsIAtom* aPrefix,
+ int32_t& aID)
+{
+ NS_ASSERTION(aPrefix && aPrefix != nsGkAtoms::_empty,
+ "caller should handle default namespace ''");
+ aID = mElementContext->mMappings->lookupNamespace(aPrefix);
+ return (aID != kNameSpaceID_Unknown) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+/**
+ * Error Function to be used for unknown extension functions.
+ *
+ */
+class txErrorFunctionCall : public FunctionCall
+{
+public:
+ explicit txErrorFunctionCall(nsIAtom* aName)
+ : mName(aName)
+ {
+ }
+
+ TX_DECL_FUNCTION
+
+private:
+ nsCOMPtr<nsIAtom> mName;
+};
+
+nsresult
+txErrorFunctionCall::evaluate(txIEvalContext* aContext,
+ txAExprResult** aResult)
+{
+ *aResult = nullptr;
+
+ return NS_ERROR_XPATH_BAD_EXTENSION_FUNCTION;
+}
+
+Expr::ResultType
+txErrorFunctionCall::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
+txErrorFunctionCall::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
+txErrorFunctionCall::getNameAtom(nsIAtom** aAtom)
+{
+ NS_IF_ADDREF(*aAtom = mName);
+
+ return NS_OK;
+}
+#endif
+
+static nsresult
+TX_ConstructXSLTFunction(nsIAtom* aName, int32_t aNamespaceID,
+ txStylesheetCompilerState* aState,
+ FunctionCall** aFunction)
+{
+ if (aName == nsGkAtoms::document) {
+ *aFunction =
+ new DocumentFunctionCall(aState->mElementContext->mBaseURI);
+ }
+ else if (aName == nsGkAtoms::key) {
+ if (!aState->allowed(txIParseContext::KEY_FUNCTION)) {
+ return NS_ERROR_XSLT_CALL_TO_KEY_NOT_ALLOWED;
+ }
+ *aFunction =
+ new txKeyFunctionCall(aState->mElementContext->mMappings);
+ }
+ else if (aName == nsGkAtoms::formatNumber) {
+ *aFunction =
+ new txFormatNumberFunctionCall(aState->mStylesheet,
+ aState->mElementContext->mMappings);
+ }
+ else if (aName == nsGkAtoms::current) {
+ *aFunction = new CurrentFunctionCall();
+ }
+ else if (aName == nsGkAtoms::unparsedEntityUri) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ else if (aName == nsGkAtoms::generateId) {
+ *aFunction = new GenerateIdFunctionCall();
+ }
+ else if (aName == nsGkAtoms::systemProperty) {
+ *aFunction = new txXSLTEnvironmentFunctionCall(
+ txXSLTEnvironmentFunctionCall::SYSTEM_PROPERTY,
+ aState->mElementContext->mMappings);
+ }
+ else if (aName == nsGkAtoms::elementAvailable) {
+ *aFunction = new txXSLTEnvironmentFunctionCall(
+ txXSLTEnvironmentFunctionCall::ELEMENT_AVAILABLE,
+ aState->mElementContext->mMappings);
+ }
+ else if (aName == nsGkAtoms::functionAvailable) {
+ *aFunction = new txXSLTEnvironmentFunctionCall(
+ txXSLTEnvironmentFunctionCall::FUNCTION_AVAILABLE,
+ aState->mElementContext->mMappings);
+ }
+ else {
+ return NS_ERROR_XPATH_UNKNOWN_FUNCTION;
+ }
+
+ MOZ_ASSERT(*aFunction);
+ return NS_OK;
+}
+
+typedef nsresult (*txFunctionFactory)(nsIAtom* aName,
+ int32_t aNamespaceID,
+ txStylesheetCompilerState* aState,
+ FunctionCall** aResult);
+struct txFunctionFactoryMapping
+{
+ const char* const mNamespaceURI;
+ int32_t mNamespaceID;
+ txFunctionFactory mFactory;
+};
+
+extern nsresult
+TX_ConstructEXSLTFunction(nsIAtom *aName,
+ int32_t aNamespaceID,
+ txStylesheetCompilerState* aState,
+ FunctionCall **aResult);
+
+static txFunctionFactoryMapping kExtensionFunctions[] = {
+ { "", kNameSpaceID_Unknown, TX_ConstructXSLTFunction },
+ { "http://exslt.org/common", kNameSpaceID_Unknown,
+ TX_ConstructEXSLTFunction },
+ { "http://exslt.org/sets", kNameSpaceID_Unknown,
+ TX_ConstructEXSLTFunction },
+ { "http://exslt.org/strings", kNameSpaceID_Unknown,
+ TX_ConstructEXSLTFunction },
+ { "http://exslt.org/math", kNameSpaceID_Unknown,
+ TX_ConstructEXSLTFunction },
+ { "http://exslt.org/dates-and-times", kNameSpaceID_Unknown,
+ TX_ConstructEXSLTFunction }
+};
+
+extern nsresult
+TX_ResolveFunctionCallXPCOM(const nsCString &aContractID, int32_t aNamespaceID,
+ nsIAtom *aName, nsISupports *aState,
+ FunctionCall **aFunction);
+
+struct txXPCOMFunctionMapping
+{
+ int32_t mNamespaceID;
+ nsCString mContractID;
+};
+
+static nsTArray<txXPCOMFunctionMapping> *sXPCOMFunctionMappings = nullptr;
+
+static nsresult
+findFunction(nsIAtom* aName, int32_t aNamespaceID,
+ txStylesheetCompilerState* aState, FunctionCall** aResult)
+{
+ if (kExtensionFunctions[0].mNamespaceID == kNameSpaceID_Unknown) {
+ uint32_t i;
+ for (i = 0; i < ArrayLength(kExtensionFunctions); ++i) {
+ txFunctionFactoryMapping& mapping = kExtensionFunctions[i];
+ NS_ConvertASCIItoUTF16 namespaceURI(mapping.mNamespaceURI);
+ mapping.mNamespaceID =
+ txNamespaceManager::getNamespaceID(namespaceURI);
+ }
+ }
+
+ uint32_t i;
+ for (i = 0; i < ArrayLength(kExtensionFunctions); ++i) {
+ const txFunctionFactoryMapping& mapping = kExtensionFunctions[i];
+ if (mapping.mNamespaceID == aNamespaceID) {
+ return mapping.mFactory(aName, aNamespaceID, aState, aResult);
+ }
+ }
+
+ if (!sXPCOMFunctionMappings) {
+ sXPCOMFunctionMappings = new nsTArray<txXPCOMFunctionMapping>;
+ }
+
+ txXPCOMFunctionMapping *map = nullptr;
+ uint32_t count = sXPCOMFunctionMappings->Length();
+ for (i = 0; i < count; ++i) {
+ map = &sXPCOMFunctionMappings->ElementAt(i);
+ if (map->mNamespaceID == aNamespaceID) {
+ break;
+ }
+ }
+
+ if (i == count) {
+ nsresult rv;
+ nsCOMPtr<nsICategoryManager> catman =
+ do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString namespaceURI;
+ rv = txNamespaceManager::getNamespaceURI(aNamespaceID, namespaceURI);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsXPIDLCString contractID;
+ rv = catman->GetCategoryEntry("XSLT-extension-functions",
+ NS_ConvertUTF16toUTF8(namespaceURI).get(),
+ getter_Copies(contractID));
+ if (rv == NS_ERROR_NOT_AVAILABLE) {
+ return NS_ERROR_XPATH_UNKNOWN_FUNCTION;
+ }
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ map = sXPCOMFunctionMappings->AppendElement();
+ if (!map) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ map->mNamespaceID = aNamespaceID;
+ map->mContractID = contractID;
+ }
+
+ return TX_ResolveFunctionCallXPCOM(map->mContractID, aNamespaceID, aName,
+ nullptr, aResult);
+}
+
+extern bool
+TX_XSLTFunctionAvailable(nsIAtom* aName, int32_t aNameSpaceID)
+{
+ RefPtr<txStylesheetCompiler> compiler =
+ new txStylesheetCompiler(EmptyString(),
+ mozilla::net::RP_Default, nullptr);
+ NS_ENSURE_TRUE(compiler, false);
+
+ nsAutoPtr<FunctionCall> fnCall;
+
+ return NS_SUCCEEDED(findFunction(aName, aNameSpaceID, compiler,
+ getter_Transfers(fnCall)));
+}
+
+nsresult
+txStylesheetCompilerState::resolveFunctionCall(nsIAtom* aName, int32_t aID,
+ FunctionCall **aFunction)
+{
+ *aFunction = nullptr;
+
+ nsresult rv = findFunction(aName, aID, this, aFunction);
+ if (rv == NS_ERROR_XPATH_UNKNOWN_FUNCTION &&
+ (aID != kNameSpaceID_None || fcp())) {
+ *aFunction = new txErrorFunctionCall(aName);
+ rv = NS_OK;
+ }
+
+ return rv;
+}
+
+bool
+txStylesheetCompilerState::caseInsensitiveNameTests()
+{
+ return false;
+}
+
+void
+txStylesheetCompilerState::SetErrorOffset(uint32_t aOffset)
+{
+ // XXX implement me
+}
+
+/* static */
+void
+txStylesheetCompilerState::shutdown()
+{
+ delete sXPCOMFunctionMappings;
+ sXPCOMFunctionMappings = nullptr;
+}
+
+txElementContext::txElementContext(const nsAString& aBaseURI)
+ : mPreserveWhitespace(false),
+ mForwardsCompatibleParsing(true),
+ mBaseURI(aBaseURI),
+ mMappings(new txNamespaceMap),
+ mDepth(0)
+{
+ mInstructionNamespaces.AppendElement(kNameSpaceID_XSLT);
+}
+
+txElementContext::txElementContext(const txElementContext& aOther)
+ : mPreserveWhitespace(aOther.mPreserveWhitespace),
+ mForwardsCompatibleParsing(aOther.mForwardsCompatibleParsing),
+ mBaseURI(aOther.mBaseURI),
+ mMappings(aOther.mMappings),
+ mDepth(0)
+{
+ mInstructionNamespaces = aOther.mInstructionNamespaces;
+}
diff --git a/dom/xslt/xslt/txStylesheetCompiler.h b/dom/xslt/xslt/txStylesheetCompiler.h
new file mode 100644
index 000000000..7fb0f05d6
--- /dev/null
+++ b/dom/xslt/xslt/txStylesheetCompiler.h
@@ -0,0 +1,267 @@
+/* -*- 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_TXSTYLESHEETCOMPILER_H
+#define TRANSFRMX_TXSTYLESHEETCOMPILER_H
+
+#include "mozilla/Attributes.h"
+#include "txStack.h"
+#include "txXSLTPatterns.h"
+#include "txExpr.h"
+#include "txIXPathContext.h"
+#include "nsAutoPtr.h"
+#include "txStylesheet.h"
+#include "nsTArray.h"
+#include "mozilla/net/ReferrerPolicy.h"
+
+extern bool
+TX_XSLTFunctionAvailable(nsIAtom* aName, int32_t aNameSpaceID);
+
+class txHandlerTable;
+class txElementContext;
+class txInstructionContainer;
+class txInstruction;
+class txNamespaceMap;
+class txToplevelItem;
+class txPushNewContext;
+class txStylesheetCompiler;
+class txInScopeVariable;
+
+class txElementContext : public txObject
+{
+public:
+ explicit txElementContext(const nsAString& aBaseURI);
+ txElementContext(const txElementContext& aOther);
+
+ bool mPreserveWhitespace;
+ bool mForwardsCompatibleParsing;
+ nsString mBaseURI;
+ RefPtr<txNamespaceMap> mMappings;
+ nsTArray<int32_t> mInstructionNamespaces;
+ int32_t mDepth;
+};
+
+class txACompileObserver
+{
+public:
+ NS_IMETHOD_(MozExternalRefCountType) AddRef() = 0;
+ NS_IMETHOD_(MozExternalRefCountType) Release() = 0;
+
+ virtual nsresult loadURI(const nsAString& aUri,
+ const nsAString& aReferrerUri,
+ mozilla::net::ReferrerPolicy aReferrerPolicy,
+ txStylesheetCompiler* aCompiler) = 0;
+ virtual void onDoneCompiling(txStylesheetCompiler* aCompiler,
+ nsresult aResult,
+ const char16_t *aErrorText = nullptr,
+ const char16_t *aParam = nullptr) = 0;
+};
+
+#define TX_DECL_ACOMPILEOBSERVER \
+ nsresult loadURI(const nsAString& aUri, const nsAString& aReferrerUri, \
+ mozilla::net::ReferrerPolicy aReferrerPolicy, \
+ txStylesheetCompiler* aCompiler); \
+ void onDoneCompiling(txStylesheetCompiler* aCompiler, nsresult aResult, \
+ const char16_t *aErrorText = nullptr, \
+ const char16_t *aParam = nullptr);
+
+class txStylesheetCompilerState : public txIParseContext
+{
+public:
+ explicit txStylesheetCompilerState(txACompileObserver* aObserver);
+ ~txStylesheetCompilerState();
+
+ nsresult init(const nsAString& aStylesheetURI,
+ mozilla::net::ReferrerPolicy aReferrerPolicy,
+ txStylesheet* aStylesheet, txListIterator* aInsertPosition);
+
+ // Embedded stylesheets state
+ bool handleEmbeddedSheet()
+ {
+ return mEmbedStatus == eInEmbed;
+ }
+ void doneEmbedding()
+ {
+ mEmbedStatus = eHasEmbed;
+ }
+
+ // Stack functions
+ enum enumStackType
+ {
+ eElementHandler,
+ eHandlerTable,
+ eVariableItem,
+ eCopy,
+ eInstruction,
+ ePushNewContext,
+ eConditionalGoto,
+ eCheckParam,
+ ePushNullTemplateRule
+ };
+ nsresult pushHandlerTable(txHandlerTable* aTable);
+ void popHandlerTable();
+ nsresult pushSorter(txPushNewContext* aSorter);
+ void popSorter();
+ nsresult pushChooseGotoList();
+ void popChooseGotoList();
+ nsresult pushObject(txObject* aObject);
+ txObject* popObject();
+ nsresult pushPtr(void* aPtr, enumStackType aType);
+ void* popPtr(enumStackType aType);
+
+ // stylesheet functions
+ nsresult addToplevelItem(txToplevelItem* aItem);
+ nsresult openInstructionContainer(txInstructionContainer* aContainer);
+ void closeInstructionContainer();
+ nsresult addInstruction(nsAutoPtr<txInstruction>&& aInstruction);
+ nsresult loadIncludedStylesheet(const nsAString& aURI);
+ nsresult loadImportedStylesheet(const nsAString& aURI,
+ txStylesheet::ImportFrame* aFrame);
+
+ // misc
+ nsresult addGotoTarget(txInstruction** aTargetPointer);
+ nsresult addVariable(const txExpandedName& aName);
+
+ // txIParseContext
+ nsresult resolveNamespacePrefix(nsIAtom* aPrefix, int32_t& aID) override;
+ nsresult resolveFunctionCall(nsIAtom* aName, int32_t aID,
+ FunctionCall** aFunction) override;
+ bool caseInsensitiveNameTests() override;
+
+ /**
+ * Should the stylesheet be parsed in forwards compatible parsing mode.
+ */
+ bool fcp()
+ {
+ return mElementContext->mForwardsCompatibleParsing;
+ }
+
+ void SetErrorOffset(uint32_t aOffset) override;
+
+ bool allowed(Allowed aAllowed) override
+ {
+ return !(mDisAllowed & aAllowed);
+ }
+
+ bool ignoreError(nsresult aResult)
+ {
+ // Some errors shouldn't be ignored even in forwards compatible parsing
+ // mode.
+ return aResult != NS_ERROR_XSLT_CALL_TO_KEY_NOT_ALLOWED &&
+ fcp();
+ }
+
+ static void shutdown();
+
+
+ RefPtr<txStylesheet> mStylesheet;
+ txHandlerTable* mHandlerTable;
+ nsAutoPtr<txElementContext> mElementContext;
+ txPushNewContext* mSorter;
+ nsAutoPtr<txList> mChooseGotoList;
+ bool mDOE;
+ bool mSearchingForFallback;
+ uint16_t mDisAllowed;
+
+protected:
+ RefPtr<txACompileObserver> mObserver;
+ nsTArray<txInScopeVariable*> mInScopeVariables;
+ nsTArray<txStylesheetCompiler*> mChildCompilerList;
+ // embed info, target information is the ID
+ nsString mTarget;
+ enum
+ {
+ eNoEmbed,
+ eNeedEmbed,
+ eInEmbed,
+ eHasEmbed
+ } mEmbedStatus;
+ nsString mStylesheetURI;
+ bool mIsTopCompiler;
+ bool mDoneWithThisStylesheet;
+ txStack mObjectStack;
+ txStack mOtherStack;
+ nsTArray<enumStackType> mTypeStack;
+
+private:
+ txInstruction** mNextInstrPtr;
+ txListIterator mToplevelIterator;
+ nsTArray<txInstruction**> mGotoTargetPointers;
+ mozilla::net::ReferrerPolicy mReferrerPolicy;
+};
+
+struct txStylesheetAttr
+{
+ int32_t mNamespaceID;
+ nsCOMPtr<nsIAtom> mLocalName;
+ nsCOMPtr<nsIAtom> mPrefix;
+ nsString mValue;
+};
+
+class txStylesheetCompiler final : private txStylesheetCompilerState,
+ public txACompileObserver
+{
+public:
+ friend class txStylesheetCompilerState;
+ friend bool TX_XSLTFunctionAvailable(nsIAtom* aName,
+ int32_t aNameSpaceID);
+ txStylesheetCompiler(const nsAString& aStylesheetURI,
+ mozilla::net::ReferrerPolicy aReferrerPolicy,
+ txACompileObserver* aObserver);
+ txStylesheetCompiler(const nsAString& aStylesheetURI,
+ txStylesheet* aStylesheet,
+ txListIterator* aInsertPosition,
+ mozilla::net::ReferrerPolicy aReferrerPolicy,
+ txACompileObserver* aObserver);
+
+ void setBaseURI(const nsString& aBaseURI);
+
+ nsresult startElement(int32_t aNamespaceID, nsIAtom* aLocalName,
+ nsIAtom* aPrefix, txStylesheetAttr* aAttributes,
+ int32_t aAttrCount);
+ nsresult startElement(const char16_t *aName,
+ const char16_t **aAtts,
+ int32_t aAttrCount);
+ nsresult endElement();
+ nsresult characters(const nsAString& aStr);
+ nsresult doneLoading();
+
+ void cancel(nsresult aError, const char16_t *aErrorText = nullptr,
+ const char16_t *aParam = nullptr);
+
+ txStylesheet* getStylesheet();
+
+ TX_DECL_ACOMPILEOBSERVER
+ NS_INLINE_DECL_REFCOUNTING(txStylesheetCompiler)
+
+private:
+ // Private destructor, to discourage deletion outside of Release():
+ ~txStylesheetCompiler()
+ {
+ }
+
+ nsresult startElementInternal(int32_t aNamespaceID, nsIAtom* aLocalName,
+ nsIAtom* aPrefix,
+ txStylesheetAttr* aAttributes,
+ int32_t aAttrCount);
+
+ nsresult flushCharacters();
+ nsresult ensureNewElementContext();
+ nsresult maybeDoneCompiling();
+
+ nsString mCharacters;
+ nsresult mStatus;
+};
+
+class txInScopeVariable {
+public:
+ explicit txInScopeVariable(const txExpandedName& aName) : mName(aName), mLevel(1)
+ {
+ }
+ txExpandedName mName;
+ int32_t mLevel;
+};
+
+#endif
diff --git a/dom/xslt/xslt/txTextHandler.cpp b/dom/xslt/xslt/txTextHandler.cpp
new file mode 100644
index 000000000..eaf822847
--- /dev/null
+++ b/dom/xslt/xslt/txTextHandler.cpp
@@ -0,0 +1,90 @@
+/* -*- 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 "txTextHandler.h"
+#include "nsAString.h"
+
+txTextHandler::txTextHandler(bool aOnlyText) : mLevel(0),
+ mOnlyText(aOnlyText)
+{
+}
+
+nsresult
+txTextHandler::attribute(nsIAtom* aPrefix, nsIAtom* aLocalName,
+ nsIAtom* aLowercaseLocalName, int32_t aNsID,
+ const nsString& aValue)
+{
+ return NS_OK;
+}
+
+nsresult
+txTextHandler::attribute(nsIAtom* aPrefix, const nsSubstring& aLocalName,
+ const int32_t aNsID,
+ const nsString& aValue)
+{
+ return NS_OK;
+}
+
+nsresult
+txTextHandler::characters(const nsSubstring& aData, bool aDOE)
+{
+ if (mLevel == 0)
+ mValue.Append(aData);
+
+ return NS_OK;
+}
+
+nsresult
+txTextHandler::comment(const nsString& aData)
+{
+ return NS_OK;
+}
+
+nsresult
+txTextHandler::endDocument(nsresult aResult)
+{
+ return NS_OK;
+}
+
+nsresult
+txTextHandler::endElement()
+{
+ if (mOnlyText)
+ --mLevel;
+
+ return NS_OK;
+}
+
+nsresult
+txTextHandler::processingInstruction(const nsString& aTarget, const nsString& aData)
+{
+ return NS_OK;
+}
+
+nsresult
+txTextHandler::startDocument()
+{
+ return NS_OK;
+}
+
+nsresult
+txTextHandler::startElement(nsIAtom* aPrefix, nsIAtom* aLocalName,
+ nsIAtom* aLowercaseLocalName, const int32_t aNsID)
+{
+ if (mOnlyText)
+ ++mLevel;
+
+ return NS_OK;
+}
+
+nsresult
+txTextHandler::startElement(nsIAtom* aPrefix, const nsSubstring& aLocalName,
+ const int32_t aNsID)
+{
+ if (mOnlyText)
+ ++mLevel;
+
+ return NS_OK;
+}
diff --git a/dom/xslt/xslt/txTextHandler.h b/dom/xslt/xslt/txTextHandler.h
new file mode 100644
index 000000000..447caedd4
--- /dev/null
+++ b/dom/xslt/xslt/txTextHandler.h
@@ -0,0 +1,26 @@
+/* -*- 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_TEXT_HANDLER_H
+#define TRANSFRMX_TEXT_HANDLER_H
+
+#include "txXMLEventHandler.h"
+#include "nsString.h"
+
+class txTextHandler : public txAXMLEventHandler
+{
+public:
+ explicit txTextHandler(bool aOnlyText);
+
+ TX_DECL_TXAXMLEVENTHANDLER
+
+ nsString mValue;
+
+private:
+ uint32_t mLevel;
+ bool mOnlyText;
+};
+
+#endif
diff --git a/dom/xslt/xslt/txToplevelItems.cpp b/dom/xslt/xslt/txToplevelItems.cpp
new file mode 100644
index 000000000..f3737e285
--- /dev/null
+++ b/dom/xslt/xslt/txToplevelItems.cpp
@@ -0,0 +1,58 @@
+/* -*- 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 "txToplevelItems.h"
+
+#include "mozilla/Move.h"
+#include "txStylesheet.h"
+#include "txInstructions.h"
+#include "txXSLTPatterns.h"
+
+using mozilla::Move;
+
+TX_IMPL_GETTYPE(txAttributeSetItem, txToplevelItem::attributeSet)
+TX_IMPL_GETTYPE(txImportItem, txToplevelItem::import)
+TX_IMPL_GETTYPE(txOutputItem, txToplevelItem::output)
+TX_IMPL_GETTYPE(txDummyItem, txToplevelItem::dummy)
+
+TX_IMPL_GETTYPE(txStripSpaceItem, txToplevelItem::stripSpace)
+
+txStripSpaceItem::~txStripSpaceItem()
+{
+ int32_t i, count = mStripSpaceTests.Length();
+ for (i = 0; i < count; ++i) {
+ delete mStripSpaceTests[i];
+ }
+}
+
+nsresult
+txStripSpaceItem::addStripSpaceTest(txStripSpaceTest* aStripSpaceTest)
+{
+ if (!mStripSpaceTests.AppendElement(aStripSpaceTest)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ return NS_OK;
+}
+
+TX_IMPL_GETTYPE(txTemplateItem, txToplevelItem::templ)
+
+txTemplateItem::txTemplateItem(nsAutoPtr<txPattern>&& aMatch,
+ const txExpandedName& aName,
+ const txExpandedName& aMode, double aPrio)
+ : mMatch(Move(aMatch)), mName(aName),
+ mMode(aMode), mPrio(aPrio)
+{
+}
+
+TX_IMPL_GETTYPE(txVariableItem, txToplevelItem::variable)
+
+txVariableItem::txVariableItem(const txExpandedName& aName,
+ nsAutoPtr<Expr>&& aValue,
+ bool aIsParam)
+ : mName(aName), mValue(Move(aValue)),
+ mIsParam(aIsParam)
+{
+}
diff --git a/dom/xslt/xslt/txToplevelItems.h b/dom/xslt/xslt/txToplevelItems.h
new file mode 100644
index 000000000..ed4e4faef
--- /dev/null
+++ b/dom/xslt/xslt/txToplevelItems.h
@@ -0,0 +1,135 @@
+/* -*- 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_TXTOPLEVELITEMS_H
+#define TRANSFRMX_TXTOPLEVELITEMS_H
+
+#include "nsError.h"
+#include "txOutputFormat.h"
+#include "txXMLUtils.h"
+#include "txStylesheet.h"
+#include "txInstructions.h"
+
+class txPattern;
+class Expr;
+
+class txToplevelItem
+{
+public:
+ txToplevelItem()
+ {
+ MOZ_COUNT_CTOR(txToplevelItem);
+ }
+ virtual ~txToplevelItem()
+ {
+ MOZ_COUNT_DTOR(txToplevelItem);
+ }
+
+ enum type {
+ attributeSet,
+ dummy,
+ import,
+ //namespaceAlias,
+ output,
+ stripSpace, //also used for preserve-space
+ templ,
+ variable
+ };
+
+ virtual type getType() = 0;
+};
+
+#define TX_DECL_TOPLEVELITEM virtual type getType();
+#define TX_IMPL_GETTYPE(_class, _type) \
+txToplevelItem::type \
+_class::getType() { return _type;}
+
+class txInstructionContainer : public txToplevelItem
+{
+public:
+ nsAutoPtr<txInstruction> mFirstInstruction;
+};
+
+// xsl:attribute-set
+class txAttributeSetItem : public txInstructionContainer
+{
+public:
+ explicit txAttributeSetItem(const txExpandedName aName) : mName(aName)
+ {
+ }
+
+ TX_DECL_TOPLEVELITEM
+
+ txExpandedName mName;
+};
+
+// xsl:import
+class txImportItem : public txToplevelItem
+{
+public:
+ TX_DECL_TOPLEVELITEM
+
+ nsAutoPtr<txStylesheet::ImportFrame> mFrame;
+};
+
+// xsl:output
+class txOutputItem : public txToplevelItem
+{
+public:
+ TX_DECL_TOPLEVELITEM
+
+ txOutputFormat mFormat;
+};
+
+// insertionpoint for xsl:include
+class txDummyItem : public txToplevelItem
+{
+public:
+ TX_DECL_TOPLEVELITEM
+};
+
+// xsl:strip-space and xsl:preserve-space
+class txStripSpaceItem : public txToplevelItem
+{
+public:
+ ~txStripSpaceItem();
+
+ TX_DECL_TOPLEVELITEM
+
+ nsresult addStripSpaceTest(txStripSpaceTest* aStripSpaceTest);
+
+ nsTArray<txStripSpaceTest*> mStripSpaceTests;
+};
+
+// xsl:template
+class txTemplateItem : public txInstructionContainer
+{
+public:
+ txTemplateItem(nsAutoPtr<txPattern>&& aMatch, const txExpandedName& aName,
+ const txExpandedName& aMode, double aPrio);
+
+ TX_DECL_TOPLEVELITEM
+
+ nsAutoPtr<txPattern> mMatch;
+ txExpandedName mName;
+ txExpandedName mMode;
+ double mPrio;
+};
+
+// xsl:variable at top level
+class txVariableItem : public txInstructionContainer
+{
+public:
+ txVariableItem(const txExpandedName& aName, nsAutoPtr<Expr>&& aValue,
+ bool aIsParam);
+
+ TX_DECL_TOPLEVELITEM
+
+ txExpandedName mName;
+ nsAutoPtr<Expr> mValue;
+ bool mIsParam;
+};
+
+#endif //TRANSFRMX_TXTOPLEVELITEMS_H
diff --git a/dom/xslt/xslt/txUnknownHandler.cpp b/dom/xslt/xslt/txUnknownHandler.cpp
new file mode 100644
index 000000000..2771d9069
--- /dev/null
+++ b/dom/xslt/xslt/txUnknownHandler.cpp
@@ -0,0 +1,201 @@
+/* -*- 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 "txUnknownHandler.h"
+
+#include "mozilla/Move.h"
+#include "txExecutionState.h"
+#include "txStringUtils.h"
+#include "txStylesheet.h"
+#include "nsGkAtoms.h"
+
+using mozilla::Move;
+
+txUnknownHandler::txUnknownHandler(txExecutionState* aEs)
+ : mEs(aEs),
+ mFlushed(false)
+{
+ MOZ_COUNT_CTOR_INHERITED(txUnknownHandler, txBufferingHandler);
+}
+
+txUnknownHandler::~txUnknownHandler()
+{
+ MOZ_COUNT_DTOR_INHERITED(txUnknownHandler, txBufferingHandler);
+}
+
+nsresult
+txUnknownHandler::attribute(nsIAtom* aPrefix, nsIAtom* aLocalName,
+ nsIAtom* aLowercaseLocalName, int32_t aNsID,
+ const nsString& aValue)
+{
+ return mFlushed ?
+ mEs->mResultHandler->attribute(aPrefix, aLocalName,
+ aLowercaseLocalName, aNsID, aValue) :
+ txBufferingHandler::attribute(aPrefix, aLocalName,
+ aLowercaseLocalName, aNsID, aValue);
+}
+
+nsresult
+txUnknownHandler::attribute(nsIAtom* aPrefix, const nsSubstring& aLocalName,
+ const int32_t aNsID, const nsString& aValue)
+{
+ return mFlushed ?
+ mEs->mResultHandler->attribute(aPrefix, aLocalName, aNsID, aValue) :
+ txBufferingHandler::attribute(aPrefix, aLocalName, aNsID, aValue);
+}
+
+nsresult
+txUnknownHandler::characters(const nsSubstring& aData, bool aDOE)
+{
+ return mFlushed ?
+ mEs->mResultHandler->characters(aData, aDOE) :
+ txBufferingHandler::characters(aData, aDOE);
+}
+
+nsresult
+txUnknownHandler::comment(const nsString& aData)
+{
+ return mFlushed ?
+ mEs->mResultHandler->comment(aData) :
+ txBufferingHandler::comment(aData);
+}
+
+nsresult
+txUnknownHandler::endDocument(nsresult aResult)
+{
+ if (!mFlushed) {
+ if (NS_FAILED(aResult)) {
+ return NS_OK;
+ }
+
+ // This is an unusual case, no output method has been set and we
+ // didn't create a document element. Switching to XML output mode
+ // anyway.
+
+ // Make sure that mEs->mResultHandler == this is true, otherwise we'll
+ // leak mEs->mResultHandler in createHandlerAndFlush.
+ NS_ASSERTION(mEs->mResultHandler == this,
+ "We're leaking mEs->mResultHandler.");
+
+ nsresult rv = createHandlerAndFlush(false, EmptyString(),
+ kNameSpaceID_None);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return mEs->mResultHandler->endDocument(aResult);
+}
+
+nsresult
+txUnknownHandler::endElement()
+{
+ return mFlushed ?
+ mEs->mResultHandler->endElement() :
+ txBufferingHandler::endElement();
+}
+
+nsresult
+txUnknownHandler::processingInstruction(const nsString& aTarget,
+ const nsString& aData)
+{
+ return mFlushed ?
+ mEs->mResultHandler->processingInstruction(aTarget, aData) :
+ txBufferingHandler::processingInstruction(aTarget, aData);
+}
+
+nsresult
+txUnknownHandler::startDocument()
+{
+ return mFlushed ?
+ mEs->mResultHandler->startDocument() :
+ txBufferingHandler::startDocument();
+}
+
+nsresult
+txUnknownHandler::startElement(nsIAtom* aPrefix, nsIAtom* aLocalName,
+ nsIAtom* aLowercaseLocalName, int32_t aNsID)
+{
+ if (!mFlushed) {
+ // Make sure that mEs->mResultHandler == this is true, otherwise we'll
+ // leak mEs->mResultHandler in createHandlerAndFlush.
+ NS_ASSERTION(mEs->mResultHandler == this,
+ "We're leaking mEs->mResultHandler.");
+
+ nsCOMPtr<nsIAtom> owner;
+ if (!aLowercaseLocalName) {
+ owner = TX_ToLowerCaseAtom(aLocalName);
+ NS_ENSURE_TRUE(owner, NS_ERROR_OUT_OF_MEMORY);
+
+ aLowercaseLocalName = owner;
+ }
+
+ bool htmlRoot = aNsID == kNameSpaceID_None && !aPrefix &&
+ aLowercaseLocalName == nsGkAtoms::html;
+
+ // Use aLocalName and not aLowercaseLocalName in case the output
+ // handler cares about case. For eHTMLOutput the handler will hardcode
+ // to 'html' anyway.
+ nsresult rv = createHandlerAndFlush(htmlRoot,
+ nsDependentAtomString(aLocalName),
+ aNsID);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return mEs->mResultHandler->startElement(aPrefix, aLocalName,
+ aLowercaseLocalName, aNsID);
+}
+
+nsresult
+txUnknownHandler::startElement(nsIAtom* aPrefix, const nsSubstring& aLocalName,
+ const int32_t aNsID)
+{
+ if (!mFlushed) {
+ // Make sure that mEs->mResultHandler == this is true, otherwise we'll
+ // leak mEs->mResultHandler in createHandlerAndFlush.
+ NS_ASSERTION(mEs->mResultHandler == this,
+ "We're leaking mEs->mResultHandler.");
+
+ bool htmlRoot = aNsID == kNameSpaceID_None && !aPrefix &&
+ aLocalName.Equals(NS_LITERAL_STRING("html"),
+ txCaseInsensitiveStringComparator());
+ nsresult rv = createHandlerAndFlush(htmlRoot, aLocalName, aNsID);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return mEs->mResultHandler->startElement(aPrefix, aLocalName, aNsID);
+}
+
+nsresult txUnknownHandler::createHandlerAndFlush(bool aHTMLRoot,
+ const nsSubstring& aName,
+ const int32_t aNsID)
+{
+ NS_ENSURE_TRUE(mBuffer, NS_ERROR_NOT_INITIALIZED);
+
+ txOutputFormat format;
+ format.merge(*(mEs->mStylesheet->getOutputFormat()));
+ if (format.mMethod == eMethodNotSet) {
+ format.mMethod = aHTMLRoot ? eHTMLOutput : eXMLOutput;
+ }
+
+ nsAutoPtr<txAXMLEventHandler> handler;
+ nsresult rv = mEs->mOutputHandlerFactory->createHandlerWith(&format, aName,
+ aNsID,
+ getter_Transfers(handler));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mEs->mOutputHandler = handler;
+ mEs->mResultHandler = handler.forget();
+ // Let the executionstate delete us. We need to stay alive because we might
+ // need to forward hooks to mEs->mResultHandler if someone is currently
+ // flushing a buffer to mEs->mResultHandler.
+ mEs->mObsoleteHandler = this;
+
+ mFlushed = true;
+
+ // Let go of out buffer as soon as we're done flushing it, we're not going
+ // to need it anymore from this point on (all hooks get forwarded to
+ // mEs->mResultHandler.
+ nsAutoPtr<txResultBuffer> buffer(Move(mBuffer));
+ return buffer->flushToHandler(mEs->mResultHandler);
+}
diff --git a/dom/xslt/xslt/txUnknownHandler.h b/dom/xslt/xslt/txUnknownHandler.h
new file mode 100644
index 000000000..c7324d86b
--- /dev/null
+++ b/dom/xslt/xslt/txUnknownHandler.h
@@ -0,0 +1,39 @@
+/* -*- 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 txUnknownHandler_h___
+#define txUnknownHandler_h___
+
+#include "txBufferingHandler.h"
+#include "txOutputFormat.h"
+
+class txExecutionState;
+
+class txUnknownHandler : public txBufferingHandler
+{
+public:
+ explicit txUnknownHandler(txExecutionState* aEs);
+ virtual ~txUnknownHandler();
+
+ TX_DECL_TXAXMLEVENTHANDLER
+
+private:
+ nsresult createHandlerAndFlush(bool aHTMLRoot,
+ const nsSubstring& aName,
+ const int32_t aNsID);
+
+ /*
+ * XXX we shouldn't hold to the txExecutionState, as we're supposed
+ * to live without it. But as a standalone handler, we don't.
+ * The right fix may need a txOutputFormat here.
+ */
+ txExecutionState* mEs;
+
+ // If mFlushed is true then we've replaced mEs->mResultHandler with a
+ // different handler and we should forward to that handler.
+ bool mFlushed;
+};
+
+#endif /* txUnknownHandler_h___ */
diff --git a/dom/xslt/xslt/txVariableMap.h b/dom/xslt/xslt/txVariableMap.h
new file mode 100644
index 000000000..737abc93d
--- /dev/null
+++ b/dom/xslt/xslt/txVariableMap.h
@@ -0,0 +1,78 @@
+/* -*- 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_VARIABLEMAP_H
+#define TRANSFRMX_VARIABLEMAP_H
+
+#include "nsError.h"
+#include "txXMLUtils.h"
+#include "txExprResult.h"
+#include "txExpandedNameMap.h"
+
+class txVariableMap {
+public:
+ txVariableMap();
+ ~txVariableMap();
+
+ nsresult bindVariable(const txExpandedName& aName, txAExprResult* aValue);
+
+ void getVariable(const txExpandedName& aName, txAExprResult** aResult);
+
+ void removeVariable(const txExpandedName& aName);
+
+private:
+ txExpandedNameMap<txAExprResult> mMap;
+};
+
+
+inline
+txVariableMap::txVariableMap()
+{
+ MOZ_COUNT_CTOR(txVariableMap);
+}
+
+inline
+txVariableMap::~txVariableMap()
+{
+ MOZ_COUNT_DTOR(txVariableMap);
+
+ txExpandedNameMap<txAExprResult>::iterator iter(mMap);
+ while (iter.next()) {
+ txAExprResult* res = iter.value();
+ NS_RELEASE(res);
+ }
+}
+
+inline nsresult
+txVariableMap::bindVariable(const txExpandedName& aName, txAExprResult* aValue)
+{
+ NS_ASSERTION(aValue, "can't add null-variables to a txVariableMap");
+ nsresult rv = mMap.add(aName, aValue);
+ if (NS_SUCCEEDED(rv)) {
+ NS_ADDREF(aValue);
+ }
+ else if (rv == NS_ERROR_XSLT_ALREADY_SET) {
+ rv = NS_ERROR_XSLT_VAR_ALREADY_SET;
+ }
+ return rv;
+}
+
+inline void
+txVariableMap::getVariable(const txExpandedName& aName, txAExprResult** aResult)
+{
+ *aResult = mMap.get(aName);
+ if (*aResult) {
+ NS_ADDREF(*aResult);
+ }
+}
+
+inline void
+txVariableMap::removeVariable(const txExpandedName& aName)
+{
+ txAExprResult* var = mMap.remove(aName);
+ NS_IF_RELEASE(var);
+}
+
+#endif //TRANSFRMX_VARIABLEMAP_H
diff --git a/dom/xslt/xslt/txXMLEventHandler.h b/dom/xslt/xslt/txXMLEventHandler.h
new file mode 100644
index 000000000..f3134dd0e
--- /dev/null
+++ b/dom/xslt/xslt/txXMLEventHandler.h
@@ -0,0 +1,198 @@
+/* -*- 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_XML_EVENT_HANDLER_H
+#define TRANSFRMX_XML_EVENT_HANDLER_H
+
+#include "txCore.h"
+#include "nsIAtom.h"
+
+#define kTXNameSpaceURI "http://www.mozilla.org/TransforMiix"
+#define kTXWrapper "transformiix:result"
+
+class txOutputFormat;
+class nsIDOMDocument;
+
+/**
+ * An interface for handling XML documents, loosely modeled
+ * after Dave Megginson's SAX 1.0 API.
+ */
+
+class txAXMLEventHandler
+{
+public:
+ virtual ~txAXMLEventHandler() {}
+
+ /**
+ * Signals to receive the start of an attribute.
+ *
+ * @param aPrefix the prefix of the attribute
+ * @param aLocalName the localname of the attribute
+ * @param aLowercaseName the localname of the attribute in lower case
+ * @param aNsID the namespace ID of the attribute
+ * @param aValue the value of the attribute
+ */
+ virtual nsresult attribute(nsIAtom* aPrefix, nsIAtom* aLocalName,
+ nsIAtom* aLowercaseLocalName, int32_t aNsID,
+ const nsString& aValue) = 0;
+
+ /**
+ * Signals to receive the start of an attribute.
+ *
+ * @param aPrefix the prefix of the attribute
+ * @param aLocalName the localname of the attribute
+ * @param aNsID the namespace ID of the attribute
+ * @param aValue the value of the attribute
+ */
+ virtual nsresult attribute(nsIAtom* aPrefix,
+ const nsSubstring& aLocalName,
+ const int32_t aNsID,
+ const nsString& aValue) = 0;
+
+ /**
+ * Signals to receive characters.
+ *
+ * @param aData the characters to receive
+ * @param aDOE disable output escaping for these characters
+ */
+ virtual nsresult characters(const nsSubstring& aData, bool aDOE) = 0;
+
+ /**
+ * Signals to receive data that should be treated as a comment.
+ *
+ * @param data the comment data to receive
+ */
+ virtual nsresult comment(const nsString& aData) = 0;
+
+ /**
+ * Signals the end of a document. It is an error to call
+ * this method more than once.
+ */
+ virtual nsresult endDocument(nsresult aResult) = 0;
+
+ /**
+ * Signals to receive the end of an element.
+ */
+ virtual nsresult endElement() = 0;
+
+ /**
+ * Signals to receive a processing instruction.
+ *
+ * @param aTarget the target of the processing instruction
+ * @param aData the data of the processing instruction
+ */
+ virtual nsresult processingInstruction(const nsString& aTarget,
+ const nsString& aData) = 0;
+
+ /**
+ * Signals the start of a document.
+ */
+ virtual nsresult startDocument() = 0;
+
+ /**
+ * Signals to receive the start of an element.
+ *
+ * @param aPrefix the prefix of the element
+ * @param aLocalName the localname of the element
+ * @param aLowercaseName the localname of the element in lower case
+ * @param aNsID the namespace ID of the element
+ */
+ virtual nsresult startElement(nsIAtom* aPrefix,
+ nsIAtom* aLocalName,
+ nsIAtom* aLowercaseLocalName,
+ int32_t aNsID) = 0;
+
+ /**
+ * Signals to receive the start of an element. Can throw
+ * NS_ERROR_XSLT_BAD_NODE_NAME if the name is invalid
+ *
+ * @param aPrefix the prefix of the element
+ * @param aLocalName the localname of the element
+ * @param aNsID the namespace ID of the element
+ */
+ virtual nsresult startElement(nsIAtom* aPrefix,
+ const nsSubstring& aLocalName,
+ const int32_t aNsID) = 0;
+};
+
+#define TX_DECL_TXAXMLEVENTHANDLER \
+ virtual nsresult attribute(nsIAtom* aPrefix, nsIAtom* aLocalName, \
+ nsIAtom* aLowercaseLocalName, int32_t aNsID, \
+ const nsString& aValue); \
+ virtual nsresult attribute(nsIAtom* aPrefix, \
+ const nsSubstring& aLocalName, \
+ const int32_t aNsID, \
+ const nsString& aValue); \
+ virtual nsresult characters(const nsSubstring& aData, bool aDOE); \
+ virtual nsresult comment(const nsString& aData); \
+ virtual nsresult endDocument(nsresult aResult = NS_OK); \
+ virtual nsresult endElement(); \
+ virtual nsresult processingInstruction(const nsString& aTarget, \
+ const nsString& aData); \
+ virtual nsresult startDocument(); \
+ virtual nsresult startElement(nsIAtom* aPrefix, \
+ nsIAtom* aLocalName, \
+ nsIAtom* aLowercaseLocalName, \
+ int32_t aNsID); \
+ virtual nsresult startElement(nsIAtom* aPrefix, \
+ const nsSubstring& aName, \
+ const int32_t aNsID);
+
+
+class txAOutputXMLEventHandler : public txAXMLEventHandler
+{
+public:
+ /**
+ * Gets the Mozilla output document
+ *
+ * @param aDocument the Mozilla output document
+ */
+ virtual void getOutputDocument(nsIDOMDocument** aDocument) = 0;
+};
+
+#define TX_DECL_TXAOUTPUTXMLEVENTHANDLER \
+ virtual void getOutputDocument(nsIDOMDocument** aDocument);
+
+/**
+ * Interface used to create the appropriate outputhandler
+ */
+class txAOutputHandlerFactory
+{
+public:
+ virtual ~txAOutputHandlerFactory() {}
+
+ /**
+ * Creates an outputhandler for the specified format.
+ * @param aFromat format to get handler for
+ * @param aHandler outparam. The created handler
+ */
+ virtual nsresult
+ createHandlerWith(txOutputFormat* aFormat,
+ txAXMLEventHandler** aHandler) = 0;
+
+ /**
+ * Creates an outputhandler for the specified format, with the specified
+ * name and namespace for the root element.
+ * @param aFromat format to get handler for
+ * @param aName name of the root element
+ * @param aNsID namespace-id of the root element
+ * @param aHandler outparam. The created handler
+ */
+ virtual nsresult
+ createHandlerWith(txOutputFormat* aFormat,
+ const nsSubstring& aName,
+ int32_t aNsID,
+ txAXMLEventHandler** aHandler) = 0;
+};
+
+#define TX_DECL_TXAOUTPUTHANDLERFACTORY \
+ nsresult createHandlerWith(txOutputFormat* aFormat, \
+ txAXMLEventHandler** aHandler); \
+ nsresult createHandlerWith(txOutputFormat* aFormat, \
+ const nsSubstring& aName, \
+ int32_t aNsID, \
+ txAXMLEventHandler** aHandler);
+
+#endif
diff --git a/dom/xslt/xslt/txXPathResultComparator.cpp b/dom/xslt/xslt/txXPathResultComparator.cpp
new file mode 100644
index 000000000..540308920
--- /dev/null
+++ b/dom/xslt/xslt/txXPathResultComparator.cpp
@@ -0,0 +1,222 @@
+/* -*- 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 "txXPathResultComparator.h"
+#include "txExpr.h"
+#include "txCore.h"
+#include "nsCollationCID.h"
+#include "nsILocale.h"
+#include "nsILocaleService.h"
+#include "nsIServiceManager.h"
+#include "prmem.h"
+
+#define kAscending (1<<0)
+#define kUpperFirst (1<<1)
+
+txResultStringComparator::txResultStringComparator(bool aAscending,
+ bool aUpperFirst,
+ const nsAFlatString& aLanguage)
+{
+ mSorting = 0;
+ if (aAscending)
+ mSorting |= kAscending;
+ if (aUpperFirst)
+ mSorting |= kUpperFirst;
+ nsresult rv = init(aLanguage);
+ if (NS_FAILED(rv))
+ NS_ERROR("Failed to initialize txResultStringComparator");
+}
+
+nsresult txResultStringComparator::init(const nsAFlatString& aLanguage)
+{
+ nsresult rv;
+
+ nsCOMPtr<nsILocaleService> localeService =
+ do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsILocale> locale;
+ if (!aLanguage.IsEmpty()) {
+ rv = localeService->NewLocale(aLanguage,
+ getter_AddRefs(locale));
+ }
+ else {
+ rv = localeService->GetApplicationLocale(getter_AddRefs(locale));
+ }
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsICollationFactory> colFactory =
+ do_CreateInstance(NS_COLLATIONFACTORY_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = colFactory->CreateCollation(locale, getter_AddRefs(mCollation));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+nsresult
+txResultStringComparator::createSortableValue(Expr *aExpr,
+ txIEvalContext *aContext,
+ txObject *&aResult)
+{
+ nsAutoPtr<StringValue> val(new StringValue);
+ if (!val) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (!mCollation)
+ return NS_ERROR_FAILURE;
+
+ val->mCaseKey = new nsString;
+ nsString& nsCaseKey = *(nsString *)val->mCaseKey;
+ nsresult rv = aExpr->evaluateToString(aContext, nsCaseKey);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (nsCaseKey.IsEmpty()) {
+ aResult = val.forget();
+
+ return NS_OK;
+ }
+
+ rv = mCollation->AllocateRawSortKey(nsICollation::kCollationCaseInSensitive,
+ nsCaseKey, &val->mKey, &val->mLength);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aResult = val.forget();
+
+ return NS_OK;
+}
+
+int txResultStringComparator::compareValues(txObject* aVal1, txObject* aVal2)
+{
+ StringValue* strval1 = (StringValue*)aVal1;
+ StringValue* strval2 = (StringValue*)aVal2;
+
+ if (!mCollation)
+ return -1;
+
+ if (strval1->mLength == 0) {
+ if (strval2->mLength == 0)
+ return 0;
+ return ((mSorting & kAscending) ? -1 : 1);
+ }
+
+ if (strval2->mLength == 0)
+ return ((mSorting & kAscending) ? 1 : -1);
+
+ nsresult rv;
+ int32_t result = -1;
+ rv = mCollation->CompareRawSortKey(strval1->mKey, strval1->mLength,
+ strval2->mKey, strval2->mLength,
+ &result);
+ if (NS_FAILED(rv)) {
+ // XXX ErrorReport
+ return -1;
+ }
+
+ if (result != 0)
+ return ((mSorting & kAscending) ? 1 : -1) * result;
+
+ if ((strval1->mCaseLength == 0) && (strval1->mLength != 0)) {
+ nsString* caseString = (nsString *)strval1->mCaseKey;
+ rv = mCollation->AllocateRawSortKey(nsICollation::kCollationCaseSensitive,
+ *caseString,
+ (uint8_t**)&strval1->mCaseKey,
+ &strval1->mCaseLength);
+ if (NS_FAILED(rv)) {
+ // XXX ErrorReport
+ strval1->mCaseKey = caseString;
+ strval1->mCaseLength = 0;
+ return -1;
+ }
+ delete caseString;
+ }
+ if ((strval2->mCaseLength == 0) && (strval2->mLength != 0)) {
+ nsString* caseString = (nsString *)strval2->mCaseKey;
+ rv = mCollation->AllocateRawSortKey(nsICollation::kCollationCaseSensitive,
+ *caseString,
+ (uint8_t**)&strval2->mCaseKey,
+ &strval2->mCaseLength);
+ if (NS_FAILED(rv)) {
+ // XXX ErrorReport
+ strval2->mCaseKey = caseString;
+ strval2->mCaseLength = 0;
+ return -1;
+ }
+ delete caseString;
+ }
+ rv = mCollation->CompareRawSortKey((uint8_t*)strval1->mCaseKey, strval1->mCaseLength,
+ (uint8_t*)strval2->mCaseKey, strval2->mCaseLength,
+ &result);
+ if (NS_FAILED(rv)) {
+ // XXX ErrorReport
+ return -1;
+ }
+
+ return ((mSorting & kAscending) ? 1 : -1) *
+ ((mSorting & kUpperFirst) ? -1 : 1) * result;
+}
+
+txResultStringComparator::StringValue::StringValue() : mKey(0),
+ mCaseKey(0),
+ mLength(0),
+ mCaseLength(0)
+{
+}
+
+txResultStringComparator::StringValue::~StringValue()
+{
+ PR_Free(mKey);
+ if (mCaseLength > 0)
+ PR_Free((uint8_t*)mCaseKey);
+ else
+ delete (nsString*)mCaseKey;
+}
+
+txResultNumberComparator::txResultNumberComparator(bool aAscending)
+{
+ mAscending = aAscending ? 1 : -1;
+}
+
+nsresult
+txResultNumberComparator::createSortableValue(Expr *aExpr,
+ txIEvalContext *aContext,
+ txObject *&aResult)
+{
+ nsAutoPtr<NumberValue> numval(new NumberValue);
+ if (!numval) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ RefPtr<txAExprResult> exprRes;
+ nsresult rv = aExpr->evaluate(aContext, getter_AddRefs(exprRes));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ numval->mVal = exprRes->numberValue();
+
+ aResult = numval.forget();
+
+ return NS_OK;
+}
+
+int txResultNumberComparator::compareValues(txObject* aVal1, txObject* aVal2)
+{
+ double dval1 = ((NumberValue*)aVal1)->mVal;
+ double dval2 = ((NumberValue*)aVal2)->mVal;
+
+ if (mozilla::IsNaN(dval1))
+ return mozilla::IsNaN(dval2) ? 0 : -mAscending;
+
+ if (mozilla::IsNaN(dval2))
+ return mAscending;
+
+ if (dval1 == dval2)
+ return 0;
+
+ return (dval1 < dval2) ? -mAscending : mAscending;
+}
diff --git a/dom/xslt/xslt/txXPathResultComparator.h b/dom/xslt/xslt/txXPathResultComparator.h
new file mode 100644
index 000000000..76d100f23
--- /dev/null
+++ b/dom/xslt/xslt/txXPathResultComparator.h
@@ -0,0 +1,96 @@
+/* -*- 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_XPATHRESULTCOMPARATOR_H
+#define TRANSFRMX_XPATHRESULTCOMPARATOR_H
+
+#include "mozilla/Attributes.h"
+#include "txCore.h"
+#include "nsCOMPtr.h"
+#include "nsICollation.h"
+#include "nsString.h"
+
+class Expr;
+class txIEvalContext;
+
+/*
+ * Result comparators
+ */
+class txXPathResultComparator
+{
+public:
+ virtual ~txXPathResultComparator()
+ {
+ }
+
+ /*
+ * Compares two XPath results. Returns -1 if val1 < val2,
+ * 1 if val1 > val2 and 0 if val1 == val2.
+ */
+ virtual int compareValues(txObject* val1, txObject* val2) = 0;
+
+ /*
+ * Create a sortable value.
+ */
+ virtual nsresult createSortableValue(Expr *aExpr, txIEvalContext *aContext,
+ txObject *&aResult) = 0;
+};
+
+/*
+ * Compare results as stings (data-type="text")
+ */
+class txResultStringComparator : public txXPathResultComparator
+{
+public:
+ txResultStringComparator(bool aAscending, bool aUpperFirst,
+ const nsAFlatString& aLanguage);
+
+ int compareValues(txObject* aVal1, txObject* aVal2) override;
+ nsresult createSortableValue(Expr *aExpr, txIEvalContext *aContext,
+ txObject *&aResult) override;
+private:
+ nsCOMPtr<nsICollation> mCollation;
+ nsresult init(const nsAFlatString& aLanguage);
+ nsresult createRawSortKey(const int32_t aStrength,
+ const nsString& aString,
+ uint8_t** aKey,
+ uint32_t* aLength);
+ int mSorting;
+
+ class StringValue : public txObject
+ {
+ public:
+ StringValue();
+ ~StringValue();
+
+ uint8_t* mKey;
+ void* mCaseKey;
+ uint32_t mLength, mCaseLength;
+ };
+};
+
+/*
+ * Compare results as numbers (data-type="number")
+ */
+class txResultNumberComparator : public txXPathResultComparator
+{
+public:
+ explicit txResultNumberComparator(bool aAscending);
+
+ int compareValues(txObject* aVal1, txObject* aVal2) override;
+ nsresult createSortableValue(Expr *aExpr, txIEvalContext *aContext,
+ txObject *&aResult) override;
+
+private:
+ int mAscending;
+
+ class NumberValue : public txObject
+ {
+ public:
+ double mVal;
+ };
+};
+
+#endif
diff --git a/dom/xslt/xslt/txXSLTEnvironmentFunctionCall.cpp b/dom/xslt/xslt/txXSLTEnvironmentFunctionCall.cpp
new file mode 100644
index 000000000..98094a672
--- /dev/null
+++ b/dom/xslt/xslt/txXSLTEnvironmentFunctionCall.cpp
@@ -0,0 +1,138 @@
+/* -*- 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 "txIXPathContext.h"
+#include "nsGkAtoms.h"
+#include "nsError.h"
+#include "txXMLUtils.h"
+#include "txXSLTFunctions.h"
+#include "txExpandedName.h"
+#include "txNamespaceMap.h"
+
+nsresult
+txXSLTEnvironmentFunctionCall::evaluate(txIEvalContext* aContext,
+ txAExprResult** aResult)
+{
+ *aResult = nullptr;
+
+ if (!requireParams(1, 1, aContext)) {
+ return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT;
+ }
+
+ nsAutoString property;
+ nsresult rv = mParams[0]->evaluateToString(aContext, property);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ txExpandedName qname;
+ rv = qname.init(property, mMappings, mType != FUNCTION_AVAILABLE);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ switch (mType) {
+ case SYSTEM_PROPERTY:
+ {
+ if (qname.mNamespaceID == kNameSpaceID_XSLT) {
+ if (qname.mLocalName == nsGkAtoms::version) {
+ return aContext->recycler()->getNumberResult(1.0, aResult);
+ }
+ if (qname.mLocalName == nsGkAtoms::vendor) {
+ return aContext->recycler()->getStringResult(
+ NS_LITERAL_STRING("Transformiix"), aResult);
+ }
+ if (qname.mLocalName == nsGkAtoms::vendorUrl) {
+ return aContext->recycler()->getStringResult(
+ NS_LITERAL_STRING("http://www.mozilla.org/projects/xslt/"),
+ aResult);
+ }
+ }
+ aContext->recycler()->getEmptyStringResult(aResult);
+ break;
+ }
+ case ELEMENT_AVAILABLE:
+ {
+ bool val = qname.mNamespaceID == kNameSpaceID_XSLT &&
+ (qname.mLocalName == nsGkAtoms::applyImports ||
+ qname.mLocalName == nsGkAtoms::applyTemplates ||
+ qname.mLocalName == nsGkAtoms::attribute ||
+ qname.mLocalName == nsGkAtoms::attributeSet ||
+ qname.mLocalName == nsGkAtoms::callTemplate ||
+ qname.mLocalName == nsGkAtoms::choose ||
+ qname.mLocalName == nsGkAtoms::comment ||
+ qname.mLocalName == nsGkAtoms::copy ||
+ qname.mLocalName == nsGkAtoms::copyOf ||
+ qname.mLocalName == nsGkAtoms::decimalFormat ||
+ qname.mLocalName == nsGkAtoms::element ||
+ qname.mLocalName == nsGkAtoms::fallback ||
+ qname.mLocalName == nsGkAtoms::forEach ||
+ qname.mLocalName == nsGkAtoms::_if ||
+ qname.mLocalName == nsGkAtoms::import ||
+ qname.mLocalName == nsGkAtoms::include ||
+ qname.mLocalName == nsGkAtoms::key ||
+ qname.mLocalName == nsGkAtoms::message ||
+ //qname.mLocalName == nsGkAtoms::namespaceAlias ||
+ qname.mLocalName == nsGkAtoms::number ||
+ qname.mLocalName == nsGkAtoms::otherwise ||
+ qname.mLocalName == nsGkAtoms::output ||
+ qname.mLocalName == nsGkAtoms::param ||
+ qname.mLocalName == nsGkAtoms::preserveSpace ||
+ qname.mLocalName == nsGkAtoms::processingInstruction ||
+ qname.mLocalName == nsGkAtoms::sort ||
+ qname.mLocalName == nsGkAtoms::stripSpace ||
+ qname.mLocalName == nsGkAtoms::stylesheet ||
+ qname.mLocalName == nsGkAtoms::_template ||
+ qname.mLocalName == nsGkAtoms::text ||
+ qname.mLocalName == nsGkAtoms::transform ||
+ qname.mLocalName == nsGkAtoms::valueOf ||
+ qname.mLocalName == nsGkAtoms::variable ||
+ qname.mLocalName == nsGkAtoms::when ||
+ qname.mLocalName == nsGkAtoms::withParam);
+
+ aContext->recycler()->getBoolResult(val, aResult);
+ break;
+ }
+ case FUNCTION_AVAILABLE:
+ {
+ extern bool TX_XSLTFunctionAvailable(nsIAtom* aName,
+ int32_t aNameSpaceID);
+
+ txCoreFunctionCall::eType type;
+ bool val = (qname.mNamespaceID == kNameSpaceID_None &&
+ txCoreFunctionCall::getTypeFromAtom(qname.mLocalName,
+ type)) ||
+ TX_XSLTFunctionAvailable(qname.mLocalName,
+ qname.mNamespaceID);
+
+ aContext->recycler()->getBoolResult(val, aResult);
+ break;
+ }
+ }
+
+ return NS_OK;
+}
+
+Expr::ResultType
+txXSLTEnvironmentFunctionCall::getReturnType()
+{
+ return mType == SYSTEM_PROPERTY ? (STRING_RESULT | NUMBER_RESULT) :
+ BOOLEAN_RESULT;
+}
+
+bool
+txXSLTEnvironmentFunctionCall::isSensitiveTo(ContextSensitivity aContext)
+{
+ return argsSensitiveTo(aContext);
+}
+
+#ifdef TX_TO_STRING
+nsresult
+txXSLTEnvironmentFunctionCall::getNameAtom(nsIAtom** aAtom)
+{
+ *aAtom = mType == SYSTEM_PROPERTY ? nsGkAtoms::systemProperty :
+ mType == ELEMENT_AVAILABLE ? nsGkAtoms::elementAvailable :
+ nsGkAtoms::functionAvailable;
+ NS_ADDREF(*aAtom);
+
+ return NS_OK;
+}
+#endif
diff --git a/dom/xslt/xslt/txXSLTFunctions.h b/dom/xslt/xslt/txXSLTFunctions.h
new file mode 100644
index 000000000..1bc0f9961
--- /dev/null
+++ b/dom/xslt/xslt/txXSLTFunctions.h
@@ -0,0 +1,161 @@
+/* -*- 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_XSLT_FUNCTIONS_H
+#define TRANSFRMX_XSLT_FUNCTIONS_H
+
+#include "txExpr.h"
+#include "txXMLUtils.h"
+#include "nsAutoPtr.h"
+#include "txNamespaceMap.h"
+
+class txStylesheet;
+
+/**
+ * The definition for the XSLT document() function
+**/
+class DocumentFunctionCall : public FunctionCall {
+
+public:
+
+ /**
+ * Creates a new document() function call
+ **/
+ explicit DocumentFunctionCall(const nsAString& aBaseURI);
+
+ TX_DECL_FUNCTION
+
+private:
+ nsString mBaseURI;
+};
+
+/*
+ * The definition for the XSLT key() function
+ */
+class txKeyFunctionCall : public FunctionCall {
+
+public:
+
+ /*
+ * Creates a new key() function call
+ */
+ explicit txKeyFunctionCall(txNamespaceMap* aMappings);
+
+ TX_DECL_FUNCTION
+
+private:
+ RefPtr<txNamespaceMap> mMappings;
+};
+
+/**
+ * The definition for the XSLT format-number() function
+**/
+class txFormatNumberFunctionCall : public FunctionCall {
+
+public:
+
+ /**
+ * Creates a new format-number() function call
+ **/
+ txFormatNumberFunctionCall(txStylesheet* aStylesheet, txNamespaceMap* aMappings);
+
+ TX_DECL_FUNCTION
+
+private:
+ static const char16_t FORMAT_QUOTE;
+
+ enum FormatParseState {
+ Prefix,
+ IntDigit,
+ IntZero,
+ FracZero,
+ FracDigit,
+ Suffix,
+ Finished
+ };
+
+ txStylesheet* mStylesheet;
+ RefPtr<txNamespaceMap> mMappings;
+};
+
+/**
+ * DecimalFormat
+ * A representation of the XSLT element <xsl:decimal-format>
+ */
+class txDecimalFormat {
+
+public:
+ /*
+ * Creates a new decimal format and initilizes all properties with
+ * default values
+ */
+ txDecimalFormat();
+ bool isEqual(txDecimalFormat* other);
+
+ char16_t mDecimalSeparator;
+ char16_t mGroupingSeparator;
+ nsString mInfinity;
+ char16_t mMinusSign;
+ nsString mNaN;
+ char16_t mPercent;
+ char16_t mPerMille;
+ char16_t mZeroDigit;
+ char16_t mDigit;
+ char16_t mPatternSeparator;
+};
+
+/**
+ * The definition for the XSLT current() function
+**/
+class CurrentFunctionCall : public FunctionCall {
+
+public:
+
+ /**
+ * Creates a new current() function call
+ **/
+ CurrentFunctionCall();
+
+ TX_DECL_FUNCTION
+};
+
+/**
+ * The definition for the XSLT generate-id() function
+**/
+class GenerateIdFunctionCall : public FunctionCall {
+
+public:
+
+ /**
+ * Creates a new generate-id() function call
+ **/
+ GenerateIdFunctionCall();
+
+ TX_DECL_FUNCTION
+};
+
+
+/**
+ * A system-property(), element-available() or function-available() function.
+ */
+class txXSLTEnvironmentFunctionCall : public FunctionCall
+{
+public:
+ enum eType { SYSTEM_PROPERTY, ELEMENT_AVAILABLE, FUNCTION_AVAILABLE };
+
+ txXSLTEnvironmentFunctionCall(eType aType, txNamespaceMap* aMappings)
+ : mType(aType),
+ mMappings(aMappings)
+ {
+ }
+
+ TX_DECL_FUNCTION
+
+private:
+ eType mType;
+ RefPtr<txNamespaceMap> mMappings; // Used to resolve prefixes
+};
+
+#endif
diff --git a/dom/xslt/xslt/txXSLTNumber.cpp b/dom/xslt/xslt/txXSLTNumber.cpp
new file mode 100644
index 000000000..237a46ec5
--- /dev/null
+++ b/dom/xslt/xslt/txXSLTNumber.cpp
@@ -0,0 +1,728 @@
+/* -*- 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 "txXSLTNumber.h"
+#include "nsGkAtoms.h"
+#include "txCore.h"
+#include <math.h>
+#include "txExpr.h"
+#include "txXSLTPatterns.h"
+#include "txIXPathContext.h"
+#include "txXPathTreeWalker.h"
+
+#include <algorithm>
+
+nsresult txXSLTNumber::createNumber(Expr* aValueExpr, txPattern* aCountPattern,
+ txPattern* aFromPattern, LevelType aLevel,
+ Expr* aGroupSize, Expr* aGroupSeparator,
+ Expr* aFormat, txIEvalContext* aContext,
+ nsAString& aResult)
+{
+ aResult.Truncate();
+ nsresult rv = NS_OK;
+
+ // Parse format
+ txList counters;
+ nsAutoString head, tail;
+ rv = getCounters(aGroupSize, aGroupSeparator, aFormat, aContext, counters,
+ head, tail);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Create list of values to format
+ txList values;
+ nsAutoString valueString;
+ rv = getValueList(aValueExpr, aCountPattern, aFromPattern, aLevel,
+ aContext, values, valueString);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!valueString.IsEmpty()) {
+ aResult = valueString;
+
+ return NS_OK;
+ }
+
+ // Create resulting string
+ aResult = head;
+ bool first = true;
+ txListIterator valueIter(&values);
+ txListIterator counterIter(&counters);
+ valueIter.resetToEnd();
+ int32_t value;
+ txFormattedCounter* counter = 0;
+ while ((value = NS_PTR_TO_INT32(valueIter.previous()))) {
+ if (counterIter.hasNext()) {
+ counter = (txFormattedCounter*)counterIter.next();
+ }
+
+ if (!first) {
+ aResult.Append(counter->mSeparator);
+ }
+
+ counter->appendNumber(value, aResult);
+ first = false;
+ }
+
+ aResult.Append(tail);
+
+ txListIterator iter(&counters);
+ while (iter.hasNext()) {
+ delete (txFormattedCounter*)iter.next();
+ }
+
+ return NS_OK;
+}
+
+nsresult
+txXSLTNumber::getValueList(Expr* aValueExpr, txPattern* aCountPattern,
+ txPattern* aFromPattern, LevelType aLevel,
+ txIEvalContext* aContext, txList& aValues,
+ nsAString& aValueString)
+{
+ aValueString.Truncate();
+ nsresult rv = NS_OK;
+
+ // If the value attribute exists then use that
+ if (aValueExpr) {
+ RefPtr<txAExprResult> result;
+ rv = aValueExpr->evaluate(aContext, getter_AddRefs(result));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ double value = result->numberValue();
+
+ if (mozilla::IsInfinite(value) || mozilla::IsNaN(value) ||
+ value < 0.5) {
+ txDouble::toString(value, aValueString);
+ return NS_OK;
+ }
+
+ aValues.add(NS_INT32_TO_PTR((int32_t)floor(value + 0.5)));
+ return NS_OK;
+ }
+
+
+ // Otherwise use count/from/level
+
+ txPattern* countPattern = aCountPattern;
+ bool ownsCountPattern = false;
+ const txXPathNode& currNode = aContext->getContextNode();
+
+ // Parse count- and from-attributes
+
+ if (!aCountPattern) {
+ ownsCountPattern = true;
+ txNodeTest* nodeTest;
+ uint16_t nodeType = txXPathNodeUtils::getNodeType(currNode);
+ switch (nodeType) {
+ case txXPathNodeType::ELEMENT_NODE:
+ {
+ nsCOMPtr<nsIAtom> localName =
+ txXPathNodeUtils::getLocalName(currNode);
+ int32_t namespaceID = txXPathNodeUtils::getNamespaceID(currNode);
+ nodeTest = new txNameTest(0, localName, namespaceID,
+ txXPathNodeType::ELEMENT_NODE);
+ break;
+ }
+ case txXPathNodeType::TEXT_NODE:
+ case txXPathNodeType::CDATA_SECTION_NODE:
+ {
+ nodeTest = new txNodeTypeTest(txNodeTypeTest::TEXT_TYPE);
+ break;
+ }
+ case txXPathNodeType::PROCESSING_INSTRUCTION_NODE:
+ {
+ txNodeTypeTest* typeTest;
+ typeTest = new txNodeTypeTest(txNodeTypeTest::PI_TYPE);
+ nsAutoString nodeName;
+ txXPathNodeUtils::getNodeName(currNode, nodeName);
+ typeTest->setNodeName(nodeName);
+ nodeTest = typeTest;
+ break;
+ }
+ case txXPathNodeType::COMMENT_NODE:
+ {
+ nodeTest = new txNodeTypeTest(txNodeTypeTest::COMMENT_TYPE);
+ break;
+ }
+ case txXPathNodeType::DOCUMENT_NODE:
+ case txXPathNodeType::ATTRIBUTE_NODE:
+ default:
+ {
+ // this won't match anything as we walk up the tree
+ // but it's what the spec says to do
+ nodeTest = new txNameTest(0, nsGkAtoms::_asterisk, 0,
+ nodeType);
+ break;
+ }
+ }
+ MOZ_ASSERT(nodeTest);
+ countPattern = new txStepPattern(nodeTest, false);
+ }
+
+
+ // Generate list of values depending on the value of the level-attribute
+
+ // level = "single"
+ if (aLevel == eLevelSingle) {
+ txXPathTreeWalker walker(currNode);
+ do {
+ if (aFromPattern && !walker.isOnNode(currNode) &&
+ aFromPattern->matches(walker.getCurrentPosition(), aContext)) {
+ break;
+ }
+
+ if (countPattern->matches(walker.getCurrentPosition(), aContext)) {
+ aValues.add(NS_INT32_TO_PTR(getSiblingCount(walker, countPattern,
+ aContext)));
+ break;
+ }
+
+ } while (walker.moveToParent());
+
+ // Spec says to only match ancestors that are decendants of the
+ // ancestor that matches the from-pattern, so keep going to make
+ // sure that there is an ancestor that does.
+ if (aFromPattern && aValues.getLength()) {
+ bool hasParent;
+ while ((hasParent = walker.moveToParent())) {
+ if (aFromPattern->matches(walker.getCurrentPosition(), aContext)) {
+ break;
+ }
+ }
+
+ if (!hasParent) {
+ aValues.clear();
+ }
+ }
+ }
+ // level = "multiple"
+ else if (aLevel == eLevelMultiple) {
+ // find all ancestor-or-selfs that matches count until...
+ txXPathTreeWalker walker(currNode);
+ bool matchedFrom = false;
+ do {
+ if (aFromPattern && !walker.isOnNode(currNode) &&
+ aFromPattern->matches(walker.getCurrentPosition(), aContext)) {
+ //... we find one that matches from
+ matchedFrom = true;
+ break;
+ }
+
+ if (countPattern->matches(walker.getCurrentPosition(), aContext)) {
+ aValues.add(NS_INT32_TO_PTR(getSiblingCount(walker, countPattern,
+ aContext)));
+ }
+ } while (walker.moveToParent());
+
+ // Spec says to only match ancestors that are decendants of the
+ // ancestor that matches the from-pattern, so if none did then
+ // we shouldn't search anything
+ if (aFromPattern && !matchedFrom) {
+ aValues.clear();
+ }
+ }
+ // level = "any"
+ else if (aLevel == eLevelAny) {
+ int32_t value = 0;
+ bool matchedFrom = false;
+
+ txXPathTreeWalker walker(currNode);
+ do {
+ if (aFromPattern && !walker.isOnNode(currNode) &&
+ aFromPattern->matches(walker.getCurrentPosition(), aContext)) {
+ matchedFrom = true;
+ break;
+ }
+
+ if (countPattern->matches(walker.getCurrentPosition(), aContext)) {
+ ++value;
+ }
+
+ } while (getPrevInDocumentOrder(walker));
+
+ // Spec says to only count nodes that follows the first node that
+ // matches the from pattern. So so if none did then we shouldn't
+ // count any
+ if (aFromPattern && !matchedFrom) {
+ value = 0;
+ }
+
+ if (value) {
+ aValues.add(NS_INT32_TO_PTR(value));
+ }
+ }
+
+ if (ownsCountPattern) {
+ delete countPattern;
+ }
+
+ return NS_OK;
+}
+
+
+nsresult
+txXSLTNumber::getCounters(Expr* aGroupSize, Expr* aGroupSeparator,
+ Expr* aFormat, txIEvalContext* aContext,
+ txList& aCounters, nsAString& aHead,
+ nsAString& aTail)
+{
+ aHead.Truncate();
+ aTail.Truncate();
+
+ nsresult rv = NS_OK;
+
+ nsAutoString groupSeparator;
+ int32_t groupSize = 0;
+ if (aGroupSize && aGroupSeparator) {
+ nsAutoString sizeStr;
+ rv = aGroupSize->evaluateToString(aContext, sizeStr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ double size = txDouble::toDouble(sizeStr);
+ groupSize = (int32_t)size;
+ if ((double)groupSize != size) {
+ groupSize = 0;
+ }
+
+ rv = aGroupSeparator->evaluateToString(aContext, groupSeparator);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ nsAutoString format;
+ if (aFormat) {
+ rv = aFormat->evaluateToString(aContext, format);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ uint32_t formatLen = format.Length();
+ uint32_t formatPos = 0;
+ char16_t ch = 0;
+
+ // start with header
+ while (formatPos < formatLen &&
+ !isAlphaNumeric(ch = format.CharAt(formatPos))) {
+ aHead.Append(ch);
+ ++formatPos;
+ }
+
+ // If there are no formatting tokens we need to create a default one.
+ if (formatPos == formatLen) {
+ txFormattedCounter* defaultCounter;
+ rv = txFormattedCounter::getCounterFor(NS_LITERAL_STRING("1"), groupSize,
+ groupSeparator, defaultCounter);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ defaultCounter->mSeparator.Assign('.');
+ rv = aCounters.add(defaultCounter);
+ if (NS_FAILED(rv)) {
+ // XXX ErrorReport: out of memory
+ delete defaultCounter;
+ return rv;
+ }
+
+ return NS_OK;
+ }
+
+ while (formatPos < formatLen) {
+ nsAutoString sepToken;
+ // parse separator token
+ if (!aCounters.getLength()) {
+ // Set the first counters separator to default value so that if
+ // there is only one formatting token and we're formatting a
+ // value-list longer then one we use the default separator. This
+ // won't be used when formatting the first value anyway.
+ sepToken.Assign('.');
+ }
+ else {
+ while (formatPos < formatLen &&
+ !isAlphaNumeric(ch = format.CharAt(formatPos))) {
+ sepToken.Append(ch);
+ ++formatPos;
+ }
+ }
+
+ // if we're at the end of the string then the previous token was the tail
+ if (formatPos == formatLen) {
+ aTail = sepToken;
+ return NS_OK;
+ }
+
+ // parse formatting token
+ nsAutoString numToken;
+ while (formatPos < formatLen &&
+ isAlphaNumeric(ch = format.CharAt(formatPos))) {
+ numToken.Append(ch);
+ ++formatPos;
+ }
+
+ txFormattedCounter* counter = 0;
+ rv = txFormattedCounter::getCounterFor(numToken, groupSize,
+ groupSeparator, counter);
+ if (NS_FAILED(rv)) {
+ txListIterator iter(&aCounters);
+ while (iter.hasNext()) {
+ delete (txFormattedCounter*)iter.next();
+ }
+ aCounters.clear();
+ return rv;
+ }
+
+ // Add to list of counters
+ counter->mSeparator = sepToken;
+ rv = aCounters.add(counter);
+ if (NS_FAILED(rv)) {
+ // XXX ErrorReport: out of memory
+ txListIterator iter(&aCounters);
+ while (iter.hasNext()) {
+ delete (txFormattedCounter*)iter.next();
+ }
+ aCounters.clear();
+ return rv;
+ }
+ }
+
+ return NS_OK;
+}
+
+int32_t
+txXSLTNumber::getSiblingCount(txXPathTreeWalker& aWalker,
+ txPattern* aCountPattern,
+ txIMatchContext* aContext)
+{
+ int32_t value = 1;
+ while (aWalker.moveToPreviousSibling()) {
+ if (aCountPattern->matches(aWalker.getCurrentPosition(), aContext)) {
+ ++value;
+ }
+ }
+ return value;
+}
+
+bool
+txXSLTNumber::getPrevInDocumentOrder(txXPathTreeWalker& aWalker)
+{
+ if (aWalker.moveToPreviousSibling()) {
+ while (aWalker.moveToLastChild()) {
+ // do nothing
+ }
+ return true;
+ }
+ return aWalker.moveToParent();
+}
+
+struct CharRange {
+ char16_t lower; // inclusive
+ char16_t upper; // inclusive
+
+ bool operator<(const CharRange& other) const {
+ return upper < other.lower;
+ }
+};
+
+bool txXSLTNumber::isAlphaNumeric(char16_t ch)
+{
+ static const CharRange alphanumericRanges[] = {
+ { 0x0030, 0x0039 },
+ { 0x0041, 0x005A },
+ { 0x0061, 0x007A },
+ { 0x00AA, 0x00AA },
+ { 0x00B2, 0x00B3 },
+ { 0x00B5, 0x00B5 },
+ { 0x00B9, 0x00BA },
+ { 0x00BC, 0x00BE },
+ { 0x00C0, 0x00D6 },
+ { 0x00D8, 0x00F6 },
+ { 0x00F8, 0x021F },
+ { 0x0222, 0x0233 },
+ { 0x0250, 0x02AD },
+ { 0x02B0, 0x02B8 },
+ { 0x02BB, 0x02C1 },
+ { 0x02D0, 0x02D1 },
+ { 0x02E0, 0x02E4 },
+ { 0x02EE, 0x02EE },
+ { 0x037A, 0x037A },
+ { 0x0386, 0x0386 },
+ { 0x0388, 0x038A },
+ { 0x038C, 0x038C },
+ { 0x038E, 0x03A1 },
+ { 0x03A3, 0x03CE },
+ { 0x03D0, 0x03D7 },
+ { 0x03DA, 0x03F3 },
+ { 0x0400, 0x0481 },
+ { 0x048C, 0x04C4 },
+ { 0x04C7, 0x04C8 },
+ { 0x04CB, 0x04CC },
+ { 0x04D0, 0x04F5 },
+ { 0x04F8, 0x04F9 },
+ { 0x0531, 0x0556 },
+ { 0x0559, 0x0559 },
+ { 0x0561, 0x0587 },
+ { 0x05D0, 0x05EA },
+ { 0x05F0, 0x05F2 },
+ { 0x0621, 0x063A },
+ { 0x0640, 0x064A },
+ { 0x0660, 0x0669 },
+ { 0x0671, 0x06D3 },
+ { 0x06D5, 0x06D5 },
+ { 0x06E5, 0x06E6 },
+ { 0x06F0, 0x06FC },
+ { 0x0710, 0x0710 },
+ { 0x0712, 0x072C },
+ { 0x0780, 0x07A5 },
+ { 0x0905, 0x0939 },
+ { 0x093D, 0x093D },
+ { 0x0950, 0x0950 },
+ { 0x0958, 0x0961 },
+ { 0x0966, 0x096F },
+ { 0x0985, 0x098C },
+ { 0x098F, 0x0990 },
+ { 0x0993, 0x09A8 },
+ { 0x09AA, 0x09B0 },
+ { 0x09B2, 0x09B2 },
+ { 0x09B6, 0x09B9 },
+ { 0x09DC, 0x09DD },
+ { 0x09DF, 0x09E1 },
+ { 0x09E6, 0x09F1 },
+ { 0x09F4, 0x09F9 },
+ { 0x0A05, 0x0A0A },
+ { 0x0A0F, 0x0A10 },
+ { 0x0A13, 0x0A28 },
+ { 0x0A2A, 0x0A30 },
+ { 0x0A32, 0x0A33 },
+ { 0x0A35, 0x0A36 },
+ { 0x0A38, 0x0A39 },
+ { 0x0A59, 0x0A5C },
+ { 0x0A5E, 0x0A5E },
+ { 0x0A66, 0x0A6F },
+ { 0x0A72, 0x0A74 },
+ { 0x0A85, 0x0A8B },
+ { 0x0A8D, 0x0A8D },
+ { 0x0A8F, 0x0A91 },
+ { 0x0A93, 0x0AA8 },
+ { 0x0AAA, 0x0AB0 },
+ { 0x0AB2, 0x0AB3 },
+ { 0x0AB5, 0x0AB9 },
+ { 0x0ABD, 0x0ABD },
+ { 0x0AD0, 0x0AD0 },
+ { 0x0AE0, 0x0AE0 },
+ { 0x0AE6, 0x0AEF },
+ { 0x0B05, 0x0B0C },
+ { 0x0B0F, 0x0B10 },
+ { 0x0B13, 0x0B28 },
+ { 0x0B2A, 0x0B30 },
+ { 0x0B32, 0x0B33 },
+ { 0x0B36, 0x0B39 },
+ { 0x0B3D, 0x0B3D },
+ { 0x0B5C, 0x0B5D },
+ { 0x0B5F, 0x0B61 },
+ { 0x0B66, 0x0B6F },
+ { 0x0B85, 0x0B8A },
+ { 0x0B8E, 0x0B90 },
+ { 0x0B92, 0x0B95 },
+ { 0x0B99, 0x0B9A },
+ { 0x0B9C, 0x0B9C },
+ { 0x0B9E, 0x0B9F },
+ { 0x0BA3, 0x0BA4 },
+ { 0x0BA8, 0x0BAA },
+ { 0x0BAE, 0x0BB5 },
+ { 0x0BB7, 0x0BB9 },
+ { 0x0BE7, 0x0BF2 },
+ { 0x0C05, 0x0C0C },
+ { 0x0C0E, 0x0C10 },
+ { 0x0C12, 0x0C28 },
+ { 0x0C2A, 0x0C33 },
+ { 0x0C35, 0x0C39 },
+ { 0x0C60, 0x0C61 },
+ { 0x0C66, 0x0C6F },
+ { 0x0C85, 0x0C8C },
+ { 0x0C8E, 0x0C90 },
+ { 0x0C92, 0x0CA8 },
+ { 0x0CAA, 0x0CB3 },
+ { 0x0CB5, 0x0CB9 },
+ { 0x0CDE, 0x0CDE },
+ { 0x0CE0, 0x0CE1 },
+ { 0x0CE6, 0x0CEF },
+ { 0x0D05, 0x0D0C },
+ { 0x0D0E, 0x0D10 },
+ { 0x0D12, 0x0D28 },
+ { 0x0D2A, 0x0D39 },
+ { 0x0D60, 0x0D61 },
+ { 0x0D66, 0x0D6F },
+ { 0x0D85, 0x0D96 },
+ { 0x0D9A, 0x0DB1 },
+ { 0x0DB3, 0x0DBB },
+ { 0x0DBD, 0x0DBD },
+ { 0x0DC0, 0x0DC6 },
+ { 0x0E01, 0x0E30 },
+ { 0x0E32, 0x0E33 },
+ { 0x0E40, 0x0E46 },
+ { 0x0E50, 0x0E59 },
+ { 0x0E81, 0x0E82 },
+ { 0x0E84, 0x0E84 },
+ { 0x0E87, 0x0E88 },
+ { 0x0E8A, 0x0E8A },
+ { 0x0E8D, 0x0E8D },
+ { 0x0E94, 0x0E97 },
+ { 0x0E99, 0x0E9F },
+ { 0x0EA1, 0x0EA3 },
+ { 0x0EA5, 0x0EA5 },
+ { 0x0EA7, 0x0EA7 },
+ { 0x0EAA, 0x0EAB },
+ { 0x0EAD, 0x0EB0 },
+ { 0x0EB2, 0x0EB3 },
+ { 0x0EBD, 0x0EBD },
+ { 0x0EC0, 0x0EC4 },
+ { 0x0EC6, 0x0EC6 },
+ { 0x0ED0, 0x0ED9 },
+ { 0x0EDC, 0x0EDD },
+ { 0x0F00, 0x0F00 },
+ { 0x0F20, 0x0F33 },
+ { 0x0F40, 0x0F47 },
+ { 0x0F49, 0x0F6A },
+ { 0x0F88, 0x0F8B },
+ { 0x1000, 0x1021 },
+ { 0x1023, 0x1027 },
+ { 0x1029, 0x102A },
+ { 0x1040, 0x1049 },
+ { 0x1050, 0x1055 },
+ { 0x10A0, 0x10C5 },
+ { 0x10D0, 0x10F6 },
+ { 0x1100, 0x1159 },
+ { 0x115F, 0x11A2 },
+ { 0x11A8, 0x11F9 },
+ { 0x1200, 0x1206 },
+ { 0x1208, 0x1246 },
+ { 0x1248, 0x1248 },
+ { 0x124A, 0x124D },
+ { 0x1250, 0x1256 },
+ { 0x1258, 0x1258 },
+ { 0x125A, 0x125D },
+ { 0x1260, 0x1286 },
+ { 0x1288, 0x1288 },
+ { 0x128A, 0x128D },
+ { 0x1290, 0x12AE },
+ { 0x12B0, 0x12B0 },
+ { 0x12B2, 0x12B5 },
+ { 0x12B8, 0x12BE },
+ { 0x12C0, 0x12C0 },
+ { 0x12C2, 0x12C5 },
+ { 0x12C8, 0x12CE },
+ { 0x12D0, 0x12D6 },
+ { 0x12D8, 0x12EE },
+ { 0x12F0, 0x130E },
+ { 0x1310, 0x1310 },
+ { 0x1312, 0x1315 },
+ { 0x1318, 0x131E },
+ { 0x1320, 0x1346 },
+ { 0x1348, 0x135A },
+ { 0x1369, 0x137C },
+ { 0x13A0, 0x13F4 },
+ { 0x1401, 0x166C },
+ { 0x166F, 0x1676 },
+ { 0x1681, 0x169A },
+ { 0x16A0, 0x16EA },
+ { 0x16EE, 0x16F0 },
+ { 0x1780, 0x17B3 },
+ { 0x17E0, 0x17E9 },
+ { 0x1810, 0x1819 },
+ { 0x1820, 0x1877 },
+ { 0x1880, 0x18A8 },
+ { 0x1E00, 0x1E9B },
+ { 0x1EA0, 0x1EF9 },
+ { 0x1F00, 0x1F15 },
+ { 0x1F18, 0x1F1D },
+ { 0x1F20, 0x1F45 },
+ { 0x1F48, 0x1F4D },
+ { 0x1F50, 0x1F57 },
+ { 0x1F59, 0x1F59 },
+ { 0x1F5B, 0x1F5B },
+ { 0x1F5D, 0x1F5D },
+ { 0x1F5F, 0x1F7D },
+ { 0x1F80, 0x1FB4 },
+ { 0x1FB6, 0x1FBC },
+ { 0x1FBE, 0x1FBE },
+ { 0x1FC2, 0x1FC4 },
+ { 0x1FC6, 0x1FCC },
+ { 0x1FD0, 0x1FD3 },
+ { 0x1FD6, 0x1FDB },
+ { 0x1FE0, 0x1FEC },
+ { 0x1FF2, 0x1FF4 },
+ { 0x1FF6, 0x1FFC },
+ { 0x2070, 0x2070 },
+ { 0x2074, 0x2079 },
+ { 0x207F, 0x2089 },
+ { 0x2102, 0x2102 },
+ { 0x2107, 0x2107 },
+ { 0x210A, 0x2113 },
+ { 0x2115, 0x2115 },
+ { 0x2119, 0x211D },
+ { 0x2124, 0x2124 },
+ { 0x2126, 0x2126 },
+ { 0x2128, 0x2128 },
+ { 0x212A, 0x212D },
+ { 0x212F, 0x2131 },
+ { 0x2133, 0x2139 },
+ { 0x2153, 0x2183 },
+ { 0x2460, 0x249B },
+ { 0x24EA, 0x24EA },
+ { 0x2776, 0x2793 },
+ { 0x3005, 0x3007 },
+ { 0x3021, 0x3029 },
+ { 0x3031, 0x3035 },
+ { 0x3038, 0x303A },
+ { 0x3041, 0x3094 },
+ { 0x309D, 0x309E },
+ { 0x30A1, 0x30FA },
+ { 0x30FC, 0x30FE },
+ { 0x3105, 0x312C },
+ { 0x3131, 0x318E },
+ { 0x3192, 0x3195 },
+ { 0x31A0, 0x31B7 },
+ { 0x3220, 0x3229 },
+ { 0x3280, 0x3289 },
+ { 0x3400, 0x3400 },
+ { 0x4DB5, 0x4DB5 },
+ { 0x4E00, 0x4E00 },
+ { 0x9FA5, 0x9FA5 },
+ { 0xA000, 0xA48C },
+ { 0xAC00, 0xAC00 },
+ { 0xD7A3, 0xD7A3 },
+ { 0xF900, 0xFA2D },
+ { 0xFB00, 0xFB06 },
+ { 0xFB13, 0xFB17 },
+ { 0xFB1D, 0xFB1D },
+ { 0xFB1F, 0xFB28 },
+ { 0xFB2A, 0xFB36 },
+ { 0xFB38, 0xFB3C },
+ { 0xFB3E, 0xFB3E },
+ { 0xFB40, 0xFB41 },
+ { 0xFB43, 0xFB44 },
+ { 0xFB46, 0xFBB1 },
+ { 0xFBD3, 0xFD3D },
+ { 0xFD50, 0xFD8F },
+ { 0xFD92, 0xFDC7 },
+ { 0xFDF0, 0xFDFB },
+ { 0xFE70, 0xFE72 },
+ { 0xFE74, 0xFE74 },
+ { 0xFE76, 0xFEFC },
+ { 0xFF10, 0xFF19 },
+ { 0xFF21, 0xFF3A },
+ { 0xFF41, 0xFF5A },
+ { 0xFF66, 0xFFBE },
+ { 0xFFC2, 0xFFC7 },
+ { 0xFFCA, 0xFFCF },
+ { 0xFFD2, 0xFFD7 }
+ };
+
+ CharRange search = { ch, ch };
+ const CharRange* end = mozilla::ArrayEnd(alphanumericRanges);
+ const CharRange* element = std::lower_bound(&alphanumericRanges[0], end, search);
+ if (element == end) {
+ return false;
+ }
+ return element->lower <= ch && ch <= element->upper;
+}
diff --git a/dom/xslt/xslt/txXSLTNumber.h b/dom/xslt/xslt/txXSLTNumber.h
new file mode 100644
index 000000000..e6cdd4210
--- /dev/null
+++ b/dom/xslt/xslt/txXSLTNumber.h
@@ -0,0 +1,73 @@
+/* -*- 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_TXXSLTNUMBER_H
+#define TRANSFRMX_TXXSLTNUMBER_H
+
+#include "nsError.h"
+#include "txList.h"
+#include "nsString.h"
+
+class Expr;
+class txPattern;
+class txIEvalContext;
+class txIMatchContext;
+class txXPathTreeWalker;
+
+class txXSLTNumber {
+public:
+ enum LevelType {
+ eLevelSingle,
+ eLevelMultiple,
+ eLevelAny
+ };
+
+ static nsresult createNumber(Expr* aValueExpr, txPattern* aCountPattern,
+ txPattern* aFromPattern, LevelType aLevel,
+ Expr* aGroupSize, Expr* aGroupSeparator,
+ Expr* aFormat, txIEvalContext* aContext,
+ nsAString& aResult);
+
+private:
+ static nsresult getValueList(Expr* aValueExpr, txPattern* aCountPattern,
+ txPattern* aFromPattern, LevelType aLevel,
+ txIEvalContext* aContext, txList& aValues,
+ nsAString& aValueString);
+
+ static nsresult getCounters(Expr* aGroupSize, Expr* aGroupSeparator,
+ Expr* aFormat, txIEvalContext* aContext,
+ txList& aCounters, nsAString& aHead,
+ nsAString& aTail);
+
+ /**
+ * getSiblingCount uses aWalker to walk the siblings of aWalker's current
+ * position.
+ *
+ */
+ static int32_t getSiblingCount(txXPathTreeWalker& aWalker,
+ txPattern* aCountPattern,
+ txIMatchContext* aContext);
+
+ static bool getPrevInDocumentOrder(txXPathTreeWalker& aWalker);
+
+ static bool isAlphaNumeric(char16_t ch);
+};
+
+class txFormattedCounter {
+public:
+ virtual ~txFormattedCounter()
+ {
+ }
+
+ virtual void appendNumber(int32_t aNumber, nsAString& aDest) = 0;
+
+ static nsresult getCounterFor(const nsAFlatString& aToken, int aGroupSize,
+ const nsAString& aGroupSeparator,
+ txFormattedCounter*& aCounter);
+
+ nsString mSeparator;
+};
+
+#endif //TRANSFRMX_TXXSLTNUMBER_H
diff --git a/dom/xslt/xslt/txXSLTNumberCounters.cpp b/dom/xslt/xslt/txXSLTNumberCounters.cpp
new file mode 100644
index 000000000..66abe438d
--- /dev/null
+++ b/dom/xslt/xslt/txXSLTNumberCounters.cpp
@@ -0,0 +1,214 @@
+/* -*- 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 "txXSLTNumber.h"
+#include "nsReadableUtils.h"
+#include "txCore.h"
+
+class txDecimalCounter : public txFormattedCounter {
+public:
+ txDecimalCounter() : mMinLength(1), mGroupSize(50)
+ {
+ }
+
+ txDecimalCounter(int32_t aMinLength, int32_t aGroupSize,
+ const nsAString& mGroupSeparator);
+
+ virtual void appendNumber(int32_t aNumber, nsAString& aDest);
+
+private:
+ int32_t mMinLength;
+ int32_t mGroupSize;
+ nsString mGroupSeparator;
+};
+
+class txAlphaCounter : public txFormattedCounter {
+public:
+ explicit txAlphaCounter(char16_t aOffset) : mOffset(aOffset)
+ {
+ }
+
+ virtual void appendNumber(int32_t aNumber, nsAString& aDest);
+
+private:
+ char16_t mOffset;
+};
+
+class txRomanCounter : public txFormattedCounter {
+public:
+ explicit txRomanCounter(bool aUpper) : mTableOffset(aUpper ? 30 : 0)
+ {
+ }
+
+ void appendNumber(int32_t aNumber, nsAString& aDest);
+
+private:
+ int32_t mTableOffset;
+};
+
+
+nsresult
+txFormattedCounter::getCounterFor(const nsAFlatString& aToken,
+ int32_t aGroupSize,
+ const nsAString& aGroupSeparator,
+ txFormattedCounter*& aCounter)
+{
+ int32_t length = aToken.Length();
+ NS_ASSERTION(length, "getting counter for empty token");
+ aCounter = 0;
+
+ if (length == 1) {
+ char16_t ch = aToken.CharAt(0);
+ switch (ch) {
+
+ case 'i':
+ case 'I':
+ aCounter = new txRomanCounter(ch == 'I');
+ break;
+
+ case 'a':
+ case 'A':
+ aCounter = new txAlphaCounter(ch);
+ break;
+
+ case '1':
+ default:
+ // if we don't recognize the token then use "1"
+ aCounter = new txDecimalCounter(1, aGroupSize,
+ aGroupSeparator);
+ break;
+ }
+ MOZ_ASSERT(aCounter);
+ return NS_OK;
+ }
+
+ // for now, the only multi-char token we support are decimals
+ int32_t i;
+ for (i = 0; i < length-1; ++i) {
+ if (aToken.CharAt(i) != '0')
+ break;
+ }
+ if (i == length-1 && aToken.CharAt(i) == '1') {
+ aCounter = new txDecimalCounter(length, aGroupSize, aGroupSeparator);
+ }
+ else {
+ // if we don't recognize the token then use '1'
+ aCounter = new txDecimalCounter(1, aGroupSize, aGroupSeparator);
+ }
+ MOZ_ASSERT(aCounter);
+ return NS_OK;
+}
+
+
+txDecimalCounter::txDecimalCounter(int32_t aMinLength, int32_t aGroupSize,
+ const nsAString& aGroupSeparator)
+ : mMinLength(aMinLength), mGroupSize(aGroupSize),
+ mGroupSeparator(aGroupSeparator)
+{
+ if (mGroupSize <= 0) {
+ mGroupSize = aMinLength + 10;
+ }
+}
+
+void txDecimalCounter::appendNumber(int32_t aNumber, nsAString& aDest)
+{
+ const int32_t bufsize = 10; //must be able to fit an int32_t
+ char16_t buf[bufsize];
+ int32_t pos = bufsize;
+ while (aNumber > 0) {
+ int32_t ch = aNumber % 10;
+ aNumber /= 10;
+ buf[--pos] = ch + '0';
+ }
+
+ // in case we didn't get a long enough string
+ int32_t end = (bufsize > mMinLength) ? bufsize - mMinLength : 0;
+ while (pos > end) {
+ buf[--pos] = '0';
+ }
+
+ // in case we *still* didn't get a long enough string.
+ // this should be very rare since it only happens if mMinLength is bigger
+ // then the length of any int32_t.
+ // pos will always be zero
+ int32_t extraPos = mMinLength;
+ while (extraPos > bufsize) {
+ aDest.Append(char16_t('0'));
+ --extraPos;
+ if (extraPos % mGroupSize == 0) {
+ aDest.Append(mGroupSeparator);
+ }
+ }
+
+ // copy string to buffer
+ if (mGroupSize >= bufsize - pos) {
+ // no grouping will occur
+ aDest.Append(buf + pos, (uint32_t)(bufsize - pos));
+ }
+ else {
+ // append chars up to first grouping separator
+ int32_t len = ((bufsize - pos - 1) % mGroupSize) + 1;
+ aDest.Append(buf + pos, len);
+ pos += len;
+ while (bufsize - pos > 0) {
+ aDest.Append(mGroupSeparator);
+ aDest.Append(buf + pos, mGroupSize);
+ pos += mGroupSize;
+ }
+ NS_ASSERTION(bufsize == pos, "error while grouping");
+ }
+}
+
+
+void txAlphaCounter::appendNumber(int32_t aNumber, nsAString& aDest)
+{
+ char16_t buf[12];
+ buf[11] = 0;
+ int32_t pos = 11;
+ while (aNumber > 0) {
+ --aNumber;
+ int32_t ch = aNumber % 26;
+ aNumber /= 26;
+ buf[--pos] = ch + mOffset;
+ }
+
+ aDest.Append(buf + pos, (uint32_t)(11 - pos));
+}
+
+
+const char* const kTxRomanNumbers[] =
+ {"", "c", "cc", "ccc", "cd", "d", "dc", "dcc", "dccc", "cm",
+ "", "x", "xx", "xxx", "xl", "l", "lx", "lxx", "lxxx", "xc",
+ "", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix",
+ "", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM",
+ "", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC",
+ "", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"};
+
+void txRomanCounter::appendNumber(int32_t aNumber, nsAString& aDest)
+{
+ // Numbers bigger then 3999 and negative numbers can't be done in roman
+ if (uint32_t(aNumber) >= 4000) {
+ txDecimalCounter().appendNumber(aNumber, aDest);
+ return;
+ }
+
+ while (aNumber >= 1000) {
+ aDest.Append(!mTableOffset ? char16_t('m') : char16_t('M'));
+ aNumber -= 1000;
+ }
+
+ int32_t posValue;
+
+ // Hundreds
+ posValue = aNumber / 100;
+ aNumber %= 100;
+ AppendASCIItoUTF16(kTxRomanNumbers[posValue + mTableOffset], aDest);
+ // Tens
+ posValue = aNumber / 10;
+ aNumber %= 10;
+ AppendASCIItoUTF16(kTxRomanNumbers[10 + posValue + mTableOffset], aDest);
+ // Ones
+ AppendASCIItoUTF16(kTxRomanNumbers[20 + aNumber + mTableOffset], aDest);
+}
diff --git a/dom/xslt/xslt/txXSLTPatterns.cpp b/dom/xslt/xslt/txXSLTPatterns.cpp
new file mode 100644
index 000000000..c8fe71f72
--- /dev/null
+++ b/dom/xslt/xslt/txXSLTPatterns.cpp
@@ -0,0 +1,526 @@
+/* -*- 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 "nsReadableUtils.h"
+#include "txExecutionState.h"
+#include "txXSLTPatterns.h"
+#include "txNodeSetContext.h"
+#include "txForwardContext.h"
+#include "txXMLUtils.h"
+#include "txXSLTFunctions.h"
+#include "nsWhitespaceTokenizer.h"
+#include "nsIContent.h"
+
+/*
+ * Returns the default priority of this Pattern.
+ * UnionPatterns don't like this.
+ * This should be called on the simple patterns.
+ */
+double txUnionPattern::getDefaultPriority()
+{
+ NS_ERROR("Don't call getDefaultPriority on txUnionPattern");
+ return mozilla::UnspecifiedNaN<double>();
+}
+
+/*
+ * Determines whether this Pattern matches the given node within
+ * the given context
+ * This should be called on the simple patterns for xsl:template,
+ * but is fine for xsl:key and xsl:number
+ */
+bool txUnionPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext)
+{
+ uint32_t i, len = mLocPathPatterns.Length();
+ for (i = 0; i < len; ++i) {
+ if (mLocPathPatterns[i]->matches(aNode, aContext)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+txPattern::Type
+txUnionPattern::getType()
+{
+ return UNION_PATTERN;
+}
+
+TX_IMPL_PATTERN_STUBS_NO_SUB_EXPR(txUnionPattern)
+txPattern*
+txUnionPattern::getSubPatternAt(uint32_t aPos)
+{
+ return mLocPathPatterns.SafeElementAt(aPos);
+}
+
+void
+txUnionPattern::setSubPatternAt(uint32_t aPos, txPattern* aPattern)
+{
+ NS_ASSERTION(aPos < mLocPathPatterns.Length(),
+ "setting bad subexpression index");
+ mLocPathPatterns[aPos] = aPattern;
+}
+
+
+#ifdef TX_TO_STRING
+void
+txUnionPattern::toString(nsAString& aDest)
+{
+#ifdef DEBUG
+ aDest.AppendLiteral("txUnionPattern{");
+#endif
+ for (uint32_t i = 0; i < mLocPathPatterns.Length(); ++i) {
+ if (i != 0)
+ aDest.AppendLiteral(" | ");
+ mLocPathPatterns[i]->toString(aDest);
+ }
+#ifdef DEBUG
+ aDest.Append(char16_t('}'));
+#endif
+}
+#endif
+
+
+/*
+ * LocationPathPattern
+ *
+ * a list of step patterns, can start with id or key
+ * (dealt with by the parser)
+ */
+
+nsresult txLocPathPattern::addStep(txPattern* aPattern, bool isChild)
+{
+ Step* step = mSteps.AppendElement();
+ if (!step)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ step->pattern = aPattern;
+ step->isChild = isChild;
+
+ return NS_OK;
+}
+
+bool txLocPathPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext)
+{
+ NS_ASSERTION(mSteps.Length() > 1, "Internal error");
+
+ /*
+ * The idea is to split up a path into blocks separated by descendant
+ * operators. For example "foo/bar//baz/bop//ying/yang" is split up into
+ * three blocks. The "ying/yang" block is handled by the first while-loop
+ * and the "foo/bar" and "baz/bop" blocks are handled by the second
+ * while-loop.
+ * A block is considered matched when we find a list of ancestors that
+ * match the block. If there are more than one list of ancestors that
+ * match a block we only need to find the one furthermost down in the
+ * tree.
+ */
+
+ uint32_t pos = mSteps.Length();
+ Step* step = &mSteps[--pos];
+ if (!step->pattern->matches(aNode, aContext))
+ return false;
+
+ txXPathTreeWalker walker(aNode);
+ bool hasParent = walker.moveToParent();
+
+ while (step->isChild) {
+ if (!pos)
+ return true; // all steps matched
+ step = &mSteps[--pos];
+ if (!hasParent || !step->pattern->matches(walker.getCurrentPosition(), aContext))
+ return false; // no more ancestors or no match
+
+ hasParent = walker.moveToParent();
+ }
+
+ // We have at least one // path separator
+ txXPathTreeWalker blockWalker(walker);
+ uint32_t blockPos = pos;
+
+ while (pos) {
+ if (!hasParent)
+ return false; // There are more steps in the current block
+ // than ancestors of the tested node
+
+ step = &mSteps[--pos];
+ if (!step->pattern->matches(walker.getCurrentPosition(), aContext)) {
+ // Didn't match. We restart at beginning of block using a new
+ // start node
+ pos = blockPos;
+ hasParent = blockWalker.moveToParent();
+ walker.moveTo(blockWalker);
+ }
+ else {
+ hasParent = walker.moveToParent();
+ if (!step->isChild) {
+ // We've matched an entire block. Set new start pos and start node
+ blockPos = pos;
+ blockWalker.moveTo(walker);
+ }
+ }
+ }
+
+ return true;
+} // txLocPathPattern::matches
+
+double txLocPathPattern::getDefaultPriority()
+{
+ NS_ASSERTION(mSteps.Length() > 1, "Internal error");
+
+ return 0.5;
+}
+
+TX_IMPL_PATTERN_STUBS_NO_SUB_EXPR(txLocPathPattern)
+txPattern*
+txLocPathPattern::getSubPatternAt(uint32_t aPos)
+{
+ return aPos < mSteps.Length() ? mSteps[aPos].pattern.get() : nullptr;
+}
+
+void
+txLocPathPattern::setSubPatternAt(uint32_t aPos, txPattern* aPattern)
+{
+ NS_ASSERTION(aPos < mSteps.Length(), "setting bad subexpression index");
+ Step* step = &mSteps[aPos];
+ step->pattern.forget();
+ step->pattern = aPattern;
+}
+
+#ifdef TX_TO_STRING
+void
+txLocPathPattern::toString(nsAString& aDest)
+{
+#ifdef DEBUG
+ aDest.AppendLiteral("txLocPathPattern{");
+#endif
+ for (uint32_t i = 0; i < mSteps.Length(); ++i) {
+ if (i != 0) {
+ if (mSteps[i].isChild)
+ aDest.Append(char16_t('/'));
+ else
+ aDest.AppendLiteral("//");
+ }
+ mSteps[i].pattern->toString(aDest);
+ }
+#ifdef DEBUG
+ aDest.Append(char16_t('}'));
+#endif
+}
+#endif
+
+/*
+ * txRootPattern
+ *
+ * a txPattern matching the document node, or '/'
+ */
+
+bool txRootPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext)
+{
+ return txXPathNodeUtils::isRoot(aNode);
+}
+
+double txRootPattern::getDefaultPriority()
+{
+ return 0.5;
+}
+
+TX_IMPL_PATTERN_STUBS_NO_SUB_EXPR(txRootPattern)
+TX_IMPL_PATTERN_STUBS_NO_SUB_PATTERN(txRootPattern)
+
+#ifdef TX_TO_STRING
+void
+txRootPattern::toString(nsAString& aDest)
+{
+#ifdef DEBUG
+ aDest.AppendLiteral("txRootPattern{");
+#endif
+ if (mSerialize)
+ aDest.Append(char16_t('/'));
+#ifdef DEBUG
+ aDest.Append(char16_t('}'));
+#endif
+}
+#endif
+
+/*
+ * txIdPattern
+ *
+ * txIdPattern matches if the given node has a ID attribute with one
+ * of the space delimited values.
+ * This looks like the id() function, but may only have LITERALs as
+ * argument.
+ */
+txIdPattern::txIdPattern(const nsSubstring& aString)
+{
+ nsWhitespaceTokenizer tokenizer(aString);
+ while (tokenizer.hasMoreTokens()) {
+ // this can fail, XXX move to a Init(aString) method
+ nsCOMPtr<nsIAtom> atom = NS_Atomize(tokenizer.nextToken());
+ mIds.AppendObject(atom);
+ }
+}
+
+bool txIdPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext)
+{
+ if (!txXPathNodeUtils::isElement(aNode)) {
+ return false;
+ }
+
+ // Get a ID attribute, if there is
+ nsIContent* content = txXPathNativeNode::getContent(aNode);
+ NS_ASSERTION(content, "a Element without nsIContent");
+
+ nsIAtom* id = content->GetID();
+ return id && mIds.IndexOf(id) > -1;
+}
+
+double txIdPattern::getDefaultPriority()
+{
+ return 0.5;
+}
+
+TX_IMPL_PATTERN_STUBS_NO_SUB_EXPR(txIdPattern)
+TX_IMPL_PATTERN_STUBS_NO_SUB_PATTERN(txIdPattern)
+
+#ifdef TX_TO_STRING
+void
+txIdPattern::toString(nsAString& aDest)
+{
+#ifdef DEBUG
+ aDest.AppendLiteral("txIdPattern{");
+#endif
+ aDest.AppendLiteral("id('");
+ uint32_t k, count = mIds.Count() - 1;
+ for (k = 0; k < count; ++k) {
+ nsAutoString str;
+ mIds[k]->ToString(str);
+ aDest.Append(str);
+ aDest.Append(char16_t(' '));
+ }
+ nsAutoString str;
+ mIds[count]->ToString(str);
+ aDest.Append(str);
+ aDest.AppendLiteral("')");
+#ifdef DEBUG
+ aDest.Append(char16_t('}'));
+#endif
+}
+#endif
+
+/*
+ * txKeyPattern
+ *
+ * txKeyPattern matches if the given node is in the evalation of
+ * the key() function
+ * This resembles the key() function, but may only have LITERALs as
+ * argument.
+ */
+
+bool txKeyPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext)
+{
+ txExecutionState* es = (txExecutionState*)aContext->getPrivateContext();
+ nsAutoPtr<txXPathNode> contextDoc(txXPathNodeUtils::getOwnerDocument(aNode));
+ NS_ENSURE_TRUE(contextDoc, false);
+
+ RefPtr<txNodeSet> nodes;
+ nsresult rv = es->getKeyNodes(mName, *contextDoc, mValue, true,
+ getter_AddRefs(nodes));
+ NS_ENSURE_SUCCESS(rv, false);
+
+ return nodes->contains(aNode);
+}
+
+double txKeyPattern::getDefaultPriority()
+{
+ return 0.5;
+}
+
+TX_IMPL_PATTERN_STUBS_NO_SUB_EXPR(txKeyPattern)
+TX_IMPL_PATTERN_STUBS_NO_SUB_PATTERN(txKeyPattern)
+
+#ifdef TX_TO_STRING
+void
+txKeyPattern::toString(nsAString& aDest)
+{
+#ifdef DEBUG
+ aDest.AppendLiteral("txKeyPattern{");
+#endif
+ aDest.AppendLiteral("key('");
+ nsAutoString tmp;
+ if (mPrefix) {
+ mPrefix->ToString(tmp);
+ aDest.Append(tmp);
+ aDest.Append(char16_t(':'));
+ }
+ mName.mLocalName->ToString(tmp);
+ aDest.Append(tmp);
+ aDest.AppendLiteral(", ");
+ aDest.Append(mValue);
+ aDest.AppendLiteral("')");
+#ifdef DEBUG
+ aDest.Append(char16_t('}'));
+#endif
+}
+#endif
+
+/*
+ * txStepPattern
+ *
+ * a txPattern to hold the NodeTest and the Predicates of a StepPattern
+ */
+
+bool txStepPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext)
+{
+ NS_ASSERTION(mNodeTest, "Internal error");
+
+ if (!mNodeTest->matches(aNode, aContext))
+ return false;
+
+ txXPathTreeWalker walker(aNode);
+ if ((!mIsAttr &&
+ txXPathNodeUtils::isAttribute(walker.getCurrentPosition())) ||
+ !walker.moveToParent()) {
+ return false;
+ }
+ if (isEmpty()) {
+ return true;
+ }
+
+ /*
+ * Evaluate Predicates
+ *
+ * Copy all siblings/attributes matching mNodeTest to nodes
+ * Up to the last Predicate do
+ * Foreach node in nodes
+ * evaluate Predicate with node as context node
+ * if the result is a number, check the context position,
+ * otherwise convert to bool
+ * if result is true, copy node to newNodes
+ * if aNode is not member of newNodes, return false
+ * nodes = newNodes
+ *
+ * For the last Predicate, evaluate Predicate with aNode as
+ * context node, if the result is a number, check the position,
+ * otherwise return the result converted to boolean
+ */
+
+ // Create the context node set for evaluating the predicates
+ RefPtr<txNodeSet> nodes;
+ nsresult rv = aContext->recycler()->getNodeSet(getter_AddRefs(nodes));
+ NS_ENSURE_SUCCESS(rv, false);
+
+ bool hasNext = mIsAttr ? walker.moveToFirstAttribute() :
+ walker.moveToFirstChild();
+ while (hasNext) {
+ if (mNodeTest->matches(walker.getCurrentPosition(), aContext)) {
+ nodes->append(walker.getCurrentPosition());
+ }
+ hasNext = mIsAttr ? walker.moveToNextAttribute() :
+ walker.moveToNextSibling();
+ }
+
+ Expr* predicate = mPredicates[0];
+ RefPtr<txNodeSet> newNodes;
+ rv = aContext->recycler()->getNodeSet(getter_AddRefs(newNodes));
+ NS_ENSURE_SUCCESS(rv, false);
+
+ uint32_t i, predLen = mPredicates.Length();
+ for (i = 1; i < predLen; ++i) {
+ newNodes->clear();
+ bool contextIsInPredicate = false;
+ txNodeSetContext predContext(nodes, aContext);
+ while (predContext.hasNext()) {
+ predContext.next();
+ RefPtr<txAExprResult> exprResult;
+ rv = predicate->evaluate(&predContext, getter_AddRefs(exprResult));
+ NS_ENSURE_SUCCESS(rv, false);
+
+ switch(exprResult->getResultType()) {
+ case txAExprResult::NUMBER:
+ // handle default, [position() == numberValue()]
+ if ((double)predContext.position() ==
+ exprResult->numberValue()) {
+ const txXPathNode& tmp = predContext.getContextNode();
+ if (tmp == aNode)
+ contextIsInPredicate = true;
+ newNodes->append(tmp);
+ }
+ break;
+ default:
+ if (exprResult->booleanValue()) {
+ const txXPathNode& tmp = predContext.getContextNode();
+ if (tmp == aNode)
+ contextIsInPredicate = true;
+ newNodes->append(tmp);
+ }
+ break;
+ }
+ }
+ // Move new NodeSet to the current one
+ nodes->clear();
+ nodes->append(*newNodes);
+ if (!contextIsInPredicate) {
+ return false;
+ }
+ predicate = mPredicates[i];
+ }
+ txForwardContext evalContext(aContext, aNode, nodes);
+ RefPtr<txAExprResult> exprResult;
+ rv = predicate->evaluate(&evalContext, getter_AddRefs(exprResult));
+ NS_ENSURE_SUCCESS(rv, false);
+
+ if (exprResult->getResultType() == txAExprResult::NUMBER)
+ // handle default, [position() == numberValue()]
+ return ((double)evalContext.position() == exprResult->numberValue());
+
+ return exprResult->booleanValue();
+} // matches
+
+double txStepPattern::getDefaultPriority()
+{
+ if (isEmpty())
+ return mNodeTest->getDefaultPriority();
+ return 0.5;
+}
+
+txPattern::Type
+txStepPattern::getType()
+{
+ return STEP_PATTERN;
+}
+
+TX_IMPL_PATTERN_STUBS_NO_SUB_PATTERN(txStepPattern)
+Expr*
+txStepPattern::getSubExprAt(uint32_t aPos)
+{
+ return PredicateList::getSubExprAt(aPos);
+}
+
+void
+txStepPattern::setSubExprAt(uint32_t aPos, Expr* aExpr)
+{
+ PredicateList::setSubExprAt(aPos, aExpr);
+}
+
+#ifdef TX_TO_STRING
+void
+txStepPattern::toString(nsAString& aDest)
+{
+#ifdef DEBUG
+ aDest.AppendLiteral("txStepPattern{");
+#endif
+ if (mIsAttr)
+ aDest.Append(char16_t('@'));
+ if (mNodeTest)
+ mNodeTest->toString(aDest);
+
+ PredicateList::toString(aDest);
+#ifdef DEBUG
+ aDest.Append(char16_t('}'));
+#endif
+}
+#endif
diff --git a/dom/xslt/xslt/txXSLTPatterns.h b/dom/xslt/xslt/txXSLTPatterns.h
new file mode 100644
index 000000000..d93d54fe0
--- /dev/null
+++ b/dom/xslt/xslt/txXSLTPatterns.h
@@ -0,0 +1,247 @@
+/* -*- 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_XSLT_PATTERNS_H
+#define TX_XSLT_PATTERNS_H
+
+#include "mozilla/Attributes.h"
+#include "txExpandedName.h"
+#include "txExpr.h"
+#include "txXMLUtils.h"
+
+class txPattern
+{
+public:
+ txPattern()
+ {
+ MOZ_COUNT_CTOR(txPattern);
+ }
+ virtual ~txPattern()
+ {
+ MOZ_COUNT_DTOR(txPattern);
+ }
+
+ /*
+ * Determines whether this Pattern matches the given node.
+ */
+ virtual bool matches(const txXPathNode& aNode,
+ txIMatchContext* aContext) = 0;
+
+ /*
+ * Returns the default priority of this Pattern.
+ *
+ * Simple Patterns return the values as specified in XPath 5.5.
+ * Returns -Inf for union patterns, as it shouldn't be called on them.
+ */
+ virtual double getDefaultPriority() = 0;
+
+ /**
+ * Returns the type of this pattern.
+ */
+ enum Type {
+ STEP_PATTERN,
+ UNION_PATTERN,
+ OTHER_PATTERN
+ };
+ virtual Type getType()
+ {
+ return OTHER_PATTERN;
+ }
+
+ /**
+ * 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;
+
+ /**
+ * Returns sub-pattern at given position
+ */
+ virtual txPattern* getSubPatternAt(uint32_t aPos) = 0;
+
+ /**
+ * Replace sub-pattern at given position. Does not delete the old
+ * pattern, that is the responsibility of the caller.
+ */
+ virtual void setSubPatternAt(uint32_t aPos, txPattern* aPattern) = 0;
+
+#ifdef TX_TO_STRING
+ /*
+ * Returns the String representation of this Pattern.
+ * @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 Patterns.
+ * @return the String representation of this Pattern.
+ */
+ virtual void toString(nsAString& aDest) = 0;
+#endif
+};
+
+#define TX_DECL_PATTERN_BASE \
+ bool matches(const txXPathNode& aNode, txIMatchContext* aContext) override; \
+ double getDefaultPriority() override; \
+ virtual Expr* getSubExprAt(uint32_t aPos) override; \
+ virtual void setSubExprAt(uint32_t aPos, Expr* aExpr) override; \
+ virtual txPattern* getSubPatternAt(uint32_t aPos) override; \
+ virtual void setSubPatternAt(uint32_t aPos, txPattern* aPattern) override
+
+#ifndef TX_TO_STRING
+#define TX_DECL_PATTERN TX_DECL_PATTERN_BASE
+#else
+#define TX_DECL_PATTERN \
+ TX_DECL_PATTERN_BASE; \
+ void toString(nsAString& aDest) override
+#endif
+
+#define TX_IMPL_PATTERN_STUBS_NO_SUB_EXPR(_class) \
+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_PATTERN_STUBS_NO_SUB_PATTERN(_class) \
+txPattern* \
+_class::getSubPatternAt(uint32_t aPos) \
+{ \
+ return nullptr; \
+} \
+void \
+_class::setSubPatternAt(uint32_t aPos, txPattern* aPattern) \
+{ \
+ NS_NOTREACHED("setting bad subexpression index"); \
+}
+
+class txUnionPattern : public txPattern
+{
+public:
+ nsresult addPattern(txPattern* aPattern)
+ {
+ return mLocPathPatterns.AppendElement(aPattern) ?
+ NS_OK : NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ TX_DECL_PATTERN;
+ Type getType() override;
+
+private:
+ txOwningArray<txPattern> mLocPathPatterns;
+};
+
+class txLocPathPattern : public txPattern
+{
+public:
+ nsresult addStep(txPattern* aPattern, bool isChild);
+
+ TX_DECL_PATTERN;
+
+private:
+ class Step {
+ public:
+ nsAutoPtr<txPattern> pattern;
+ bool isChild;
+ };
+
+ nsTArray<Step> mSteps;
+};
+
+class txRootPattern : public txPattern
+{
+public:
+#ifdef TX_TO_STRING
+ txRootPattern()
+ : mSerialize(true)
+ {
+ }
+#endif
+
+ TX_DECL_PATTERN;
+
+#ifdef TX_TO_STRING
+public:
+ void setSerialize(bool aSerialize)
+ {
+ mSerialize = aSerialize;
+ }
+
+private:
+ // Don't serialize txRootPattern if it's used in a txLocPathPattern
+ bool mSerialize;
+#endif
+};
+
+class txIdPattern : public txPattern
+{
+public:
+ explicit txIdPattern(const nsSubstring& aString);
+
+ TX_DECL_PATTERN;
+
+private:
+ nsCOMArray<nsIAtom> mIds;
+};
+
+class txKeyPattern : public txPattern
+{
+public:
+ txKeyPattern(nsIAtom* aPrefix, nsIAtom* aLocalName,
+ int32_t aNSID, const nsAString& aValue)
+ : mName(aNSID, aLocalName),
+#ifdef TX_TO_STRING
+ mPrefix(aPrefix),
+#endif
+ mValue(aValue)
+ {
+ }
+
+ TX_DECL_PATTERN;
+
+private:
+ txExpandedName mName;
+#ifdef TX_TO_STRING
+ nsCOMPtr<nsIAtom> mPrefix;
+#endif
+ nsString mValue;
+};
+
+class txStepPattern : public txPattern,
+ public PredicateList
+{
+public:
+ txStepPattern(txNodeTest* aNodeTest, bool isAttr)
+ : mNodeTest(aNodeTest), mIsAttr(isAttr)
+ {
+ }
+
+ TX_DECL_PATTERN;
+ Type getType() override;
+
+ txNodeTest* getNodeTest()
+ {
+ return mNodeTest;
+ }
+ void setNodeTest(txNodeTest* aNodeTest)
+ {
+ mNodeTest.forget();
+ mNodeTest = aNodeTest;
+ }
+
+private:
+ nsAutoPtr<txNodeTest> mNodeTest;
+ bool mIsAttr;
+};
+
+#endif // TX_XSLT_PATTERNS_H
diff --git a/dom/xslt/xslt/txXSLTProcessor.cpp b/dom/xslt/xslt/txXSLTProcessor.cpp
new file mode 100644
index 000000000..7335d7b84
--- /dev/null
+++ b/dom/xslt/xslt/txXSLTProcessor.cpp
@@ -0,0 +1,54 @@
+/* -*- 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 "txXSLTProcessor.h"
+#include "txInstructions.h"
+#include "nsGkAtoms.h"
+#include "txLog.h"
+#include "txStylesheetCompileHandlers.h"
+#include "txStylesheetCompiler.h"
+#include "txExecutionState.h"
+#include "txExprResult.h"
+
+TX_LG_IMPL
+
+/* static */
+bool
+txXSLTProcessor::init()
+{
+ TX_LG_CREATE;
+
+ if (!txHandlerTable::init())
+ return false;
+
+ extern bool TX_InitEXSLTFunction();
+ if (!TX_InitEXSLTFunction())
+ return false;
+
+ return true;
+}
+
+/* static */
+void
+txXSLTProcessor::shutdown()
+{
+ txStylesheetCompilerState::shutdown();
+ txHandlerTable::shutdown();
+}
+
+
+/* static */
+nsresult
+txXSLTProcessor::execute(txExecutionState& aEs)
+{
+ nsresult rv = NS_OK;
+ txInstruction* instr;
+ while ((instr = aEs.getNextInstruction())) {
+ rv = instr->execute(aEs);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return NS_OK;
+}
diff --git a/dom/xslt/xslt/txXSLTProcessor.h b/dom/xslt/xslt/txXSLTProcessor.h
new file mode 100644
index 000000000..11eecc8d9
--- /dev/null
+++ b/dom/xslt/xslt/txXSLTProcessor.h
@@ -0,0 +1,28 @@
+/* -*- 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_TXXSLTPROCESSOR_H
+#define TRANSFRMX_TXXSLTPROCESSOR_H
+
+#include "txExecutionState.h"
+
+class txXSLTProcessor
+{
+public:
+ /**
+ * Initialisation and shutdown routines. Initilizes and cleansup all
+ * dependant classes
+ */
+ static bool init();
+ static void shutdown();
+
+
+ static nsresult execute(txExecutionState& aEs);
+
+ // once we want to have interuption we should probably have functions for
+ // running X number of steps or running until a condition is true.
+};
+
+#endif