[platform_view]fix regression for addSubview when re-ordering (flutter/engine#40091)

[platform_view]fix regression for addSubview when re-ordering
This commit is contained in:
hellohuanlin
2023-03-07 15:56:00 -08:00
committed by GitHub
parent 5c19fa810c
commit 6e08fa0d29
2 changed files with 118 additions and 4 deletions

View File

@@ -750,17 +750,34 @@ void FlutterPlatformViewsController::BringLayersIntoView(LayersMap layer_map) {
UIView* flutter_view = flutter_view_.get();
// Clear the `active_composition_order_`, which will be populated down below.
active_composition_order_.clear();
NSMutableArray* desired_platform_subviews = [NSMutableArray array];
for (size_t i = 0; i < composition_order_.size(); i++) {
int64_t platform_view_id = composition_order_[i];
std::vector<std::shared_ptr<FlutterPlatformViewLayer>> layers = layer_map[platform_view_id];
UIView* platform_view_root = root_views_[platform_view_id].get();
// `addSubview` will automatically reorder subview if it is already added.
[flutter_view addSubview:platform_view_root];
[desired_platform_subviews addObject:platform_view_root];
for (const std::shared_ptr<FlutterPlatformViewLayer>& layer : layers) {
[flutter_view addSubview:layer->overlay_view_wrapper];
[desired_platform_subviews addObject:layer->overlay_view_wrapper];
}
active_composition_order_.push_back(platform_view_id);
}
NSSet* desired_platform_subviews_set = [NSSet setWithArray:desired_platform_subviews];
NSArray* existing_platform_subviews = [flutter_view.subviews
filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id object,
NSDictionary* bindings) {
return [desired_platform_subviews_set containsObject:object];
}]];
// Manipulate view hierarchy only if needed, to address a performance issue where
// `BringLayersIntoView` is called even when view hierarchy stays the same.
// See: https://github.com/flutter/flutter/issues/121833
// TODO(hellohuanlin): investigate if it is possible to skip unnecessary BringLayersIntoView.
if (![desired_platform_subviews isEqualToArray:existing_platform_subviews]) {
for (UIView* subview in desired_platform_subviews) {
// `addSubview` will automatically reorder subview if it is already added.
[flutter_view addSubview:subview];
}
}
}
std::shared_ptr<FlutterPlatformViewLayer> FlutterPlatformViewsController::GetLayer(

View File

@@ -2342,7 +2342,8 @@ fml::RefPtr<fml::TaskRunner> CreateNewThread(std::string name) {
XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 1UL);
}
- (void)testFlutterPlatformViewControllerSubmitFrameShouldOrderSubviewsCorrectly {
- (void)
testFlutterPlatformViewControllerSubmitFrameShouldOrderSubviewsCorrectlyWithDifferentViewHierarchy {
flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
@@ -2437,6 +2438,102 @@ fml::RefPtr<fml::TaskRunner> CreateNewThread(std::string name) {
@"The first clipping view should be added after the second clipping view.");
}
- (void)
testFlutterPlatformViewControllerSubmitFrameShouldOrderSubviewsCorrectlyWithSameViewHierarchy {
flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
/*platform=*/thread_task_runner,
/*raster=*/thread_task_runner,
/*ui=*/thread_task_runner,
/*io=*/thread_task_runner);
auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
/*delegate=*/mock_delegate,
/*rendering_api=*/flutter::IOSRenderingAPI::kSoftware,
/*platform_views_controller=*/flutterPlatformViewsController,
/*task_runners=*/runners);
UIView* mockFlutterView = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)] autorelease];
flutterPlatformViewsController->SetFlutterView(mockFlutterView);
FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
[[FlutterPlatformViewsTestMockFlutterPlatformFactory new] autorelease];
flutterPlatformViewsController->RegisterViewFactory(
factory, @"MockFlutterPlatformView",
FlutterPlatformViewGestureRecognizersBlockingPolicyEager);
FlutterResult result = ^(id result) {
};
flutterPlatformViewsController->OnMethodCall(
[FlutterMethodCall
methodCallWithMethodName:@"create"
arguments:@{@"id" : @0, @"viewType" : @"MockFlutterPlatformView"}],
result);
UIView* view1 = gMockPlatformView;
// This overwrites `gMockPlatformView` to another view.
flutterPlatformViewsController->OnMethodCall(
[FlutterMethodCall
methodCallWithMethodName:@"create"
arguments:@{@"id" : @1, @"viewType" : @"MockFlutterPlatformView"}],
result);
UIView* view2 = gMockPlatformView;
flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300));
flutter::MutatorsStack stack;
SkMatrix finalMatrix;
auto embeddedViewParams1 =
std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1));
flutterPlatformViewsController->CompositeEmbeddedView(0);
auto embeddedViewParams2 =
std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(500, 500), stack);
flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams2));
flutterPlatformViewsController->CompositeEmbeddedView(1);
// SKSurface is required if the root FlutterView is present.
const SkImageInfo image_info = SkImageInfo::MakeN32Premul(1000, 1000);
sk_sp<SkSurface> mock_sk_surface = SkSurface::MakeRaster(image_info);
flutter::SurfaceFrame::FramebufferInfo framebuffer_info;
auto mock_surface = std::make_unique<flutter::SurfaceFrame>(
std::move(mock_sk_surface), framebuffer_info,
[](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
/*frame_size=*/SkISize::Make(800, 600));
XCTAssertTrue(
flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface)));
// platform view is wrapped by touch interceptor, which itself is wrapped by clipping view.
UIView* clippingView1 = view1.superview.superview;
UIView* clippingView2 = view2.superview.superview;
UIView* flutterView = clippingView1.superview;
XCTAssertTrue([flutterView.subviews indexOfObject:clippingView1] <
[flutterView.subviews indexOfObject:clippingView2],
@"The first clipping view should be added before the second clipping view.");
// Need to recreate these params since they are `std::move`ed.
flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300));
// Process the second frame in the same order.
embeddedViewParams1 =
std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1));
flutterPlatformViewsController->CompositeEmbeddedView(0);
embeddedViewParams2 =
std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(500, 500), stack);
flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams2));
flutterPlatformViewsController->CompositeEmbeddedView(1);
mock_sk_surface = SkSurface::MakeRaster(image_info);
mock_surface = std::make_unique<flutter::SurfaceFrame>(
std::move(mock_sk_surface), framebuffer_info,
[](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
/*frame_size=*/SkISize::Make(800, 600));
XCTAssertTrue(
flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface)));
XCTAssertTrue([flutterView.subviews indexOfObject:clippingView1] <
[flutterView.subviews indexOfObject:clippingView2],
@"The first clipping view should be added before the second clipping view.");
}
- (void)testThreadMergeAtEndFrame {
flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
auto thread_task_runner_platform = CreateNewThread("FlutterPlatformViewsTest1");