Unverified Commit 48bb12df authored by Michael Goderbauer's avatar Michael Goderbauer Committed by GitHub

Make Element tree root generic (#123352)

Make Element tree root generic
parent 66e6acbe
......@@ -56,19 +56,19 @@ Future<void> main() async {
final Stopwatch watch = Stopwatch();
print('flutter_test allElements benchmark... (${WidgetsBinding.instance.renderViewElement})');
print('flutter_test allElements benchmark... (${WidgetsBinding.instance.rootElement})');
// Make sure we get enough elements to process for consistent benchmark runs
int elementCount = collectAllElementsFrom(WidgetsBinding.instance.renderViewElement!, skipOffstage: false).length;
int elementCount = collectAllElementsFrom(WidgetsBinding.instance.rootElement!, skipOffstage: false).length;
while (elementCount < 2458) {
await Future<void>.delayed(Duration.zero);
elementCount = collectAllElementsFrom(WidgetsBinding.instance.renderViewElement!, skipOffstage: false).length;
elementCount = collectAllElementsFrom(WidgetsBinding.instance.rootElement!, skipOffstage: false).length;
}
print('element count: $elementCount');
watch.start();
for (int i = 0; i < _kNumIters; i += 1) {
final List<Element> allElements = collectAllElementsFrom(
WidgetsBinding.instance.renderViewElement!,
WidgetsBinding.instance.rootElement!,
skipOffstage: false,
).toList();
allElements.clear();
......
......@@ -78,7 +78,7 @@ Future<void> smokeDemo(WidgetTester tester, GalleryDemo demo) async {
// Verify that the dumps are pretty.
final String routeName = demo.routeName;
verifyToStringOutput('debugDumpApp', routeName, WidgetsBinding.instance.renderViewElement!.toStringDeep());
verifyToStringOutput('debugDumpApp', routeName, WidgetsBinding.instance.rootElement!.toStringDeep());
verifyToStringOutput('debugDumpRenderTree', routeName, RendererBinding.instance.renderView.toStringDeep());
verifyToStringOutput('debugDumpLayerTree', routeName, RendererBinding.instance.renderView.debugLayer?.toStringDeep() ?? '');
......
......@@ -23,6 +23,17 @@
# * ListWheelScrollView: fix_list_wheel_scroll_view.yaml
version: 1
transforms:
# Changes made in https://github.com/flutter/flutter/pull/123352
- title: "Migrate to 'rootElement'"
date: 2023-03-13
element:
uris: [ 'widgets.dart', 'material.dart', 'cupertino.dart' ]
field: 'renderViewElement'
inClass: 'WidgetsBinding'
changes:
- kind: 'rename'
newName: 'rootElement'
# Changes made in https://github.com/flutter/flutter/pull/122555
- title: "Migrate to 'decorationClipBehavior'"
date: 2023-03-13
......
......@@ -479,8 +479,8 @@ mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureB
}
Future<void> _forceRebuild() {
if (renderViewElement != null) {
buildOwner!.reassemble(renderViewElement!, null);
if (rootElement != null) {
buildOwner!.reassemble(rootElement!, null);
return endOfFrame;
}
return Future<void>.value();
......@@ -889,8 +889,8 @@ mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureB
}
try {
if (renderViewElement != null) {
buildOwner!.buildScope(renderViewElement!);
if (rootElement != null) {
buildOwner!.buildScope(rootElement!);
}
super.drawFrame();
buildOwner!.finalizeTree();
......@@ -914,12 +914,20 @@ mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureB
}
}
/// The [Element] that is at the root of the hierarchy (and which wraps the
/// [RenderView] object at the root of the rendering hierarchy).
/// The [Element] that is at the root of the element tree hierarchy.
///
/// This is initialized the first time [runApp] is called.
Element? get renderViewElement => _renderViewElement;
Element? _renderViewElement;
Element? get rootElement => _rootElement;
Element? _rootElement;
/// Deprecated. Will be removed in a future version of Flutter.
///
/// Use [rootElement] instead.
@Deprecated(
'Use rootElement instead. '
'This feature was deprecated after v3.9.0-16.0.pre.'
)
Element? get renderViewElement => rootElement;
bool _readyToProduceFrames = false;
......@@ -951,7 +959,7 @@ mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureB
});
}
/// Takes a widget and attaches it to the [renderViewElement], creating it if
/// Takes a widget and attaches it to the [rootElement], creating it if
/// necessary.
///
/// This is called by [runApp] to configure the widget tree.
......@@ -961,23 +969,23 @@ mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureB
/// * [RenderObjectToWidgetAdapter.attachToRenderTree], which inflates a
/// widget and attaches it to the render tree.
void attachRootWidget(Widget rootWidget) {
final bool isBootstrapFrame = renderViewElement == null;
final bool isBootstrapFrame = rootElement == null;
_readyToProduceFrames = true;
_renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
_rootElement = RenderObjectToWidgetAdapter<RenderBox>(
container: renderView,
debugShortDescription: '[root]',
child: rootWidget,
).attachToRenderTree(buildOwner!, renderViewElement as RenderObjectToWidgetElement<RenderBox>?);
).attachToRenderTree(buildOwner!, rootElement as RenderObjectToWidgetElement<RenderBox>?);
if (isBootstrapFrame) {
SchedulerBinding.instance.ensureVisualUpdate();
}
}
/// Whether the [renderViewElement] has been initialized.
/// Whether the [rootElement] has been initialized.
///
/// This will be false until [runApp] is called (or [WidgetTester.pumpWidget]
/// is called in the context of a [TestWidgetsFlutterBinding]).
bool get isRootWidgetAttached => _renderViewElement != null;
bool get isRootWidgetAttached => _rootElement != null;
@override
Future<void> performReassemble() {
......@@ -986,8 +994,8 @@ mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureB
return true;
}());
if (renderViewElement != null) {
buildOwner!.reassemble(renderViewElement!, BindingBase.debugReassembleConfig);
if (rootElement != null) {
buildOwner!.reassemble(rootElement!, BindingBase.debugReassembleConfig);
}
return super.performReassemble();
}
......@@ -1069,8 +1077,8 @@ String _debugDumpAppString() {
const String mode = kDebugMode ? 'DEBUG MODE' : kReleaseMode ? 'RELEASE MODE' : 'PROFILE MODE';
final StringBuffer buffer = StringBuffer();
buffer.writeln('${WidgetsBinding.instance.runtimeType} - $mode');
if (WidgetsBinding.instance.renderViewElement != null) {
buffer.writeln(WidgetsBinding.instance.renderViewElement!.toStringDeep());
if (WidgetsBinding.instance.rootElement != null) {
buffer.writeln(WidgetsBinding.instance.rootElement!.toStringDeep());
} else {
buffer.writeln('<no tree currently mounted>');
}
......@@ -1148,7 +1156,7 @@ class RenderObjectToWidgetAdapter<T extends RenderObject> extends RenderObjectWi
String toStringShort() => debugShortDescription ?? super.toStringShort();
}
/// A [RootRenderObjectElement] that is hosted by a [RenderObject].
/// The root of the element tree that is hosted by a [RenderObject].
///
/// This element class is the instantiation of a [RenderObjectToWidgetAdapter]
/// widget. It can be used only as the root of an [Element] tree (it cannot be
......@@ -1158,7 +1166,7 @@ class RenderObjectToWidgetAdapter<T extends RenderObject> extends RenderObjectWi
/// whose container is the [RenderView] that connects to the Flutter engine. In
/// this usage, it is normally instantiated by the bootstrapping logic in the
/// [WidgetsFlutterBinding] singleton created by [runApp].
class RenderObjectToWidgetElement<T extends RenderObject> extends RootRenderObjectElement {
class RenderObjectToWidgetElement<T extends RenderObject> extends RenderObjectElement with RootElementMixin {
/// Creates an element that is hosted by a [RenderObject].
///
/// The [RenderObject] created by this element is not automatically set as a
......
......@@ -2499,7 +2499,7 @@ abstract class BuildContext {
/// Additional build owners can be built to manage off-screen widget trees.
///
/// To assign a build owner to a tree, use the
/// [RootRenderObjectElement.assignOwner] method on the root element of the
/// [RootElementMixin.assignOwner] method on the root element of the
/// widget tree.
///
/// {@tool dartpad}
......@@ -6323,14 +6323,29 @@ abstract class RenderObjectElement extends Element {
}
}
/// The element at the root of the tree.
/// Deprecated. Unused in the framework and will be removed in a future version
/// of Flutter.
///
/// Only root elements may have their owner set explicitly. All other
/// elements inherit their owner from their parent.
abstract class RootRenderObjectElement extends RenderObjectElement {
/// Classes that extend this class can extend [RenderObjectElement] and mixin
/// [RootElementMixin] instead.
@Deprecated(
'Use RootElementMixin instead. '
'This feature was deprecated after v3.9.0-16.0.pre.'
)
abstract class RootRenderObjectElement extends RenderObjectElement with RootElementMixin {
/// Initializes fields for subclasses.
@Deprecated(
'Use RootElementMixin instead. '
'This feature was deprecated after v3.9.0-16.0.pre.'
)
RootRenderObjectElement(super.widget);
}
/// Mixin for the element at the root of the tree.
///
/// Only root elements may have their owner set explicitly. All other
/// elements inherit their owner from their parent.
mixin RootElementMixin on Element {
/// Set the owner of the element. The owner will be propagated to all the
/// descendants of this element.
///
......
......@@ -914,8 +914,8 @@ mixin WidgetInspectorService {
@protected
Future<void> forceRebuild() {
final WidgetsBinding binding = WidgetsBinding.instance;
if (binding.renderViewElement != null) {
binding.buildOwner!.reassemble(binding.renderViewElement!, null);
if (binding.rootElement != null) {
binding.buildOwner!.reassemble(binding.rootElement!, null);
return binding.endOfFrame;
}
return Future<void>.value();
......@@ -1832,7 +1832,7 @@ mixin WidgetInspectorService {
}
Map<String, Object?>? _getRootWidget(String groupName) {
return _nodeToJson(WidgetsBinding.instance.renderViewElement?.toDiagnosticsNode(), InspectorSerializationDelegate(groupName: groupName, service: this));
return _nodeToJson(WidgetsBinding.instance.rootElement?.toDiagnosticsNode(), InspectorSerializationDelegate(groupName: groupName, service: this));
}
/// Returns a JSON representation of the [DiagnosticsNode] for the root
......@@ -1846,7 +1846,7 @@ mixin WidgetInspectorService {
Map<String, Object>? Function(DiagnosticsNode, InspectorSerializationDelegate)? addAdditionalPropertiesCallback,
}) {
return _nodeToJson(
WidgetsBinding.instance.renderViewElement?.toDiagnosticsNode(),
WidgetsBinding.instance.rootElement?.toDiagnosticsNode(),
InspectorSerializationDelegate(
groupName: groupName,
subtreeDepth: 1000000,
......
......@@ -17,34 +17,34 @@ void main() {
expect(Fizz.count, 0);
DebugReassembleConfig config = DebugReassembleConfig(widgetName: 'Bar');
WidgetsBinding.instance.buildOwner!.reassemble(WidgetsBinding.instance.renderViewElement!, config);
WidgetsBinding.instance.buildOwner!.reassemble(WidgetsBinding.instance.rootElement!, config);
expect(Foo.count, 0);
expect(Bar.count, 1);
expect(Fizz.count, 1);
config = DebugReassembleConfig(widgetName: 'Fizz');
WidgetsBinding.instance.buildOwner!.reassemble(WidgetsBinding.instance.renderViewElement!, config);
WidgetsBinding.instance.buildOwner!.reassemble(WidgetsBinding.instance.rootElement!, config);
expect(Foo.count, 0);
expect(Bar.count, 1);
expect(Fizz.count, 2);
config = DebugReassembleConfig(widgetName: 'NoMatch');
WidgetsBinding.instance.buildOwner!.reassemble(WidgetsBinding.instance.renderViewElement!, config);
WidgetsBinding.instance.buildOwner!.reassemble(WidgetsBinding.instance.rootElement!, config);
expect(Foo.count, 0);
expect(Bar.count, 1);
expect(Fizz.count, 2);
config = DebugReassembleConfig();
WidgetsBinding.instance.buildOwner!.reassemble(WidgetsBinding.instance.renderViewElement!, config);
WidgetsBinding.instance.buildOwner!.reassemble(WidgetsBinding.instance.rootElement!, config);
expect(Foo.count, 1);
expect(Bar.count, 2);
expect(Fizz.count, 3);
WidgetsBinding.instance.buildOwner!.reassemble(WidgetsBinding.instance.renderViewElement!, null);
WidgetsBinding.instance.buildOwner!.reassemble(WidgetsBinding.instance.rootElement!, null);
expect(Foo.count, 2);
expect(Bar.count, 3);
......
......@@ -58,7 +58,7 @@ class _TestLeafRenderObjectWidget extends LeafRenderObjectWidget {
}
}
class _TestElement extends RootRenderObjectElement{
class _TestElement extends RenderObjectElement with RootElementMixin {
_TestElement(): super(_TestLeafRenderObjectWidget());
void makeInactive() {
......
......@@ -50,9 +50,9 @@ void main() {
),
);
// Rendering tree is not built synchronously.
expect(WidgetsBinding.instance.renderViewElement, isNull);
expect(WidgetsBinding.instance.rootElement, isNull);
fakeAsync.flushTimers();
expect(WidgetsBinding.instance.renderViewElement, isNotNull);
expect(WidgetsBinding.instance.rootElement, isNotNull);
});
});
}
......@@ -873,7 +873,7 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService {
final List<Object?> chainElements = jsonList! as List<Object?>;
final List<Element> expectedChain = elementB.debugGetDiagnosticChain().reversed.toList();
// Sanity check that the chain goes back to the root.
expect(expectedChain.first, tester.binding.renderViewElement);
expect(expectedChain.first, tester.binding.rootElement);
expect(chainElements.length, equals(expectedChain.length));
for (int i = 0; i < expectedChain.length; i += 1) {
......@@ -2081,7 +2081,7 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService {
final List<Object?> chainElements = jsonList! as List<Object?>;
final List<Element> expectedChain = elementB.debugGetDiagnosticChain().reversed.toList();
// Sanity check that the chain goes back to the root.
expect(expectedChain.first, tester.binding.renderViewElement);
expect(expectedChain.first, tester.binding.rootElement);
expect(chainElements.length, equals(expectedChain.length));
for (int i = 0; i < expectedChain.length; i += 1) {
......@@ -2327,7 +2327,7 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService {
// directories so we get an empty tree other than the root that is always
// included.
final Object? rootWidget = service.toObject(rootJson['valueId']! as String);
expect(rootWidget, equals(WidgetsBinding.instance.renderViewElement));
expect(rootWidget, equals(WidgetsBinding.instance.rootElement));
List<Object?> childrenJson = rootJson['children']! as List<Object?>;
// There are no summary tree children.
expect(childrenJson.length, equals(0));
......
......@@ -106,8 +106,8 @@ class TestWidgetInspectorService extends Object with WidgetInspectorService {
rebuildCount++;
final WidgetsBinding binding = WidgetsBinding.instance;
if (binding.renderViewElement != null) {
binding.buildOwner!.reassemble(binding.renderViewElement!, null);
if (binding.rootElement != null) {
binding.buildOwner!.reassemble(binding.rootElement!, null);
}
}
......
......@@ -12,6 +12,9 @@ void main() {
Object object;
TickerProvider vsync;
// Changes made in https://github.com/flutter/flutter/pull/123352
WidgetsBinding.instance.renderViewElement;
// Changes made in https://github.com/flutter/flutter/pull/119647
MediaQueryData.fromWindow(View.of(context));
......
......@@ -12,6 +12,9 @@ void main() {
Object object;
TickerProvider vsync;
// Changes made in https://github.com/flutter/flutter/pull/123352
WidgetsBinding.instance.rootElement;
// Changes made in https://github.com/flutter/flutter/pull/119647
MediaQueryData.fromView(View.of(context));
......
......@@ -55,7 +55,7 @@ class _TestRenderObject extends RenderObject {
Rect get semanticBounds => throw UnimplementedError();
}
class _TestElement extends RootRenderObjectElement{
class _TestElement extends RenderObjectElement with RootElementMixin {
_TestElement(): super(_TestLeafRenderObjectWidget());
void makeInactive() {
......
......@@ -57,7 +57,7 @@ class MatchesGoldenFile extends AsyncMatcher {
final RenderObject renderObject = _findRepaintBoundary(element);
final Size size = renderObject.paintBounds.size;
final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.instance;
final Element e = binding.renderViewElement!;
final Element e = binding.rootElement!;
final ui.FlutterView view = binding.platformDispatcher.implicitView!;
// Unlike `flutter_tester`, we don't have the ability to render an element
......
......@@ -908,7 +908,7 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
// directly called again.
DiagnosticsNode treeDump;
try {
treeDump = renderViewElement?.toDiagnosticsNode() ?? DiagnosticsNode.message('<no tree>');
treeDump = rootElement?.toDiagnosticsNode() ?? DiagnosticsNode.message('<no tree>');
// We try to stringify the tree dump here (though we immediately discard the result) because
// we want to make sure that if it can't be serialised, we replace it with a message that
// says the tree could not be serialised. Otherwise, the real exception might get obscured
......@@ -1372,7 +1372,7 @@ class AutomatedTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
assert(inTest);
try {
debugBuildingDirtyElements = true;
buildOwner!.buildScope(renderViewElement!);
buildOwner!.buildScope(rootElement!);
if (_phase != EnginePhase.build) {
pipelineOwner.flushLayout();
if (_phase != EnginePhase.layout) {
......
......@@ -379,7 +379,7 @@ abstract class WidgetController {
/// using [Iterator.moveNext].
Iterable<Element> get allElements {
TestAsyncUtils.guardSync();
return collectAllElementsFrom(binding.renderViewElement!, skipOffstage: false);
return collectAllElementsFrom(binding.rootElement!, skipOffstage: false);
}
/// The matching element in the widget tree.
......
......@@ -493,7 +493,7 @@ abstract class Finder {
@protected
Iterable<Element> get allCandidates {
return collectAllElementsFrom(
WidgetsBinding.instance.renderViewElement!,
WidgetsBinding.instance.rootElement!,
skipOffstage: skipOffstage,
);
}
......
......@@ -750,7 +750,7 @@ class WidgetTester extends WidgetController implements HitTestDispatcher, Ticker
'your widget tree in a RootRestorationScope?',
);
return TestAsyncUtils.guard<void>(() async {
final Widget widget = ((binding.renderViewElement! as RenderObjectToWidgetElement<RenderObject>).widget as RenderObjectToWidgetAdapter<RenderObject>).child!;
final Widget widget = ((binding.rootElement! as RenderObjectToWidgetElement<RenderObject>).widget as RenderObjectToWidgetAdapter<RenderObject>).child!;
final TestRestorationData restorationData = binding.restorationManager.restorationData;
runApp(Container(key: UniqueKey()));
await pump();
......@@ -863,7 +863,7 @@ class WidgetTester extends WidgetController implements HitTestDispatcher, Ticker
.whereType<RenderObject>()
.first;
final Element? innerTargetElement = _lastWhereOrNull(
collectAllElementsFrom(binding.renderViewElement!, skipOffstage: true),
collectAllElementsFrom(binding.rootElement!, skipOffstage: true),
(Element element) => element.renderObject == innerTarget,
);
if (innerTargetElement == null) {
......
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