forked from firka/flutter
[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:
@@ -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(
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user