Commit dda744ec authored by Adam Barth's avatar Adam Barth Committed by GitHub

Fix attaching a dirty, detached SemanticsNode (#4855)

Previously we triggered an assert.

Fixes #4850
parent c9248cd4
...@@ -362,7 +362,6 @@ class SemanticsNode extends AbstractNode { ...@@ -362,7 +362,6 @@ class SemanticsNode extends AbstractNode {
return; return;
_dirty = true; _dirty = true;
if (attached) { if (attached) {
assert(!owner._dirtyNodes.contains(this));
assert(!owner._detachedNodes.contains(this)); assert(!owner._detachedNodes.contains(this));
owner._dirtyNodes.add(this); owner._dirtyNodes.add(this);
} }
...@@ -478,7 +477,7 @@ class SemanticsOwner { ...@@ -478,7 +477,7 @@ class SemanticsOwner {
final VoidCallback _onLastListenerRemoved; final VoidCallback _onLastListenerRemoved;
final List<SemanticsNode> _dirtyNodes = <SemanticsNode>[]; final Set<SemanticsNode> _dirtyNodes = new Set<SemanticsNode>();
final Map<int, SemanticsNode> _nodes = <int, SemanticsNode>{}; final Map<int, SemanticsNode> _nodes = <int, SemanticsNode>{};
final Set<SemanticsNode> _detachedNodes = new Set<SemanticsNode>(); final Set<SemanticsNode> _detachedNodes = new Set<SemanticsNode>();
...@@ -526,10 +525,13 @@ class SemanticsOwner { ...@@ -526,10 +525,13 @@ class SemanticsOwner {
_detachedNodes.clear(); _detachedNodes.clear();
if (_dirtyNodes.isEmpty) if (_dirtyNodes.isEmpty)
return; return;
_dirtyNodes.sort((SemanticsNode a, SemanticsNode b) => a.depth - b.depth); List<SemanticsNode> visitedNodes = <SemanticsNode>[];
for (int index = 0; index < _dirtyNodes.length; index += 1) { while (_dirtyNodes.isNotEmpty) {
// we mutate the list as we walk it here, which is why we use an index instead of an iterator List<SemanticsNode> localDirtyNodes = _dirtyNodes.toList();
SemanticsNode node = _dirtyNodes[index]; _dirtyNodes.clear();
localDirtyNodes.sort((SemanticsNode a, SemanticsNode b) => a.depth - b.depth);
visitedNodes.addAll(localDirtyNodes);
for (SemanticsNode node in localDirtyNodes) {
assert(node._dirty); assert(node._dirty);
assert(node.parent == null || !node.parent._shouldMergeAllDescendantsIntoThisNode || node._inheritedMergeAllDescendantsIntoThisNode); assert(node.parent == null || !node.parent._shouldMergeAllDescendantsIntoThisNode || node._inheritedMergeAllDescendantsIntoThisNode);
if (node._shouldMergeAllDescendantsIntoThisNode) { if (node._shouldMergeAllDescendantsIntoThisNode) {
...@@ -557,11 +559,11 @@ class SemanticsOwner { ...@@ -557,11 +559,11 @@ class SemanticsOwner {
} }
} }
} }
assert(_dirtyNodes[index] == node); // make sure nothing went in front of us in the list
} }
_dirtyNodes.sort((SemanticsNode a, SemanticsNode b) => a.depth - b.depth); }
visitedNodes.sort((SemanticsNode a, SemanticsNode b) => a.depth - b.depth);
List<mojom.SemanticsNode> updatedNodes = <mojom.SemanticsNode>[]; List<mojom.SemanticsNode> updatedNodes = <mojom.SemanticsNode>[];
for (SemanticsNode node in _dirtyNodes) { for (SemanticsNode node in visitedNodes) {
assert(node.parent?._dirty != true); // could be null (no parent) or false (not dirty) assert(node.parent?._dirty != true); // could be null (no parent) or false (not dirty)
// The _serialize() method marks the node as not dirty, and // The _serialize() method marks the node as not dirty, and
// recurses through the tree to do a deep serialization of all // recurses through the tree to do a deep serialization of all
......
...@@ -50,4 +50,57 @@ void main() { ...@@ -50,4 +50,57 @@ void main() {
client.updates.clear(); client.updates.clear();
client.dispose(); client.dispose();
}); });
testWidgets('Detach and reattach assert', (WidgetTester tester) async {
TestSemanticsClient client = new TestSemanticsClient(tester.binding.pipelineOwner);
GlobalKey key = new GlobalKey();
await tester.pumpWidget(
new Container(
child: new Semantics(
label: 'test1',
child: new Semantics(
key: key,
container: true,
label: 'test2a',
child: new Container()
)
)
)
);
expect(client.updates.length, equals(1));
expect(client.updates[0].strings.label, equals('test1'));
expect(client.updates[0].children.length, equals(1));
expect(client.updates[0].children[0].strings.label, equals('test2a'));
client.updates.clear();
await tester.pumpWidget(
new Container(
child: new Semantics(
label: 'test1',
child: new Semantics(
container: true,
label: 'middle',
child: new Semantics(
key: key,
container: true,
label: 'test2b',
child: new Container()
)
)
)
)
);
expect(client.updates.length, equals(1));
expect(client.updates[0].strings.label, equals('test1'));
expect(client.updates[0].children.length, equals(1));
expect(client.updates[0].children[0].strings.label, equals('middle'));
expect(client.updates[0].children[0].children.length, equals(1));
expect(client.updates[0].children[0].children[0].strings.label, equals('test2b'));
expect(client.updates[0].children[0].children[0].children.length, equals(0));
client.dispose();
});
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment