Skip to content

Commit 7835cc5

Browse files
Fixing issue with infinite loops if apply-templates uses an absolute match.
1 parent d24cbae commit 7835cc5

File tree

4 files changed

+80
-36
lines changed

4 files changed

+80
-36
lines changed

README.md

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,50 @@
11
# XSLT-processor
22

3-
[![Build Status](https://travis-ci.com/fiduswriter/xslt-processor.svg?branch=master)](https://travis-ci.com/fiduswriter/xslt-processor)
4-
53
_A JavaScript XSLT processor without native library dependencies._
64

7-
## Howto
5+
## How to
86

97
Install xslt-processor using npm:
108

119
```
1210
npm install xslt-processor
1311
```
1412

15-
Within your ES2015+ code, import the two main functions and apply them:
13+
Within your ES2015+ code, import the `Xslt` class, the `xmlParse` function and use this way:
14+
15+
```js
16+
import { Xslt, xmlParse } from 'xslt-processor'
17+
18+
// xmlString: string of xml file contents
19+
// xsltString: string of xslt file contents
20+
// outXmlString: output xml string.
21+
const xslt = new Xslt();
22+
const outXmlString = xslt.xsltProcess(
23+
xmlParse(xmlString),
24+
xmlParse(xsltString)
25+
);
26+
```
27+
28+
To access the XPath parser, you can use the instance present at `Xslt` class:
29+
30+
```js
31+
const xslt = new Xslt();
32+
const xPath = xslt.xPath;
33+
```
34+
35+
Or ou can import it like this:
36+
37+
```js
38+
import { XPath } from 'xslt-processor'
39+
40+
const xPath = new XPath();
41+
```
42+
43+
If you write pre-2015 JS code, make adjustments as needed.
44+
45+
### Breaking Changes
46+
47+
Until version 0.17, use like the example below:
1648

1749
```js
1850
import { xsltProcess, xmlParse } from 'xslt-processor'
@@ -26,14 +58,13 @@ const outXmlString = xsltProcess(
2658
);
2759
```
2860

29-
To access the Xpath-parser, import it like this:
61+
and to access the XPath parser:
3062

3163
```js
3264
import { xpathParse } from 'xslt-processor'
3365
```
3466

35-
If you write pre-2015 JS code, make adjustments as needed.
36-
67+
These functions are part of `Xslt` and `XPath` classes, respectively, at version 1.x onward.
3768

3869
## Introduction
3970

@@ -75,8 +106,7 @@ Issues are also marked in the source code using throw-statements.
75106
The DOM implementation is minimal so as to support the XSLT processing, and not intended to be complete.
76107

77108
The implementation is all agnostic about namespaces. It just expects
78-
XSLT elements to have tags that carry the xsl: prefix, but we
79-
disregard all namespace declaration for them.
109+
XSLT elements to have tags that carry the `xsl:` prefix, but we disregard all namespace declaration for them.
80110

81111
There are a few nonstandard XPath functions. Grep `xpath.js` for `ext-` to see their definitions.
82112

TODO.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ XSLT-processor TODO
22
=====
33

44
* Deal with namespaces (if namespaces are not the same in XSLT and XML document, matches do no occur.)
5-
5+
* XSLT validation
66
* XSL:number
7+
* `attribute-set`, `decimal-format`, etc. (check `src/xslt.ts`)
78

89
Help is much appreciated. It seems to currently work for most of our purposes, but fixes and additions are always welcome!

src/dom/util.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import { XDocument } from './xdocument';
2323
// Throws an exception if false.
2424
export function assert(b) {
2525
if (!b) {
26-
throw 'Assertion failed';
26+
throw new Error('Assertion failed');
2727
}
2828
}
2929

@@ -209,31 +209,31 @@ export function xmlOwnerDocument(node) {
209209
}
210210

211211
// Wrapper around DOM methods so we can condense their invocations.
212-
export function domGetAttribute(node, name) {
212+
export function domGetAttribute(node: any, name: any) {
213213
return node.getAttribute(name);
214214
}
215215

216-
export function domSetAttribute(node, name, value) {
216+
export function domSetAttribute(node: any, name: any, value: any) {
217217
return node.setAttribute(name, value);
218218
}
219219

220-
export function domAppendChild(node, child) {
220+
export function domAppendChild(node: any, child: any) {
221221
return node.appendChild(child);
222222
}
223223

224-
export function domCreateTextNode(doc, text) {
224+
export function domCreateTextNode(doc: any, text: any) {
225225
return doc.createTextNode(text);
226226
}
227227

228-
export function domCreateElement(doc, name) {
228+
export function domCreateElement(doc: any, name: any) {
229229
return doc.createElement(name);
230230
}
231231

232-
export function domCreateCDATASection(doc, data) {
232+
export function domCreateCDATASection(doc: any, data: any) {
233233
return doc.createCDATASection(data);
234234
}
235235

236-
export function domCreateComment(doc, text) {
236+
export function domCreateComment(doc: any, text: any) {
237237
return doc.createComment(text);
238238
}
239239

@@ -251,7 +251,7 @@ const regExpSpecials = ['/', '.', '*', '+', '?', '|', '^', '$', '(', ')', '[', '
251251

252252
const sRE = new RegExp(`(\\${regExpSpecials.join('|\\')})`, 'g');
253253

254-
export function regExpEscape(text) {
254+
export function regExpEscape(text: string) {
255255
return text.replace(sRE, '\\$1');
256256
}
257257

src/xslt.ts

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ export class Xslt {
9898
// @param template The stylesheet document root, as DOM node.
9999
// @param the root of the generated output, as DOM node.
100100

101-
xsltProcessContext(input: any, template: any, output: any, _parameters?: any) {
101+
xsltProcessContext(input: any, template: XNode, output: any, _parameters?: any) {
102102
const outputDocument = xmlOwnerDocument(output);
103103

104104
if (!this.isXsltElement(template)) {
@@ -137,9 +137,17 @@ export class Xslt {
137137

138138
mode = this.xmlGetAttribute(template, 'mode');
139139
top = template.ownerDocument.documentElement;
140+
140141
templates = [];
141142
for (let i = 0; i < top.childNodes.length; ++i) {
142143
let c = top.childNodes[i];
144+
let matchAttribute = c.getAttribute('match');
145+
146+
// Avoiding infinite loops.
147+
if (matchAttribute && matchAttribute.startsWith('/')) {
148+
continue;
149+
}
150+
143151
if (
144152
c.nodeType == DOM_ELEMENT_NODE &&
145153
this.isXsltElement(c, 'template') &&
@@ -299,27 +307,32 @@ export class Xslt {
299307
}
300308
}
301309

302-
// Sets parameters defined by xsl:with-param child nodes of the
303-
// current template node, in the current input context. This happens
304-
// before the operation specified by the current template node is
305-
// executed.
306-
307-
xsltWithParam(input, template) {
310+
/**
311+
* Sets parameters defined by xsl:with-param child nodes of the
312+
* current template node, in the current input context. This happens
313+
* before the operation specified by the current template node is
314+
* executed.
315+
* @param input TODO
316+
* @param template TODO
317+
*/
318+
xsltWithParam(input: any, template: any) {
308319
for (const c of template.childNodes) {
309320
if (c.nodeType == DOM_ELEMENT_NODE && this.isXsltElement(c, 'with-param')) {
310321
this.xsltVariable(input, c, true);
311322
}
312323
}
313324
}
314325

315-
// Orders the current node list in the input context according to the
316-
// sort order specified by xsl:sort child nodes of the current
317-
// template node. This happens before the operation specified by the
318-
// current template node is executed.
319-
//
320-
// TODO(mesch): case-order is not implemented.
321-
322-
xsltSort(input, template) {
326+
/**
327+
* Orders the current node list in the input context according to the
328+
* sort order specified by xsl:sort child nodes of the current
329+
* template node. This happens before the operation specified by the
330+
* current template node is executed.
331+
* @param input TODO
332+
* @param template TODO
333+
* @todo case-order is not implemented.
334+
*/
335+
xsltSort(input: any, template: any) {
323336
const sort = [];
324337

325338
for (const c of template.childNodes) {
@@ -690,7 +703,7 @@ export class Xslt {
690703
// Test if the given element is an XSLT element, optionally the one with the given name
691704
isXsltElement(element: any, opt_wantedName?: string) {
692705
if (opt_wantedName && element.localName != opt_wantedName) return false;
693-
if (element.namespaceURI) return element.namespaceURI == 'http://www.w3.org/1999/XSL/Transform';
694-
return element.prefix == 'xsl'; // backwards compatibility with earlier versions of xslt-processor
706+
if (element.namespaceURI) return element.namespaceURI === 'http://www.w3.org/1999/XSL/Transform';
707+
return element.prefix === 'xsl'; // backwards compatibility with earlier versions of xslt-processor
695708
}
696709
}

0 commit comments

Comments
 (0)