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
670f9d20
Unverified
Commit
670f9d20
authored
Mar 10, 2023
by
Jim Graham
Committed by
GitHub
Mar 10, 2023
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Revert "Introduce the PipelineOwner tree (#122231)" (#122425)
This reverts commit
f73c358e
.
parent
f73c358e
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
34 additions
and
1341 deletions
+34
-1341
binding.dart
packages/flutter/lib/src/rendering/binding.dart
+14
-26
object.dart
packages/flutter/lib/src/rendering/object.dart
+19
-239
binding_pipeline_manifold_init_test.dart
...r/test/rendering/binding_pipeline_manifold_init_test.dart
+0
-25
binding_pipeline_manifold_test.dart
...lutter/test/rendering/binding_pipeline_manifold_test.dart
+0
-98
pipeline_owner_tree_test.dart
...ages/flutter/test/rendering/pipeline_owner_tree_test.dart
+0
-952
widget_tester.dart
packages/flutter_test/lib/src/widget_tester.dart
+1
-1
No files found.
packages/flutter/lib/src/rendering/binding.dart
View file @
670f9d20
...
...
@@ -30,6 +30,7 @@ mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, Gesture
super
.
initInstances
();
_instance
=
this
;
_pipelineOwner
=
PipelineOwner
(
onNeedVisualUpdate:
ensureVisualUpdate
,
onSemanticsOwnerCreated:
_handleSemanticsOwnerCreated
,
onSemanticsUpdate:
_handleSemanticsUpdate
,
onSemanticsOwnerDisposed:
_handleSemanticsOwnerDisposed
,
...
...
@@ -44,7 +45,8 @@ mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, Gesture
if
(
kIsWeb
)
{
addPostFrameCallback
(
_handleWebFirstFrame
);
}
_pipelineOwner
.
attach
(
_manifold
);
addSemanticsEnabledListener
(
_handleSemanticsEnabledChanged
);
_handleSemanticsEnabledChanged
();
}
/// The current [RendererBinding], if one has been created.
...
...
@@ -199,8 +201,6 @@ mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, Gesture
}
}
late
final
PipelineManifold
_manifold
=
_BindingPipelineManifold
(
this
);
/// Creates a [RenderView] object to be the root of the
/// [RenderObject] rendering tree, and initializes it so that it
/// will be rendered when the next frame is requested.
...
...
@@ -330,6 +330,17 @@ mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, Gesture
super
.
dispatchEvent
(
event
,
hitTestResult
);
}
SemanticsHandle
?
_semanticsHandle
;
void
_handleSemanticsEnabledChanged
()
{
if
(
semanticsEnabled
)
{
_semanticsHandle
??=
_pipelineOwner
.
ensureSemantics
();
}
else
{
_semanticsHandle
?.
dispose
();
_semanticsHandle
=
null
;
}
}
@override
void
performSemanticsAction
(
SemanticsActionEvent
action
)
{
_pipelineOwner
.
semanticsOwner
?.
performAction
(
action
.
nodeId
,
action
.
type
,
action
.
arguments
);
...
...
@@ -610,26 +621,3 @@ class RenderingFlutterBinding extends BindingBase with GestureBinding, Scheduler
return
RendererBinding
.
instance
;
}
}
/// A [PipelineManifold] implementation that is backed by the [RendererBinding].
class
_BindingPipelineManifold
extends
ChangeNotifier
implements
PipelineManifold
{
_BindingPipelineManifold
(
this
.
_binding
)
{
_binding
.
addSemanticsEnabledListener
(
notifyListeners
);
}
final
RendererBinding
_binding
;
@override
void
requestVisualUpdate
()
{
_binding
.
ensureVisualUpdate
();
}
@override
bool
get
semanticsEnabled
=>
_binding
.
semanticsEnabled
;
@override
void
dispose
()
{
_binding
.
removeSemanticsEnabledListener
(
notifyListeners
);
super
.
dispose
();
}
}
packages/flutter/lib/src/rendering/object.dart
View file @
670f9d20
...
...
@@ -859,20 +859,6 @@ class _LocalSemanticsHandle implements SemanticsHandle {
/// are visible on screen. You can create other pipeline owners to manage
/// off-screen objects, which can flush their pipelines independently of the
/// on-screen render objects.
///
/// [PipelineOwner]s can be organized in a tree to manage multiple render trees,
/// where each [PipelineOwner] is responsible for one of the render trees. To
/// build or modify the tree, call [adoptChild] or [dropChild]. During each of
/// the different flush phases described above, a [PipelineOwner] will first
/// perform the phase on the nodes it manages in its own render tree before
/// calling the same flush method on its children. No assumption must be made
/// about the order in which child [PipelineOwner]s are flushed.
///
/// A [PipelineOwner] may also be [attach]ed to a [PipelineManifold], which
/// gives it access to platform functionality usually exposed by the bindings
/// without tying it to a specific binding implementation. All [PipelineOwner]s
/// in a given tree must be attached to the same [PipelineManifold]. This
/// happens automatically during [adoptChild].
class
PipelineOwner
{
/// Creates a pipeline owner.
///
...
...
@@ -893,10 +879,6 @@ class PipelineOwner {
/// various stages of the pipeline. This function might be called multiple
/// times in quick succession. Implementations should take care to discard
/// duplicate calls quickly.
///
/// When the [PipelineOwner] is attached to a [PipelineManifold] and
/// [onNeedVisualUpdate] is provided, the [onNeedVisualUpdate] callback is
/// invoked instead of calling [PipelineManifold.requestVisualUpdate].
final
VoidCallback
?
onNeedVisualUpdate
;
/// Called whenever this pipeline owner creates a semantics object.
...
...
@@ -921,11 +903,7 @@ class PipelineOwner {
/// Used to notify the pipeline owner that an associated render object wishes
/// to update its visual appearance.
void
requestVisualUpdate
()
{
if
(
onNeedVisualUpdate
!=
null
)
{
onNeedVisualUpdate
!();
}
else
{
_manifold
?.
requestVisualUpdate
();
}
onNeedVisualUpdate
?.
call
();
}
/// The unique object managed by this pipeline that has no parent.
...
...
@@ -967,7 +945,6 @@ class PipelineOwner {
/// always returns false.
bool
get
debugDoingLayout
=>
_debugDoingLayout
;
bool
_debugDoingLayout
=
false
;
bool
_debugDoingChildLayout
=
false
;
/// Update the layout information for all dirty render objects.
///
...
...
@@ -1020,20 +997,10 @@ class PipelineOwner {
// relayout boundary back.
_shouldMergeDirtyNodes
=
false
;
}
assert
(()
{
_debugDoingChildLayout
=
true
;
return
true
;
}());
for
(
final
PipelineOwner
child
in
_children
)
{
child
.
flushLayout
();
}
assert
(
_nodesNeedingLayout
.
isEmpty
,
'Child PipelineOwners must not dirty nodes in their parent.'
);
}
finally
{
_shouldMergeDirtyNodes
=
false
;
assert
(()
{
_debugDoingLayout
=
false
;
_debugDoingChildLayout
=
false
;
return
true
;
}());
if
(!
kReleaseMode
)
{
...
...
@@ -1085,10 +1052,6 @@ class PipelineOwner {
}
}
_nodesNeedingCompositingBitsUpdate
.
clear
();
for
(
final
PipelineOwner
child
in
_children
)
{
child
.
flushCompositingBits
();
}
assert
(
_nodesNeedingCompositingBitsUpdate
.
isEmpty
,
'Child PipelineOwners must not dirty nodes in their parent.'
);
if
(!
kReleaseMode
)
{
Timeline
.
finishSync
();
}
...
...
@@ -1153,10 +1116,7 @@ class PipelineOwner {
}
}
}
for
(
final
PipelineOwner
child
in
_children
)
{
child
.
flushPaint
();
}
assert
(
_nodesNeedingPaint
.
isEmpty
,
'Child PipelineOwners must not dirty nodes in their parent.'
);
assert
(
_nodesNeedingPaint
.
isEmpty
);
}
finally
{
assert
(()
{
_debugDoingPaint
=
false
;
...
...
@@ -1170,13 +1130,11 @@ class PipelineOwner {
/// The object that is managing semantics for this pipeline owner, if any.
///
/// An owner is created by [ensureSemantics] or when the [PipelineManifold] to
/// which this owner is connected has [PipelineManifold.semanticsEnabled] set
/// to true. The owner is valid for as long as
/// [PipelineManifold.semanticsEnabled] remains true or while there are
/// outstanding [SemanticsHandle]s from calls to [ensureSemantics]. The
/// [semanticsOwner] field will revert to null once both conditions are no
/// longer met.
/// An owner is created by [ensureSemantics]. The owner is valid for as long
/// there are [SemanticsHandle]s returned by [ensureSemantics] that have not
/// yet been disposed. Once the last handle has been disposed, the
/// [semanticsOwner] field will revert to null, and the previous owner will be
/// disposed.
///
/// When [semanticsOwner] is null, the [PipelineOwner] skips all steps
/// relating to semantics.
...
...
@@ -1209,28 +1167,23 @@ class PipelineOwner {
/// maintaining the semantics tree.
SemanticsHandle
ensureSemantics
({
VoidCallback
?
listener
})
{
_outstandingSemanticsHandles
+=
1
;
_updateSemanticsOwner
();
return
_LocalSemanticsHandle
.
_
(
this
,
listener
);
}
void
_updateSemanticsOwner
()
{
if
((
_manifold
?.
semanticsEnabled
??
false
)
||
_outstandingSemanticsHandles
>
0
)
{
if
(
_semanticsOwner
==
null
)
{
assert
(
onSemanticsUpdate
!=
null
,
'Attempted to enable semantics without configuring an onSemanticsUpdate callback.'
);
_semanticsOwner
=
SemanticsOwner
(
onSemanticsUpdate:
onSemanticsUpdate
!);
onSemanticsOwnerCreated
?.
call
();
}
}
else
if
(
_semanticsOwner
!=
null
)
{
_semanticsOwner
?.
dispose
();
_semanticsOwner
=
null
;
onSemanticsOwnerDisposed
?.
call
();
if
(
_outstandingSemanticsHandles
==
1
)
{
assert
(
_semanticsOwner
==
null
);
assert
(
onSemanticsUpdate
!=
null
,
'Attempted to open a semantics handle without an onSemanticsUpdate callback.'
);
_semanticsOwner
=
SemanticsOwner
(
onSemanticsUpdate:
onSemanticsUpdate
!);
onSemanticsOwnerCreated
?.
call
();
}
return
_LocalSemanticsHandle
.
_
(
this
,
listener
);
}
void
_didDisposeSemanticsHandle
()
{
assert
(
_semanticsOwner
!=
null
);
_outstandingSemanticsHandles
-=
1
;
_updateSemanticsOwner
();
if
(
_outstandingSemanticsHandles
==
0
)
{
_semanticsOwner
!.
dispose
();
_semanticsOwner
=
null
;
onSemanticsOwnerDisposed
?.
call
();
}
}
bool
_debugDoingSemantics
=
false
;
...
...
@@ -1269,11 +1222,8 @@ class PipelineOwner {
}
}
_semanticsOwner
!.
sendSemanticsUpdate
();
for
(
final
PipelineOwner
child
in
_children
)
{
child
.
flushSemantics
();
}
assert
(
_nodesNeedingSemantics
.
isEmpty
,
'Child PipelineOwners must not dirty nodes in their parent.'
);
}
finally
{
assert
(
_nodesNeedingSemantics
.
isEmpty
);
assert
(()
{
_debugDoingSemantics
=
false
;
return
true
;
...
...
@@ -1283,176 +1233,6 @@ class PipelineOwner {
}
}
}
// TREE MANAGEMENT
final
Set
<
PipelineOwner
>
_children
=
<
PipelineOwner
>{};
PipelineManifold
?
_manifold
;
PipelineOwner
?
_debugParent
;
bool
_debugSetParent
(
PipelineOwner
child
,
PipelineOwner
?
parent
)
{
child
.
_debugParent
=
parent
;
return
true
;
}
/// Mark this [PipelineOwner] as attached to the given [PipelineManifold].
///
/// Typically, this is only called directly on the root [PipelineOwner].
/// Children are automatically attached to their parent's [PipelineManifold]
/// when [adoptChild] is called.
void
attach
(
PipelineManifold
manifold
)
{
assert
(
_manifold
==
null
);
_manifold
=
manifold
;
_manifold
!.
addListener
(
_updateSemanticsOwner
);
_updateSemanticsOwner
();
// If onNeedVisualUpdate is specified, it has already been called when the node was dirtied in the first place.
if
(
onNeedVisualUpdate
==
null
&&
(
_nodesNeedingLayout
.
isNotEmpty
||
_nodesNeedingCompositingBitsUpdate
.
isNotEmpty
||
_nodesNeedingPaint
.
isNotEmpty
||
_nodesNeedingSemantics
.
isNotEmpty
))
{
requestVisualUpdate
();
}
for
(
final
PipelineOwner
child
in
_children
)
{
child
.
attach
(
manifold
);
}
}
/// Mark this [PipelineOwner] as detached.
///
/// Typically, this is only called directly on the root [PipelineOwner].
/// Children are automatically detached from their parent's [PipelineManifold]
/// when [dropChild] is called.
void
detach
()
{
assert
(
_manifold
!=
null
);
_manifold
!.
removeListener
(
_updateSemanticsOwner
);
_manifold
=
null
;
_updateSemanticsOwner
();
for
(
final
PipelineOwner
child
in
_children
)
{
child
.
detach
();
}
}
// In theory, child list modifications are also disallowed between
// _debugDoingChildrenLayout and _debugDoingPaint as well as between
// _debugDoingPaint and _debugDoingSemantics. However, since the associated
// flush methods are usually called back to back, this gets us close enough.
bool
get
_debugAllowChildListModifications
=>
!
_debugDoingChildLayout
&&
!
_debugDoingPaint
&&
!
_debugDoingSemantics
;
/// Adds `child` to this [PipelineOwner].
///
/// During the phases of frame production (see [RendererBinding.drawFrame]),
/// the parent [PipelineOwner] will complete a phase for the nodes it owns
/// directly before invoking the flush method corresponding to the current
/// phase on its child [PipelineOwner]s. For example, during layout, the
/// parent [PipelineOwner] will first lay out its own nodes before calling
/// [flushLayout] on its children. During paint, it will first paint its own
/// nodes before calling [flushPaint] on its children. This order also applies
/// for all the other phases.
///
/// No assumptions must be made about the order in which child
/// [PipelineOwner]s are flushed.
///
/// No new children may be added after the [PipelineOwner] has started calling
/// [flushLayout] on any of its children until the end of the current frame.
///
/// To remove a child, call [dropChild].
void
adoptChild
(
PipelineOwner
child
)
{
assert
(
child
.
_debugParent
==
null
);
assert
(!
_children
.
contains
(
child
));
assert
(
_debugAllowChildListModifications
,
'Cannot modify child list after layout.'
);
_children
.
add
(
child
);
assert
(
_debugSetParent
(
child
,
this
));
if
(
_manifold
!=
null
)
{
child
.
attach
(
_manifold
!);
}
}
/// Removes a child [PipelineOwner] previously added via [adoptChild].
///
/// This node will cease to call the flush methods on the `child` during frame
/// production.
///
/// No children may be removed after the [PipelineOwner] has started calling
/// [flushLayout] on any of its children until the end of the current frame.
void
dropChild
(
PipelineOwner
child
)
{
assert
(
child
.
_debugParent
==
this
);
assert
(
_children
.
contains
(
child
));
assert
(
_debugAllowChildListModifications
,
'Cannot modify child list after layout.'
);
_children
.
remove
(
child
);
assert
(
_debugSetParent
(
child
,
null
));
if
(
_manifold
!=
null
)
{
child
.
detach
();
}
}
/// Calls `visitor` for each immediate child of this [PipelineOwner].
///
/// See also:
///
/// * [adoptChild] to add a child.
/// * [dropChild] to remove a child.
void
visitChildren
(
PipelineOwnerVisitor
visitor
)
{
_children
.
forEach
(
visitor
);
}
}
/// Signature for the callback to [PipelineOwner.visitChildren].
///
/// The argument is the child being visited.
typedef
PipelineOwnerVisitor
=
void
Function
(
PipelineOwner
child
);
/// Manages a tree of [PipelineOwner]s.
///
/// All [PipelineOwner]s within a tree are attached to the same
/// [PipelineManifold], which gives them access to shared functionality such
/// as requesting a visual update (by calling [requestVisualUpdate]). As such,
/// the [PipelineManifold] gives the [PipelineOwner]s access to functionality
/// usually provided by the bindings without tying the [PipelineOwner]s to a
/// particular binding implementation.
///
/// The root of the [PipelineOwner] tree is attached to a [PipelineManifold] by
/// passing the manifold to [PipelineOwner.attach]. Children are attached to the
/// same [PipelineManifold] as their parent when they are adopted via
/// [PipelineOwner.adoptChild].
///
/// [PipelineOwner]s can register listeners with the [PipelineManifold] to be
/// informed when certain values provided by the [PipelineManifold] change.
abstract
class
PipelineManifold
implements
Listenable
{
/// Whether [PipelineOwner]s connected to this [PipelineManifold] should
/// collect semantics information and produce a semantics tree.
///
/// The [PipelineManifold] notifies its listeners (managed with [addListener]
/// and [removeListener]) when this property changes its value.
///
/// See also:
///
/// * [SemanticsBinding.semanticsEnabled], which [PipelineManifold]
/// implementations typically use to back this property.
bool
get
semanticsEnabled
;
/// Called by a [PipelineOwner] connected to this [PipelineManifold] when a
/// [RenderObject] associated with that pipeline owner wishes to update its
/// visual appearance.
///
/// Typical implementations of this function will schedule a task to flush the
/// various stages of the pipeline. This function might be called multiple
/// times in quick succession. Implementations should take care to discard
/// duplicate calls quickly.
///
/// A [PipelineOwner] connected to this [PipelineManifold] will call
/// [PipelineOwner.onNeedVisualUpdate] instead of this method if it has been
/// configured with a non-null [PipelineOwner.onNeedVisualUpdate] callback.
///
/// See also:
///
/// * [SchedulerBinding.ensureVisualUpdate], which [PipelineManifold]
/// implementations typically call to implement this method.
void
requestVisualUpdate
();
}
const
String
_flutterRenderingLibrary
=
'package:flutter/rendering.dart'
;
...
...
packages/flutter/test/rendering/binding_pipeline_manifold_init_test.dart
deleted
100644 → 0
View file @
f73c358e
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'package:flutter/rendering.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
void
main
(
)
{
test
(
'Initializing the RendererBinding does not crash when semantics is enabled'
,
()
{
try
{
MyRenderingFlutterBinding
();
}
catch
(
e
)
{
fail
(
'Initializing the RenderingBinding threw an unexpected error:
\n
$e
'
);
}
expect
(
RendererBinding
.
instance
,
isA
<
MyRenderingFlutterBinding
>());
expect
(
SemanticsBinding
.
instance
.
semanticsEnabled
,
isTrue
);
});
}
// Binding that pretends the platform had semantics enabled before the binding
// is initialized.
class
MyRenderingFlutterBinding
extends
RenderingFlutterBinding
{
@override
bool
get
semanticsEnabled
=>
true
;
}
packages/flutter/test/rendering/binding_pipeline_manifold_test.dart
deleted
100644 → 0
View file @
f73c358e
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'package:flutter/foundation.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
import
'rendering_tester.dart'
;
void
main
(
)
{
MyTestRenderingFlutterBinding
.
ensureInitialized
();
tearDown
(()
{
final
List
<
PipelineOwner
>
children
=
<
PipelineOwner
>[];
RendererBinding
.
instance
.
pipelineOwner
.
visitChildren
((
PipelineOwner
child
)
{
children
.
add
(
child
);
});
children
.
forEach
(
RendererBinding
.
instance
.
pipelineOwner
.
dropChild
);
});
test
(
"BindingPipelineManifold notifies binding if render object managed by binding's PipelineOwner tree needs visual update"
,
()
{
final
PipelineOwner
child
=
PipelineOwner
();
RendererBinding
.
instance
.
pipelineOwner
.
adoptChild
(
child
);
final
RenderObject
renderObject
=
TestRenderObject
();
child
.
rootNode
=
renderObject
;
renderObject
.
scheduleInitialLayout
();
RendererBinding
.
instance
.
pipelineOwner
.
flushLayout
();
MyTestRenderingFlutterBinding
.
instance
.
ensureVisualUpdateCount
=
0
;
renderObject
.
markNeedsLayout
();
expect
(
MyTestRenderingFlutterBinding
.
instance
.
ensureVisualUpdateCount
,
1
);
});
test
(
'Turning global semantics on/off creates semantics owners in PipelineOwner tree'
,
()
{
final
PipelineOwner
child
=
PipelineOwner
(
onSemanticsUpdate:
(
_
)
{
},
);
RendererBinding
.
instance
.
pipelineOwner
.
adoptChild
(
child
);
expect
(
child
.
semanticsOwner
,
isNull
);
expect
(
RendererBinding
.
instance
.
pipelineOwner
.
semanticsOwner
,
isNull
);
final
SemanticsHandle
handle
=
SemanticsBinding
.
instance
.
ensureSemantics
();
expect
(
child
.
semanticsOwner
,
isNotNull
);
expect
(
RendererBinding
.
instance
.
pipelineOwner
.
semanticsOwner
,
isNotNull
);
handle
.
dispose
();
expect
(
child
.
semanticsOwner
,
isNull
);
expect
(
RendererBinding
.
instance
.
pipelineOwner
.
semanticsOwner
,
isNull
);
});
}
class
MyTestRenderingFlutterBinding
extends
TestRenderingFlutterBinding
{
static
MyTestRenderingFlutterBinding
get
instance
=>
BindingBase
.
checkInstance
(
_instance
);
static
MyTestRenderingFlutterBinding
?
_instance
;
static
MyTestRenderingFlutterBinding
ensureInitialized
()
{
if
(
_instance
!=
null
)
{
return
_instance
!;
}
return
MyTestRenderingFlutterBinding
();
}
@override
void
initInstances
()
{
super
.
initInstances
();
_instance
=
this
;
}
int
ensureVisualUpdateCount
=
0
;
@override
void
ensureVisualUpdate
()
{
super
.
ensureVisualUpdate
();
ensureVisualUpdateCount
++;
}
}
class
TestRenderObject
extends
RenderObject
{
@override
void
debugAssertDoesMeetConstraints
()
{
}
@override
Rect
get
paintBounds
=>
Rect
.
zero
;
@override
void
performLayout
()
{
}
@override
void
performResize
()
{
}
@override
Rect
get
semanticBounds
=>
Rect
.
zero
;
}
packages/flutter/test/rendering/pipeline_owner_tree_test.dart
deleted
100644 → 0
View file @
f73c358e
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'dart:ui'
;
import
'package:flutter/material.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
void
main
(
)
{
FlutterError
.
presentError
=
(
FlutterErrorDetails
details
)
{
// Make tests fail on FlutterErrors.
throw
details
.
exception
;
};
test
(
'onNeedVisualUpdate takes precedence over manifold'
,
()
{
final
TestPipelineManifold
manifold
=
TestPipelineManifold
();
int
rootOnNeedVisualUpdateCallCount
=
0
;
final
TestRenderObject
rootRenderObject
=
TestRenderObject
();
final
PipelineOwner
root
=
PipelineOwner
(
onNeedVisualUpdate:
()
{
rootOnNeedVisualUpdateCallCount
+=
1
;
},
);
root
.
rootNode
=
rootRenderObject
;
rootRenderObject
.
scheduleInitialLayout
();
int
child1OnNeedVisualUpdateCallCount
=
0
;
final
TestRenderObject
child1RenderObject
=
TestRenderObject
();
final
PipelineOwner
child1
=
PipelineOwner
(
onNeedVisualUpdate:
()
{
child1OnNeedVisualUpdateCallCount
+=
1
;
},
);
child1
.
rootNode
=
child1RenderObject
;
child1RenderObject
.
scheduleInitialLayout
();
final
TestRenderObject
child2RenderObject
=
TestRenderObject
();
final
PipelineOwner
child2
=
PipelineOwner
();
child2
.
rootNode
=
child2RenderObject
;
child2RenderObject
.
scheduleInitialLayout
();
root
.
adoptChild
(
child1
);
root
.
adoptChild
(
child2
);
root
.
attach
(
manifold
);
root
.
flushLayout
();
manifold
.
requestVisualUpdateCount
=
0
;
rootRenderObject
.
markNeedsLayout
();
expect
(
manifold
.
requestVisualUpdateCount
,
0
);
expect
(
rootOnNeedVisualUpdateCallCount
,
1
);
expect
(
child1OnNeedVisualUpdateCallCount
,
0
);
child1RenderObject
.
markNeedsLayout
();
expect
(
manifold
.
requestVisualUpdateCount
,
0
);
expect
(
rootOnNeedVisualUpdateCallCount
,
1
);
expect
(
child1OnNeedVisualUpdateCallCount
,
1
);
child2RenderObject
.
markNeedsLayout
();
expect
(
manifold
.
requestVisualUpdateCount
,
1
);
expect
(
rootOnNeedVisualUpdateCallCount
,
1
);
expect
(
child1OnNeedVisualUpdateCallCount
,
1
);
});
test
(
"parent's render objects are laid out before child's render objects"
,
()
{
final
TestPipelineManifold
manifold
=
TestPipelineManifold
();
final
List
<
String
>
log
=
<
String
>[];
final
TestRenderObject
rootRenderObject
=
TestRenderObject
(
onLayout:
()
{
log
.
add
(
'layout parent'
);
},
);
final
PipelineOwner
root
=
PipelineOwner
();
root
.
rootNode
=
rootRenderObject
;
rootRenderObject
.
scheduleInitialLayout
();
final
TestRenderObject
childRenderObject
=
TestRenderObject
(
onLayout:
()
{
log
.
add
(
'layout child'
);
},
);
final
PipelineOwner
child
=
PipelineOwner
();
child
.
rootNode
=
childRenderObject
;
childRenderObject
.
scheduleInitialLayout
();
root
.
adoptChild
(
child
);
root
.
attach
(
manifold
);
expect
(
log
,
isEmpty
);
root
.
flushLayout
();
expect
(
log
,
<
String
>[
'layout parent'
,
'layout child'
]);
});
test
(
"child cannot dirty parent's render object during flushLayout"
,
()
{
final
TestPipelineManifold
manifold
=
TestPipelineManifold
();
final
TestRenderObject
rootRenderObject
=
TestRenderObject
();
final
PipelineOwner
root
=
PipelineOwner
();
root
.
rootNode
=
rootRenderObject
;
rootRenderObject
.
scheduleInitialLayout
();
bool
childLayoutExecuted
=
false
;
final
TestRenderObject
childRenderObject
=
TestRenderObject
(
onLayout:
()
{
childLayoutExecuted
=
true
;
expect
(()
=>
rootRenderObject
.
markNeedsLayout
(),
throwsFlutterError
);
},
);
final
PipelineOwner
child
=
PipelineOwner
();
child
.
rootNode
=
childRenderObject
;
childRenderObject
.
scheduleInitialLayout
();
root
.
adoptChild
(
child
);
root
.
attach
(
manifold
);
root
.
flushLayout
();
expect
(
childLayoutExecuted
,
isTrue
);
});
test
(
'updates compositing bits on children'
,
()
{
final
TestPipelineManifold
manifold
=
TestPipelineManifold
();
final
TestRenderObject
rootRenderObject
=
TestRenderObject
();
final
PipelineOwner
root
=
PipelineOwner
();
root
.
rootNode
=
rootRenderObject
;
rootRenderObject
.
markNeedsCompositingBitsUpdate
();
final
TestRenderObject
childRenderObject
=
TestRenderObject
();
final
PipelineOwner
child
=
PipelineOwner
();
child
.
rootNode
=
childRenderObject
;
childRenderObject
.
markNeedsCompositingBitsUpdate
();
root
.
adoptChild
(
child
);
root
.
attach
(
manifold
);
expect
(()
=>
rootRenderObject
.
needsCompositing
,
throwsAssertionError
);
expect
(()
=>
childRenderObject
.
needsCompositing
,
throwsAssertionError
);
root
.
flushCompositingBits
();
expect
(
rootRenderObject
.
needsCompositing
,
isTrue
);
expect
(
childRenderObject
.
needsCompositing
,
isTrue
);
});
test
(
"parent's render objects are painted before child's render objects"
,
()
{
final
TestPipelineManifold
manifold
=
TestPipelineManifold
();
final
List
<
String
>
log
=
<
String
>[];
final
TestRenderObject
rootRenderObject
=
TestRenderObject
(
onPaint:
()
{
log
.
add
(
'paint parent'
);
},
);
final
PipelineOwner
root
=
PipelineOwner
();
root
.
rootNode
=
rootRenderObject
;
final
OffsetLayer
rootLayer
=
OffsetLayer
();
rootLayer
.
attach
(
rootRenderObject
);
rootRenderObject
.
scheduleInitialLayout
();
rootRenderObject
.
scheduleInitialPaint
(
rootLayer
);
final
TestRenderObject
childRenderObject
=
TestRenderObject
(
onPaint:
()
{
log
.
add
(
'paint child'
);
},
);
final
PipelineOwner
child
=
PipelineOwner
();
child
.
rootNode
=
childRenderObject
;
final
OffsetLayer
childLayer
=
OffsetLayer
();
childLayer
.
attach
(
childRenderObject
);
childRenderObject
.
scheduleInitialLayout
();
childRenderObject
.
scheduleInitialPaint
(
childLayer
);
root
.
adoptChild
(
child
);
root
.
attach
(
manifold
);
root
.
flushLayout
();
// Can't paint with invalid layout.
expect
(
log
,
isEmpty
);
root
.
flushPaint
();
expect
(
log
,
<
String
>[
'paint parent'
,
'paint child'
]);
});
test
(
"child paint cannot dirty parent's render object"
,
()
{
final
TestPipelineManifold
manifold
=
TestPipelineManifold
();
final
TestRenderObject
rootRenderObject
=
TestRenderObject
();
final
PipelineOwner
root
=
PipelineOwner
();
root
.
rootNode
=
rootRenderObject
;
final
OffsetLayer
rootLayer
=
OffsetLayer
();
rootLayer
.
attach
(
rootRenderObject
);
rootRenderObject
.
scheduleInitialLayout
();
rootRenderObject
.
scheduleInitialPaint
(
rootLayer
);
bool
childPaintExecuted
=
false
;
final
TestRenderObject
childRenderObject
=
TestRenderObject
(
onPaint:
()
{
childPaintExecuted
=
true
;
expect
(()
=>
rootRenderObject
.
markNeedsPaint
(),
throwsAssertionError
);
},
);
final
PipelineOwner
child
=
PipelineOwner
();
child
.
rootNode
=
childRenderObject
;
final
OffsetLayer
childLayer
=
OffsetLayer
();
childLayer
.
attach
(
childRenderObject
);
childRenderObject
.
scheduleInitialLayout
();
childRenderObject
.
scheduleInitialPaint
(
childLayer
);
root
.
adoptChild
(
child
);
root
.
attach
(
manifold
);
root
.
flushLayout
();
// Can't paint with invalid layout.
root
.
flushPaint
();
expect
(
childPaintExecuted
,
isTrue
);
});
test
(
"parent's render objects do semantics before child's render objects"
,
()
{
final
TestPipelineManifold
manifold
=
TestPipelineManifold
()
..
semanticsEnabled
=
true
;
final
List
<
String
>
log
=
<
String
>[];
final
TestRenderObject
rootRenderObject
=
TestRenderObject
(
onSemantics:
()
{
log
.
add
(
'semantics parent'
);
},
);
final
PipelineOwner
root
=
PipelineOwner
(
onSemanticsOwnerCreated:
()
{
rootRenderObject
.
scheduleInitialSemantics
();
},
onSemanticsUpdate:
(
SemanticsUpdate
update
)
{
},
);
root
.
rootNode
=
rootRenderObject
;
final
TestRenderObject
childRenderObject
=
TestRenderObject
(
onSemantics:
()
{
log
.
add
(
'semantics child'
);
},
);
final
PipelineOwner
child
=
PipelineOwner
(
onSemanticsOwnerCreated:
()
{
childRenderObject
.
scheduleInitialSemantics
();
},
onSemanticsUpdate:
(
SemanticsUpdate
update
)
{
},
);
child
.
rootNode
=
childRenderObject
;
root
.
adoptChild
(
child
);
root
.
attach
(
manifold
);
log
.
clear
();
rootRenderObject
.
markNeedsSemanticsUpdate
();
childRenderObject
.
markNeedsSemanticsUpdate
();
root
.
flushSemantics
();
expect
(
log
,
<
String
>[
'semantics parent'
,
'semantics child'
]);
});
test
(
"child cannot mark parent's render object dirty during flushSemantics"
,
()
{
final
TestPipelineManifold
manifold
=
TestPipelineManifold
()
..
semanticsEnabled
=
true
;
final
TestRenderObject
rootRenderObject
=
TestRenderObject
();
final
PipelineOwner
root
=
PipelineOwner
(
onSemanticsOwnerCreated:
()
{
rootRenderObject
.
scheduleInitialSemantics
();
},
onSemanticsUpdate:
(
SemanticsUpdate
update
)
{
},
);
root
.
rootNode
=
rootRenderObject
;
bool
childSemanticsCalled
=
false
;
final
TestRenderObject
childRenderObject
=
TestRenderObject
(
onSemantics:
()
{
childSemanticsCalled
=
true
;
rootRenderObject
.
markNeedsSemanticsUpdate
();
},
);
final
PipelineOwner
child
=
PipelineOwner
(
onSemanticsOwnerCreated:
()
{
childRenderObject
.
scheduleInitialSemantics
();
},
onSemanticsUpdate:
(
SemanticsUpdate
update
)
{
},
);
child
.
rootNode
=
childRenderObject
;
root
.
adoptChild
(
child
);
root
.
attach
(
manifold
);
rootRenderObject
.
markNeedsSemanticsUpdate
();
childRenderObject
.
markNeedsSemanticsUpdate
();
root
.
flushSemantics
();
expect
(
childSemanticsCalled
,
isTrue
);
});
test
(
'when manifold enables semantics all PipelineOwners in tree create SemanticsOwner'
,
()
{
final
TestPipelineManifold
manifold
=
TestPipelineManifold
();
int
rootOnSemanticsOwnerCreatedCount
=
0
;
int
rootOnSemanticsOwnerDisposed
=
0
;
final
PipelineOwner
root
=
PipelineOwner
(
onSemanticsOwnerCreated:
()
{
rootOnSemanticsOwnerCreatedCount
++;
},
onSemanticsUpdate:
(
SemanticsUpdate
update
)
{
},
onSemanticsOwnerDisposed:
()
{
rootOnSemanticsOwnerDisposed
++;
},
);
int
childOnSemanticsOwnerCreatedCount
=
0
;
int
childOnSemanticsOwnerDisposed
=
0
;
final
PipelineOwner
child
=
PipelineOwner
(
onSemanticsOwnerCreated:
()
{
childOnSemanticsOwnerCreatedCount
++;
},
onSemanticsUpdate:
(
SemanticsUpdate
update
)
{
},
onSemanticsOwnerDisposed:
()
{
childOnSemanticsOwnerDisposed
++;
},
);
root
.
adoptChild
(
child
);
root
.
attach
(
manifold
);
expect
(
rootOnSemanticsOwnerCreatedCount
,
0
);
expect
(
childOnSemanticsOwnerCreatedCount
,
0
);
expect
(
rootOnSemanticsOwnerDisposed
,
0
);
expect
(
childOnSemanticsOwnerDisposed
,
0
);
expect
(
root
.
semanticsOwner
,
isNull
);
expect
(
child
.
semanticsOwner
,
isNull
);
manifold
.
semanticsEnabled
=
true
;
expect
(
rootOnSemanticsOwnerCreatedCount
,
1
);
expect
(
childOnSemanticsOwnerCreatedCount
,
1
);
expect
(
rootOnSemanticsOwnerDisposed
,
0
);
expect
(
childOnSemanticsOwnerDisposed
,
0
);
expect
(
root
.
semanticsOwner
,
isNotNull
);
expect
(
child
.
semanticsOwner
,
isNotNull
);
manifold
.
semanticsEnabled
=
false
;
expect
(
rootOnSemanticsOwnerCreatedCount
,
1
);
expect
(
childOnSemanticsOwnerCreatedCount
,
1
);
expect
(
rootOnSemanticsOwnerDisposed
,
1
);
expect
(
childOnSemanticsOwnerDisposed
,
1
);
expect
(
root
.
semanticsOwner
,
isNull
);
expect
(
child
.
semanticsOwner
,
isNull
);
});
test
(
'when manifold enables semantics all PipelineOwners in tree that did not have a SemanticsOwner create one'
,
()
{
final
TestPipelineManifold
manifold
=
TestPipelineManifold
();
int
rootOnSemanticsOwnerCreatedCount
=
0
;
int
rootOnSemanticsOwnerDisposed
=
0
;
final
PipelineOwner
root
=
PipelineOwner
(
onSemanticsOwnerCreated:
()
{
rootOnSemanticsOwnerCreatedCount
++;
},
onSemanticsUpdate:
(
SemanticsUpdate
update
)
{
},
onSemanticsOwnerDisposed:
()
{
rootOnSemanticsOwnerDisposed
++;
},
);
int
childOnSemanticsOwnerCreatedCount
=
0
;
int
childOnSemanticsOwnerDisposed
=
0
;
final
PipelineOwner
child
=
PipelineOwner
(
onSemanticsOwnerCreated:
()
{
childOnSemanticsOwnerCreatedCount
++;
},
onSemanticsUpdate:
(
SemanticsUpdate
update
)
{
},
onSemanticsOwnerDisposed:
()
{
childOnSemanticsOwnerDisposed
++;
},
);
root
.
adoptChild
(
child
);
root
.
attach
(
manifold
);
final
SemanticsHandle
childSemantics
=
child
.
ensureSemantics
();
expect
(
rootOnSemanticsOwnerCreatedCount
,
0
);
expect
(
childOnSemanticsOwnerCreatedCount
,
1
);
expect
(
rootOnSemanticsOwnerDisposed
,
0
);
expect
(
childOnSemanticsOwnerDisposed
,
0
);
expect
(
root
.
semanticsOwner
,
isNull
);
expect
(
child
.
semanticsOwner
,
isNotNull
);
manifold
.
semanticsEnabled
=
true
;
expect
(
rootOnSemanticsOwnerCreatedCount
,
1
);
expect
(
childOnSemanticsOwnerCreatedCount
,
1
);
expect
(
rootOnSemanticsOwnerDisposed
,
0
);
expect
(
childOnSemanticsOwnerDisposed
,
0
);
expect
(
root
.
semanticsOwner
,
isNotNull
);
expect
(
child
.
semanticsOwner
,
isNotNull
);
manifold
.
semanticsEnabled
=
false
;
expect
(
rootOnSemanticsOwnerCreatedCount
,
1
);
expect
(
childOnSemanticsOwnerCreatedCount
,
1
);
expect
(
rootOnSemanticsOwnerDisposed
,
1
);
expect
(
childOnSemanticsOwnerDisposed
,
0
);
expect
(
root
.
semanticsOwner
,
isNull
);
expect
(
child
.
semanticsOwner
,
isNotNull
);
childSemantics
.
dispose
();
expect
(
rootOnSemanticsOwnerCreatedCount
,
1
);
expect
(
childOnSemanticsOwnerCreatedCount
,
1
);
expect
(
rootOnSemanticsOwnerDisposed
,
1
);
expect
(
childOnSemanticsOwnerDisposed
,
1
);
expect
(
root
.
semanticsOwner
,
isNull
);
expect
(
child
.
semanticsOwner
,
isNull
);
});
test
(
'PipelineOwner can dispose local handle even when manifold forces semantics to on'
,
()
{
final
TestPipelineManifold
manifold
=
TestPipelineManifold
();
int
rootOnSemanticsOwnerCreatedCount
=
0
;
int
rootOnSemanticsOwnerDisposed
=
0
;
final
PipelineOwner
root
=
PipelineOwner
(
onSemanticsOwnerCreated:
()
{
rootOnSemanticsOwnerCreatedCount
++;
},
onSemanticsUpdate:
(
SemanticsUpdate
update
)
{
},
onSemanticsOwnerDisposed:
()
{
rootOnSemanticsOwnerDisposed
++;
},
);
int
childOnSemanticsOwnerCreatedCount
=
0
;
int
childOnSemanticsOwnerDisposed
=
0
;
final
PipelineOwner
child
=
PipelineOwner
(
onSemanticsOwnerCreated:
()
{
childOnSemanticsOwnerCreatedCount
++;
},
onSemanticsUpdate:
(
SemanticsUpdate
update
)
{
},
onSemanticsOwnerDisposed:
()
{
childOnSemanticsOwnerDisposed
++;
},
);
root
.
adoptChild
(
child
);
root
.
attach
(
manifold
);
final
SemanticsHandle
childSemantics
=
child
.
ensureSemantics
();
expect
(
rootOnSemanticsOwnerCreatedCount
,
0
);
expect
(
childOnSemanticsOwnerCreatedCount
,
1
);
expect
(
rootOnSemanticsOwnerDisposed
,
0
);
expect
(
childOnSemanticsOwnerDisposed
,
0
);
expect
(
root
.
semanticsOwner
,
isNull
);
expect
(
child
.
semanticsOwner
,
isNotNull
);
manifold
.
semanticsEnabled
=
true
;
expect
(
rootOnSemanticsOwnerCreatedCount
,
1
);
expect
(
childOnSemanticsOwnerCreatedCount
,
1
);
expect
(
rootOnSemanticsOwnerDisposed
,
0
);
expect
(
childOnSemanticsOwnerDisposed
,
0
);
expect
(
root
.
semanticsOwner
,
isNotNull
);
expect
(
child
.
semanticsOwner
,
isNotNull
);
childSemantics
.
dispose
();
expect
(
rootOnSemanticsOwnerCreatedCount
,
1
);
expect
(
childOnSemanticsOwnerCreatedCount
,
1
);
expect
(
rootOnSemanticsOwnerDisposed
,
0
);
expect
(
childOnSemanticsOwnerDisposed
,
0
);
expect
(
root
.
semanticsOwner
,
isNotNull
);
expect
(
child
.
semanticsOwner
,
isNotNull
);
manifold
.
semanticsEnabled
=
false
;
expect
(
rootOnSemanticsOwnerCreatedCount
,
1
);
expect
(
childOnSemanticsOwnerCreatedCount
,
1
);
expect
(
rootOnSemanticsOwnerDisposed
,
1
);
expect
(
childOnSemanticsOwnerDisposed
,
1
);
expect
(
root
.
semanticsOwner
,
isNull
);
expect
(
child
.
semanticsOwner
,
isNull
);
});
test
(
'can hold on to local handle when manifold turns off semantics'
,
()
{
final
TestPipelineManifold
manifold
=
TestPipelineManifold
();
int
rootOnSemanticsOwnerCreatedCount
=
0
;
int
rootOnSemanticsOwnerDisposed
=
0
;
final
PipelineOwner
root
=
PipelineOwner
(
onSemanticsOwnerCreated:
()
{
rootOnSemanticsOwnerCreatedCount
++;
},
onSemanticsUpdate:
(
SemanticsUpdate
update
)
{
},
onSemanticsOwnerDisposed:
()
{
rootOnSemanticsOwnerDisposed
++;
},
);
int
childOnSemanticsOwnerCreatedCount
=
0
;
int
childOnSemanticsOwnerDisposed
=
0
;
final
PipelineOwner
child
=
PipelineOwner
(
onSemanticsOwnerCreated:
()
{
childOnSemanticsOwnerCreatedCount
++;
},
onSemanticsUpdate:
(
SemanticsUpdate
update
)
{
},
onSemanticsOwnerDisposed:
()
{
childOnSemanticsOwnerDisposed
++;
},
);
root
.
adoptChild
(
child
);
root
.
attach
(
manifold
);
expect
(
rootOnSemanticsOwnerCreatedCount
,
0
);
expect
(
childOnSemanticsOwnerCreatedCount
,
0
);
expect
(
rootOnSemanticsOwnerDisposed
,
0
);
expect
(
childOnSemanticsOwnerDisposed
,
0
);
expect
(
root
.
semanticsOwner
,
isNull
);
expect
(
child
.
semanticsOwner
,
isNull
);
manifold
.
semanticsEnabled
=
true
;
expect
(
rootOnSemanticsOwnerCreatedCount
,
1
);
expect
(
childOnSemanticsOwnerCreatedCount
,
1
);
expect
(
rootOnSemanticsOwnerDisposed
,
0
);
expect
(
childOnSemanticsOwnerDisposed
,
0
);
expect
(
root
.
semanticsOwner
,
isNotNull
);
expect
(
child
.
semanticsOwner
,
isNotNull
);
final
SemanticsHandle
childSemantics
=
child
.
ensureSemantics
();
expect
(
rootOnSemanticsOwnerCreatedCount
,
1
);
expect
(
childOnSemanticsOwnerCreatedCount
,
1
);
expect
(
rootOnSemanticsOwnerDisposed
,
0
);
expect
(
childOnSemanticsOwnerDisposed
,
0
);
expect
(
root
.
semanticsOwner
,
isNotNull
);
expect
(
child
.
semanticsOwner
,
isNotNull
);
manifold
.
semanticsEnabled
=
false
;
expect
(
rootOnSemanticsOwnerCreatedCount
,
1
);
expect
(
childOnSemanticsOwnerCreatedCount
,
1
);
expect
(
rootOnSemanticsOwnerDisposed
,
1
);
expect
(
childOnSemanticsOwnerDisposed
,
0
);
expect
(
root
.
semanticsOwner
,
isNull
);
expect
(
child
.
semanticsOwner
,
isNotNull
);
childSemantics
.
dispose
();
expect
(
rootOnSemanticsOwnerCreatedCount
,
1
);
expect
(
childOnSemanticsOwnerCreatedCount
,
1
);
expect
(
rootOnSemanticsOwnerDisposed
,
1
);
expect
(
childOnSemanticsOwnerDisposed
,
1
);
expect
(
root
.
semanticsOwner
,
isNull
);
expect
(
child
.
semanticsOwner
,
isNull
);
});
test
(
'cannot attach when already attached'
,
()
{
final
TestPipelineManifold
manifold
=
TestPipelineManifold
();
final
PipelineOwner
owner
=
PipelineOwner
();
owner
.
attach
(
manifold
);
expect
(()
=>
owner
.
attach
(
manifold
),
throwsAssertionError
);
});
test
(
'attach update semanticsOwner'
,
()
{
final
TestPipelineManifold
manifold
=
TestPipelineManifold
()
..
semanticsEnabled
=
true
;
final
PipelineOwner
owner
=
PipelineOwner
(
onSemanticsUpdate:
(
_
)
{
},
);
expect
(
owner
.
semanticsOwner
,
isNull
);
owner
.
attach
(
manifold
);
expect
(
owner
.
semanticsOwner
,
isNotNull
);
});
test
(
'attach does not request visual update if nothing is dirty'
,
()
{
final
TestPipelineManifold
manifold
=
TestPipelineManifold
();
final
TestRenderObject
renderObject
=
TestRenderObject
();
final
PipelineOwner
owner
=
PipelineOwner
();
owner
.
rootNode
=
renderObject
;
expect
(
manifold
.
requestVisualUpdateCount
,
0
);
owner
.
attach
(
manifold
);
expect
(
manifold
.
requestVisualUpdateCount
,
0
);
});
test
(
'attach does not request visual update if onNeedVisualUpdate is specified'
,
()
{
final
TestPipelineManifold
manifold
=
TestPipelineManifold
();
final
TestRenderObject
renderObject
=
TestRenderObject
();
int
onNeedVisualUpdateCount
=
0
;
final
PipelineOwner
owner
=
PipelineOwner
(
onNeedVisualUpdate:
()
{
onNeedVisualUpdateCount
++;
},
onSemanticsUpdate:
(
_
)
{
},
);
owner
.
ensureSemantics
();
owner
.
rootNode
=
renderObject
;
final
OffsetLayer
rootLayer
=
OffsetLayer
();
rootLayer
.
attach
(
renderObject
);
renderObject
.
scheduleInitialLayout
();
renderObject
.
scheduleInitialPaint
(
rootLayer
);
renderObject
.
scheduleInitialSemantics
();
renderObject
.
markNeedsSemanticsUpdate
();
onNeedVisualUpdateCount
=
0
;
expect
(
manifold
.
requestVisualUpdateCount
,
0
);
owner
.
attach
(
manifold
);
expect
(
manifold
.
requestVisualUpdateCount
,
0
);
expect
(
onNeedVisualUpdateCount
,
0
);
});
test
(
'attach requests visual update if onNeedVisualUpdate was not specified and nodes are dirty for layout'
,
()
{
final
TestPipelineManifold
manifold
=
TestPipelineManifold
();
final
TestRenderObject
renderObject
=
TestRenderObject
();
final
PipelineOwner
owner
=
PipelineOwner
();
owner
.
rootNode
=
renderObject
;
renderObject
.
scheduleInitialLayout
();
expect
(
manifold
.
requestVisualUpdateCount
,
0
);
owner
.
attach
(
manifold
);
expect
(
manifold
.
requestVisualUpdateCount
,
1
);
});
test
(
'attach requests visual update if onNeedVisualUpdate was not specified and nodes are dirty for compositing bits'
,
()
{
final
TestPipelineManifold
manifold
=
TestPipelineManifold
();
final
TestRenderObject
renderObject
=
TestRenderObject
();
final
PipelineOwner
owner
=
PipelineOwner
();
owner
.
rootNode
=
renderObject
;
renderObject
.
markNeedsCompositingBitsUpdate
();
expect
(
manifold
.
requestVisualUpdateCount
,
0
);
owner
.
attach
(
manifold
);
expect
(
manifold
.
requestVisualUpdateCount
,
1
);
});
test
(
'attach requests visual update if onNeedVisualUpdate was not specified and nodes are dirty for paint'
,
()
{
final
TestPipelineManifold
manifold
=
TestPipelineManifold
();
final
TestRenderObject
renderObject
=
TestRenderObject
();
final
PipelineOwner
owner
=
PipelineOwner
();
owner
.
rootNode
=
renderObject
;
final
OffsetLayer
rootLayer
=
OffsetLayer
();
rootLayer
.
attach
(
renderObject
);
renderObject
.
scheduleInitialLayout
();
renderObject
.
scheduleInitialPaint
(
rootLayer
);
owner
.
flushLayout
();
owner
.
flushPaint
();
renderObject
.
markNeedsPaint
();
expect
(
manifold
.
requestVisualUpdateCount
,
0
);
owner
.
attach
(
manifold
);
expect
(
manifold
.
requestVisualUpdateCount
,
1
);
});
test
(
'attach requests visual update if onNeedVisualUpdate was not specified and nodes are dirty for semantics'
,
()
{
final
TestPipelineManifold
manifold
=
TestPipelineManifold
();
final
TestRenderObject
renderObject
=
TestRenderObject
();
final
PipelineOwner
owner
=
PipelineOwner
(
onSemanticsUpdate:
(
_
)
{
},
);
final
SemanticsHandle
handle
=
owner
.
ensureSemantics
();
owner
.
rootNode
=
renderObject
;
renderObject
.
scheduleInitialLayout
();
renderObject
.
scheduleInitialSemantics
();
owner
.
flushLayout
();
owner
.
flushSemantics
();
renderObject
.
markNeedsSemanticsUpdate
();
expect
(
manifold
.
requestVisualUpdateCount
,
0
);
owner
.
attach
(
manifold
);
expect
(
manifold
.
requestVisualUpdateCount
,
1
);
handle
.
dispose
();
});
test
(
'cannot detach when not attached'
,
()
{
final
PipelineOwner
owner
=
PipelineOwner
();
expect
(()
=>
owner
.
detach
(),
throwsAssertionError
);
});
test
(
'cannot adopt twice'
,
()
{
final
PipelineOwner
root
=
PipelineOwner
();
final
PipelineOwner
child
=
PipelineOwner
();
root
.
adoptChild
(
child
);
expect
(()
=>
root
.
adoptChild
(
child
),
throwsAssertionError
);
});
test
(
'cannot adopt child of other parent'
,
()
{
final
PipelineOwner
root
=
PipelineOwner
();
final
PipelineOwner
child
=
PipelineOwner
();
final
PipelineOwner
otherRoot
=
PipelineOwner
();
root
.
adoptChild
(
child
);
expect
(()
=>
otherRoot
.
adoptChild
(
child
),
throwsAssertionError
);
});
test
(
'adopting creates semantics owner if necessary'
,
()
{
final
TestPipelineManifold
manifold
=
TestPipelineManifold
();
final
PipelineOwner
root
=
PipelineOwner
(
onSemanticsUpdate:
(
_
)
{
},
);
final
PipelineOwner
child
=
PipelineOwner
(
onSemanticsUpdate:
(
_
)
{
},
);
final
PipelineOwner
childOfChild
=
PipelineOwner
(
onSemanticsUpdate:
(
_
)
{
},
);
root
.
attach
(
manifold
);
expect
(
root
.
semanticsOwner
,
isNull
);
expect
(
child
.
semanticsOwner
,
isNull
);
expect
(
childOfChild
.
semanticsOwner
,
isNull
);
root
.
adoptChild
(
child
);
expect
(
root
.
semanticsOwner
,
isNull
);
expect
(
child
.
semanticsOwner
,
isNull
);
expect
(
childOfChild
.
semanticsOwner
,
isNull
);
manifold
.
semanticsEnabled
=
true
;
expect
(
root
.
semanticsOwner
,
isNotNull
);
expect
(
child
.
semanticsOwner
,
isNotNull
);
expect
(
childOfChild
.
semanticsOwner
,
isNull
);
child
.
adoptChild
(
childOfChild
);
expect
(
root
.
semanticsOwner
,
isNotNull
);
expect
(
child
.
semanticsOwner
,
isNotNull
);
expect
(
childOfChild
.
semanticsOwner
,
isNotNull
);
});
test
(
'cannot drop unattached child'
,
()
{
final
PipelineOwner
root
=
PipelineOwner
();
final
PipelineOwner
child
=
PipelineOwner
();
expect
(()
=>
root
.
dropChild
(
child
),
throwsAssertionError
);
});
test
(
'cannot drop child attached to other parent'
,
()
{
final
PipelineOwner
root
=
PipelineOwner
();
final
PipelineOwner
child
=
PipelineOwner
();
final
PipelineOwner
otherRoot
=
PipelineOwner
();
otherRoot
.
adoptChild
(
child
);
expect
(()
=>
root
.
dropChild
(
child
),
throwsAssertionError
);
});
test
(
'dropping destroys semantics owner if necessary'
,
()
{
final
TestPipelineManifold
manifold
=
TestPipelineManifold
()
..
semanticsEnabled
=
true
;
final
PipelineOwner
root
=
PipelineOwner
(
onSemanticsUpdate:
(
_
)
{
},
);
final
PipelineOwner
child
=
PipelineOwner
(
onSemanticsUpdate:
(
_
)
{
},
);
final
PipelineOwner
childOfChild
=
PipelineOwner
(
onSemanticsUpdate:
(
_
)
{
},
);
root
.
attach
(
manifold
);
root
.
adoptChild
(
child
);
child
.
adoptChild
(
childOfChild
);
expect
(
root
.
semanticsOwner
,
isNotNull
);
expect
(
child
.
semanticsOwner
,
isNotNull
);
expect
(
childOfChild
.
semanticsOwner
,
isNotNull
);
child
.
dropChild
(
childOfChild
);
expect
(
root
.
semanticsOwner
,
isNotNull
);
expect
(
child
.
semanticsOwner
,
isNotNull
);
expect
(
childOfChild
.
semanticsOwner
,
isNull
);
final
SemanticsHandle
childSemantics
=
child
.
ensureSemantics
();
root
.
dropChild
(
child
);
expect
(
root
.
semanticsOwner
,
isNotNull
);
expect
(
child
.
semanticsOwner
,
isNotNull
);
expect
(
childOfChild
.
semanticsOwner
,
isNull
);
childSemantics
.
dispose
();
expect
(
root
.
semanticsOwner
,
isNotNull
);
expect
(
child
.
semanticsOwner
,
isNull
);
expect
(
childOfChild
.
semanticsOwner
,
isNull
);
});
test
(
'can adopt/drop children during own layout'
,
()
{
final
TestPipelineManifold
manifold
=
TestPipelineManifold
();
final
PipelineOwner
root
=
PipelineOwner
();
final
PipelineOwner
child1
=
PipelineOwner
();
final
PipelineOwner
child2
=
PipelineOwner
();
final
TestRenderObject
rootRenderObject
=
TestRenderObject
(
onLayout:
()
{
child1
.
dropChild
(
child2
);
root
.
dropChild
(
child1
);
root
.
adoptChild
(
child2
);
child2
.
adoptChild
(
child1
);
},
);
root
.
rootNode
=
rootRenderObject
;
rootRenderObject
.
scheduleInitialLayout
();
root
.
adoptChild
(
child1
);
child1
.
adoptChild
(
child2
);
root
.
attach
(
manifold
);
expect
(
_treeWalk
(
root
),
<
PipelineOwner
>[
root
,
child1
,
child2
]);
root
.
flushLayout
();
expect
(
_treeWalk
(
root
),
<
PipelineOwner
>[
root
,
child2
,
child1
]);
});
test
(
'cannot adopt/drop children during child layout'
,
()
{
final
TestPipelineManifold
manifold
=
TestPipelineManifold
();
final
PipelineOwner
root
=
PipelineOwner
();
final
PipelineOwner
child1
=
PipelineOwner
();
final
PipelineOwner
child2
=
PipelineOwner
();
final
PipelineOwner
child3
=
PipelineOwner
();
Object
?
droppingError
;
Object
?
adoptingError
;
final
TestRenderObject
childRenderObject
=
TestRenderObject
(
onLayout:
()
{
child1
.
dropChild
(
child2
);
child1
.
adoptChild
(
child3
);
try
{
root
.
dropChild
(
child1
);
}
catch
(
e
)
{
droppingError
=
e
;
}
try
{
root
.
adoptChild
(
child2
);
}
catch
(
e
)
{
adoptingError
=
e
;
}
},
);
child1
.
rootNode
=
childRenderObject
;
childRenderObject
.
scheduleInitialLayout
();
root
.
adoptChild
(
child1
);
child1
.
adoptChild
(
child2
);
root
.
attach
(
manifold
);
expect
(
_treeWalk
(
root
),
<
PipelineOwner
>[
root
,
child1
,
child2
]);
root
.
flushLayout
();
expect
(
adoptingError
,
isAssertionError
.
having
((
AssertionError
e
)
=>
e
.
message
,
'message'
,
contains
(
'Cannot modify child list after layout.'
)));
expect
(
droppingError
,
isAssertionError
.
having
((
AssertionError
e
)
=>
e
.
message
,
'message'
,
contains
(
'Cannot modify child list after layout.'
)));
});
test
(
'visitChildren visits all children'
,
()
{
final
PipelineOwner
root
=
PipelineOwner
();
final
PipelineOwner
child1
=
PipelineOwner
();
final
PipelineOwner
child2
=
PipelineOwner
();
final
PipelineOwner
child3
=
PipelineOwner
();
final
PipelineOwner
childOfChild3
=
PipelineOwner
();
root
.
adoptChild
(
child1
);
root
.
adoptChild
(
child2
);
root
.
adoptChild
(
child3
);
child3
.
adoptChild
(
childOfChild3
);
final
List
<
PipelineOwner
>
children
=
<
PipelineOwner
>[];
root
.
visitChildren
((
PipelineOwner
child
)
{
children
.
add
(
child
);
});
expect
(
children
,
<
PipelineOwner
>[
child1
,
child2
,
child3
]);
children
.
clear
();
child3
.
visitChildren
((
PipelineOwner
child
)
{
children
.
add
(
child
);
});
expect
(
children
.
single
,
childOfChild3
);
});
}
class
TestPipelineManifold
extends
ChangeNotifier
implements
PipelineManifold
{
int
requestVisualUpdateCount
=
0
;
@override
void
requestVisualUpdate
()
{
requestVisualUpdateCount
++;
}
@override
bool
get
semanticsEnabled
=>
_semanticsEnabled
;
bool
_semanticsEnabled
=
false
;
set
semanticsEnabled
(
bool
value
)
{
if
(
value
==
_semanticsEnabled
)
{
return
;
}
_semanticsEnabled
=
value
;
notifyListeners
();
}
}
class
TestRenderObject
extends
RenderObject
{
TestRenderObject
({
this
.
onLayout
,
this
.
onPaint
,
this
.
onSemantics
});
final
VoidCallback
?
onLayout
;
final
VoidCallback
?
onPaint
;
final
VoidCallback
?
onSemantics
;
@override
bool
get
isRepaintBoundary
=>
true
;
@override
void
debugAssertDoesMeetConstraints
()
{
}
@override
Rect
get
paintBounds
=>
Rect
.
zero
;
@override
void
performLayout
()
{
onLayout
?.
call
();
}
@override
void
paint
(
PaintingContext
context
,
Offset
offset
)
{
onPaint
?.
call
();
}
@override
void
describeSemanticsConfiguration
(
SemanticsConfiguration
config
)
{
onSemantics
?.
call
();
}
@override
void
performResize
()
{
}
@override
Rect
get
semanticBounds
=>
Rect
.
zero
;
}
List
<
PipelineOwner
>
_treeWalk
(
PipelineOwner
root
)
{
final
List
<
PipelineOwner
>
results
=
<
PipelineOwner
>[
root
];
void
visitor
(
PipelineOwner
child
)
{
results
.
add
(
child
);
child
.
visitChildren
(
visitor
);
}
root
.
visitChildren
(
visitor
);
return
results
;
}
packages/flutter_test/lib/src/widget_tester.dart
View file @
670f9d20
...
...
@@ -1045,7 +1045,7 @@ class WidgetTester extends WidgetController implements HitTestDispatcher, Ticker
void
_verifySemanticsHandlesWereDisposed
()
{
assert
(
_lastRecordedSemanticsHandles
!=
null
);
// TODO(goderbauer): Fix known leak in web engine when running integration tests and remove this "correction", https://github.com/flutter/flutter/issues/121640.
final
int
knownWebEngineLeakForLiveTestsCorrection
=
kIsWeb
&&
binding
is
LiveTestWidgetsFlutterBinding
?
1
:
0
;
final
int
knownWebEngineLeakForLiveTestsCorrection
=
kIsWeb
&&
binding
is
LiveTestWidgetsFlutterBinding
?
2
:
0
;
if
(
_currentSemanticsHandles
-
knownWebEngineLeakForLiveTestsCorrection
>
_lastRecordedSemanticsHandles
!)
{
throw
FlutterError
.
fromParts
(<
DiagnosticsNode
>[
...
...
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