Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
dda744ec
Commit
dda744ec
authored
Jul 08, 2016
by
Adam Barth
Committed by
GitHub
Jul 08, 2016
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fix attaching a dirty, detached SemanticsNode (#4855)
Previously we triggered an assert. Fixes #4850
parent
c9248cd4
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
88 additions
and
33 deletions
+88
-33
semantics.dart
packages/flutter/lib/src/rendering/semantics.dart
+35
-33
semantics_test.dart
packages/flutter/test/widget/semantics_test.dart
+53
-0
No files found.
packages/flutter/lib/src/rendering/semantics.dart
View file @
dda744ec
...
...
@@ -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,42 +525,45 @@ 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
];
assert
(
node
.
_dirty
);
assert
(
node
.
parent
==
null
||
!
node
.
parent
.
_shouldMergeAllDescendantsIntoThisNode
||
node
.
_inheritedMergeAllDescendantsIntoThisNode
);
if
(
node
.
_shouldMergeAllDescendantsIntoThisNode
)
{
assert
(
node
.
mergeAllDescendantsIntoThisNode
||
node
.
parent
!=
null
);
if
(
node
.
mergeAllDescendantsIntoThisNode
||
node
.
parent
!=
null
&&
node
.
parent
.
_shouldMergeAllDescendantsIntoThisNode
)
{
// if we're merged into our parent, make sure our parent is added to the list
if
(
node
.
parent
!=
null
&&
node
.
parent
.
_shouldMergeAllDescendantsIntoThisNode
)
node
.
parent
.
_markDirty
();
// this can add the node to the dirty list
// make sure all the descendants are also marked, so that if one gets marked dirty later we know to walk up then too
if
(
node
.
_children
!=
null
)
{
for
(
SemanticsNode
child
in
node
.
_children
)
child
.
_inheritedMergeAllDescendantsIntoThisNode
=
true
;
// this can add the node to the dirty list
}
}
else
{
// we previously were being merged but aren't any more
// update our bits and all our descendants'
assert
(
node
.
_inheritedMergeAllDescendantsIntoThisNode
);
assert
(!
node
.
mergeAllDescendantsIntoThisNode
);
assert
(
node
.
parent
==
null
||
!
node
.
parent
.
_shouldMergeAllDescendantsIntoThisNode
);
node
.
_inheritedMergeAllDescendantsIntoThisNode
=
false
;
if
(
node
.
_children
!=
null
)
{
for
(
SemanticsNode
child
in
node
.
_children
)
child
.
_inheritedMergeAllDescendantsIntoThisNode
=
false
;
// this can add the node to the dirty list
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
)
{
assert
(
node
.
mergeAllDescendantsIntoThisNode
||
node
.
parent
!=
null
);
if
(
node
.
mergeAllDescendantsIntoThisNode
||
node
.
parent
!=
null
&&
node
.
parent
.
_shouldMergeAllDescendantsIntoThisNode
)
{
// if we're merged into our parent, make sure our parent is added to the list
if
(
node
.
parent
!=
null
&&
node
.
parent
.
_shouldMergeAllDescendantsIntoThisNode
)
node
.
parent
.
_markDirty
();
// this can add the node to the dirty list
// make sure all the descendants are also marked, so that if one gets marked dirty later we know to walk up then too
if
(
node
.
_children
!=
null
)
{
for
(
SemanticsNode
child
in
node
.
_children
)
child
.
_inheritedMergeAllDescendantsIntoThisNode
=
true
;
// this can add the node to the dirty list
}
}
else
{
// we previously were being merged but aren't any more
// update our bits and all our descendants'
assert
(
node
.
_inheritedMergeAllDescendantsIntoThisNode
);
assert
(!
node
.
mergeAllDescendantsIntoThisNode
);
assert
(
node
.
parent
==
null
||
!
node
.
parent
.
_shouldMergeAllDescendantsIntoThisNode
);
node
.
_inheritedMergeAllDescendantsIntoThisNode
=
false
;
if
(
node
.
_children
!=
null
)
{
for
(
SemanticsNode
child
in
node
.
_children
)
child
.
_inheritedMergeAllDescendantsIntoThisNode
=
false
;
// this can add the node to the dirty list
}
}
}
}
assert
(
_dirtyNodes
[
index
]
==
node
);
// make sure nothing went in front of us in the list
}
_dirty
Nodes
.
sort
((
SemanticsNode
a
,
SemanticsNode
b
)
=>
a
.
depth
-
b
.
depth
);
visited
Nodes
.
sort
((
SemanticsNode
a
,
SemanticsNode
b
)
=>
a
.
depth
-
b
.
depth
);
List
<
mojom
.
SemanticsNode
>
updatedNodes
=
<
mojom
.
SemanticsNode
>[];
for
(
SemanticsNode
node
in
_dirty
Nodes
)
{
for
(
SemanticsNode
node
in
visited
Nodes
)
{
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
...
...
packages/flutter/test/widget/semantics_test.dart
View file @
dda744ec
...
...
@@ -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
();
});
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment