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 {
return;
_dirty = true;
if (attached) {
assert(!owner._dirtyNodes.contains(this));
assert(!owner._detachedNodes.contains(this));
owner._dirtyNodes.add(this);
}
......@@ -478,7 +477,7 @@ class SemanticsOwner {
final VoidCallback _onLastListenerRemoved;
final List<SemanticsNode> _dirtyNodes = <SemanticsNode>[];
final Set<SemanticsNode> _dirtyNodes = new Set<SemanticsNode>();
final Map<int, SemanticsNode> _nodes = <int, SemanticsNode>{};
final Set<SemanticsNode> _detachedNodes = new Set<SemanticsNode>();
......@@ -526,10 +525,13 @@ class SemanticsOwner {
_detachedNodes.clear();
if (_dirtyNodes.isEmpty)
return;
_dirtyNodes.sort((SemanticsNode a, SemanticsNode b) => a.depth - b.depth);
for (int index = 0; index < _dirtyNodes.length; index += 1) {
// we mutate the list as we walk it here, which is why we use an index instead of an iterator
SemanticsNode node = _dirtyNodes[index];
List<SemanticsNode> visitedNodes = <SemanticsNode>[];
while (_dirtyNodes.isNotEmpty) {
List<SemanticsNode> localDirtyNodes = _dirtyNodes.toList();
_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.parent == null || !node.parent._shouldMergeAllDescendantsIntoThisNode || node._inheritedMergeAllDescendantsIntoThisNode);
if (node._shouldMergeAllDescendantsIntoThisNode) {
......@@ -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>[];
for (SemanticsNode node in _dirtyNodes) {
for (SemanticsNode node in visitedNodes) {
assert(node.parent?._dirty != true); // could be null (no parent) or false (not dirty)
// The _serialize() method marks the node as not dirty, and
// recurses through the tree to do a deep serialization of all
......
......@@ -50,4 +50,57 @@ void main() {
client.updates.clear();
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