diff --git a/core/modules/utils/errors.js b/core/modules/utils/errors.js
index 1719aa60a64..73f60ae84e2 100644
--- a/core/modules/utils/errors.js
+++ b/core/modules/utils/errors.js
@@ -8,8 +8,9 @@ Custom errors for TiddlyWiki.
\*/
(function(){
-function TranscludeRecursionError(transcludeMarker) {
- this.marker = transcludeMarker;
+function TranscludeRecursionError(depth) {
+ this.depth = depth;
+ this.signatures = Object.create(null);
};
exports.TranscludeRecursionError = TranscludeRecursionError;
diff --git a/core/modules/widgets/transclude.js b/core/modules/widgets/transclude.js
index 7702641be17..ce51e21023e 100755
--- a/core/modules/widgets/transclude.js
+++ b/core/modules/widgets/transclude.js
@@ -38,7 +38,13 @@ TranscludeWidget.prototype.render = function(parent,nextSibling) {
// We need to try and abort as much of the loop as we can, so we will keep "throwing" upward until we find a transclusion that has a different signature.
// Hopefully that will land us just outside where the loop began. That's where we want to issue an error.
// Rendering widgets beneath this point may result in a freezing browser if they explode exponentially.
- if(error.marker !== this.getVariable("transclusion")) {
+ var transcludeSignature = this.getVariable("transclusion");
+ if(this.getAncestorCount() > error.depth - 50) {
+ // For the first fifty transcludes we climb up, we simply collect signatures.
+ // We're assuming that those first 50 will likely include all transcludes involved in the loop.
+ error.signatures[transcludeSignature] = true;
+ } else if(!error.signatures[transcludeSignature]) {
+ // Now that we're past the first 50, let's look for the first signature that wasn't in the loop. That'll be where we print the error and resume rendering.
this.children = [this.makeChildWidget({type: "error", attributes: {
"$message": {type: "string", value: $tw.language.getString("Error/RecursiveTransclusion")}
}})];
diff --git a/core/modules/widgets/widget.js b/core/modules/widgets/widget.js
index f45e608a8d1..1fe95178739 100755
--- a/core/modules/widgets/widget.js
+++ b/core/modules/widgets/widget.js
@@ -495,7 +495,7 @@ Widget.prototype.makeChildWidgets = function(parseTreeNodes,options) {
var self = this;
// Check for too much recursion
if(this.getAncestorCount() > MAX_WIDGET_TREE_DEPTH) {
- throw new $tw.utils.TranscludeRecursionError(this.getVariable("transclusion"));
+ throw new $tw.utils.TranscludeRecursionError(MAX_WIDGET_TREE_DEPTH);
} else {
// Create set variable widgets for each variable
$tw.utils.each(options.variables,function(value,name) {
diff --git a/editions/test/tiddlers/tests/test-widget.js b/editions/test/tiddlers/tests/test-widget.js
index 25db8aad7d0..9724c72b450 100755
--- a/editions/test/tiddlers/tests/test-widget.js
+++ b/editions/test/tiddlers/tests/test-widget.js
@@ -160,7 +160,7 @@ describe("Widget module", function() {
expect(wrapper.innerHTML).toBe("Recursive transclusion error in transclude widget");
});
- it("should handle recursion with branching nodes", function() {
+ it("should handle single-tiddler recursion with branching nodes", function() {
var wiki = new $tw.Wiki();
// Add a tiddler
wiki.addTiddlers([
@@ -180,6 +180,27 @@ describe("Widget module", function() {
expect(wrapper.innerHTML).toBe("Recursive transclusion error in transclude widget Recursive transclusion error in transclude widget");
});
+ fit("should handle many-tiddler recursion with branching nodes", function() {
+ var wiki = new $tw.Wiki();
+ // Add a tiddler
+ wiki.addTiddlers([
+ {title: "TiddlerOne", text: "<$transclude tiddler='TiddlerTwo'/> <$transclude tiddler='TiddlerTwo'/>"},
+ {title: "TiddlerTwo", text: "<$transclude tiddler='TiddlerOne'/>"}
+ ]);
+ // Test parse tree
+ var parseTreeNode = {type: "widget", children: [
+ {type: "transclude", attributes: {
+ "tiddler": {type: "string", value: "TiddlerOne"}
+ }}
+ ]};
+ // Construct the widget node
+ var widgetNode = createWidgetNode(parseTreeNode,wiki);
+ // Render the widget node to the DOM
+ var wrapper = renderWidgetNode(widgetNode);
+ // Test the rendering
+ expect(wrapper.innerHTML).toBe("Recursive transclusion error in transclude widget");
+ });
+
it("should deal with SVG elements", function() {
var wiki = new $tw.Wiki();
// Construct the widget node