Index: runtime/observatory/lib/src/elements/containers/virtual_tree.dart |
diff --git a/runtime/observatory/lib/src/elements/containers/virtual_tree.dart b/runtime/observatory/lib/src/elements/containers/virtual_tree.dart |
index 45972483287f4deb2242b374cce2841226284f82..20fe22b90d0d6225de77354899a6570e517b8383 100644 |
--- a/runtime/observatory/lib/src/elements/containers/virtual_tree.dart |
+++ b/runtime/observatory/lib/src/elements/containers/virtual_tree.dart |
@@ -4,6 +4,7 @@ |
import 'dart:async'; |
import 'dart:html'; |
+import 'dart:collection'; |
import 'dart:math' as Math; |
import 'package:observatory/src/elements/containers/virtual_collection.dart'; |
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart'; |
@@ -89,8 +90,14 @@ class VirtualTreeElement extends HtmlElement implements Renderable { |
bool autoExpandWholeTree: false}) { |
if (_expanded.add(item)) _r.dirty(); |
if (autoExpandWholeTree) { |
- for (final child in _children(item)) { |
- expand(child, autoExpandWholeTree: true); |
+ // The tree is potentially very deep, simple recursion can produce a |
+ // Stack Overflow |
+ Queue pendingNodes = new Queue(); |
+ pendingNodes.addAll(_children(item)); |
+ while (pendingNodes.isNotEmpty) { |
+ final item = pendingNodes.removeFirst(); |
+ if (_expanded.add(item)) _r.dirty(); |
+ pendingNodes.addAll(_children(item)); |
} |
} else if (autoExpandSingleChildNodes) { |
var children = _children(item); |
@@ -106,8 +113,14 @@ class VirtualTreeElement extends HtmlElement implements Renderable { |
bool autoCollapseWholeTree: false}) { |
if (_expanded.remove(item)) _r.dirty(); |
if (autoCollapseWholeTree) { |
- for (final child in _children(item)) { |
- collapse(child, autoCollapseWholeTree: true); |
+ // The tree is potentially very deep, simple recursion can produce a |
+ // Stack Overflow |
+ Queue pendingNodes = new Queue(); |
+ pendingNodes.addAll(_children(item)); |
+ while (pendingNodes.isNotEmpty) { |
+ final item = pendingNodes.removeFirst(); |
+ if (_expanded.remove(item)) _r.dirty(); |
+ pendingNodes.addAll(_children(item)); |
} |
} else if (autoCollapseSingleChildNodes) { |
var children = _children(item); |
@@ -137,29 +150,32 @@ class VirtualTreeElement extends HtmlElement implements Renderable { |
if (children.length == 0) { |
children = [_collection]; |
} |
- Iterable _toList(item) { |
- if (isExpanded(item)) { |
- Iterable children = _children(item); |
- if (children.isNotEmpty) { |
- return [item]..addAll(children.expand(_toList)); |
- } |
- } |
- return [item]; |
- } |
- _collection.items = _items.expand(_toList); |
- var depth = 0; |
- Iterable _toDepth(item) { |
- if (isExpanded(item)) { |
- Iterable children = _children(item); |
- if (children.isNotEmpty) { |
- depth++; |
- return children.expand(_toDepth).toList()..insert(0, --depth); |
+ final items = []; |
+ final depths = new List.filled(_items.length, 0, growable: true); |
+ |
+ { |
+ final toDo = new Queue(); |
+ |
+ toDo.addAll(_items); |
+ while (toDo.isNotEmpty) { |
+ final item = toDo.removeFirst(); |
+ |
+ items.add(item); |
+ if (isExpanded(item)) { |
+ final children = _children(item); |
+ children |
+ .toList(growable: false) |
+ .reversed |
+ .forEach((c) => toDo.addFirst(c)); |
+ final depth = depths[items.length - 1]; |
+ depths.insertAll( |
+ items.length, new List.filled(children.length, depth + 1)); |
} |
} |
- return [depth]; |
} |
- _depths = _items.expand(_toDepth).toList(); |
+ _depths = depths; |
+ _collection.items = items; |
} |
} |