Skip to content

Commit

Permalink
Fix for endless recursion for getLayoutExplorerNode on a Tooltip (#13…
Browse files Browse the repository at this point in the history
…1486)

![](https://media.giphy.com/media/l0ExdBwqD6YkeEhQ4/giphy-downsized.gif)

Fixes flutter/devtools#5946

While preparing DevTools for the Multi View changes, I noticed that
inspecting a Tooltip causes an stack overflow.
This PR addresses that issue by fixing the scope of the subtreeDepth variable and adding some other idiomatic fixes
  • Loading branch information
CoderDake authored Aug 2, 2023
1 parent dc4ab0f commit f80ff55
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 6 deletions.
23 changes: 17 additions & 6 deletions packages/flutter/lib/src/widgets/widget_inspector.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2109,24 +2109,35 @@ mixin WidgetInspectorService {
summaryTree: true,
subtreeDepth: subtreeDepth,
service: this,
addAdditionalPropertiesCallback: (DiagnosticsNode node, InspectorSerializationDelegate delegate) {
addAdditionalPropertiesCallback:
(DiagnosticsNode node, InspectorSerializationDelegate delegate) {
final Object? value = node.value;
final RenderObject? renderObject = value is Element ? value.renderObject : null;
final RenderObject? renderObject =
value is Element ? value.renderObject : null;
if (renderObject == null) {
return const <String, Object>{};
}

final DiagnosticsSerializationDelegate renderObjectSerializationDelegate = delegate.copyWith(
final DiagnosticsSerializationDelegate
renderObjectSerializationDelegate = delegate.copyWith(
subtreeDepth: 0,
includeProperties: true,
expandPropertyValues: false,
);
final Map<String, Object> additionalJson = <String, Object>{
'renderObject': renderObject.toDiagnosticsNode().toJsonMap(renderObjectSerializationDelegate),
// Only include renderObject properties separately if this value is not already the renderObject.
// Only include if we are expanding property values to mitigate the risk of infinite loops if
// RenderObjects have properties that are Element objects.
if (value is! RenderObject && delegate.expandPropertyValues)
'renderObject': renderObject
.toDiagnosticsNode()
.toJsonMap(renderObjectSerializationDelegate),
};

final RenderObject? renderParent = renderObject.parent;
if (renderParent is RenderObject && subtreeDepth > 0) {
if (renderParent != null &&
delegate.subtreeDepth > 0 &&
delegate.expandPropertyValues) {
final Object? parentCreator = renderParent.debugCreator;
if (parentCreator is DebugCreator) {
additionalJson['parentRenderElement'] =
Expand All @@ -2147,7 +2158,7 @@ mixin WidgetInspectorService {
if (!renderObject.debugNeedsLayout) {
// ignore: invalid_use_of_protected_member
final Constraints constraints = renderObject.constraints;
final Map<String, Object>constraintsProperty = <String, Object>{
final Map<String, Object> constraintsProperty = <String, Object>{
'type': constraints.runtimeType.toString(),
'description': constraints.toString(),
};
Expand Down
47 changes: 47 additions & 0 deletions packages/flutter/test/widgets/widget_inspector_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4771,6 +4771,53 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService {
}
expect(error, isNull);
});

testWidgets(
'ext.flutter.inspector.getLayoutExplorerNode, on a ToolTip, does not throw StackOverflowError',
(WidgetTester tester) async {
// Regression test for https://github.com/flutter/devtools/issues/5946
const Widget widget = MaterialApp(
home: Directionality(
textDirection: TextDirection.ltr,
child: Center(
child: Row(
children: <Widget>[
Flexible(
child: ColoredBox(
color: Colors.green,
child: Tooltip(
message: 'a',
child: ElevatedButton(
onPressed: null,
child: Text('a'),
),
),
),
),
],
),
),
),
);
await tester.pumpWidget(widget);

final Element elevatedButton =
tester.element(find.byType(ElevatedButton).first);
service.setSelection(elevatedButton, group);

final String id = service.toId(elevatedButton, group)!;

Object? error;
try {
await service.testExtension(
WidgetInspectorServiceExtensions.getLayoutExplorerNode.name,
<String, String>{'id': id, 'groupName': group, 'subtreeDepth': '1'},
);
} catch (e) {
error = e;
}
expect(error, isNull);
});
});

test('ext.flutter.inspector.structuredErrors', () async {
Expand Down

0 comments on commit f80ff55

Please sign in to comment.