From 02556ae73b7b7b84cdd944ba6ac1d4e33c1d5a52 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Thu, 11 Jul 2024 12:17:45 -0700 Subject: [PATCH 01/49] ++ --- .../framework/Source/FlutterPlatformViews.mm | 52 ++++++++++--------- .../Source/FlutterPlatformViews_Internal.h | 4 ++ 2 files changed, 32 insertions(+), 24 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 551be716b15de..d8c91dc8359d9 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -79,11 +79,16 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, // Becomes NO if Apple's API changes and blurred backdrop filters cannot be applied. BOOL canApplyBlurBackdrop = YES; +bool FlutterPlatformViewLayerPool::HasLayer() const { + available_layer_index_ +} + std::shared_ptr FlutterPlatformViewLayerPool::GetLayer( GrDirectContext* gr_context, const std::shared_ptr& ios_context, MTLPixelFormat pixel_format) { if (available_layer_index_ >= layers_.size()) { + //FML_DCHECK([[NSThread] isMainThread]); std::shared_ptr layer; fml::scoped_nsobject overlay_view; fml::scoped_nsobject overlay_view_wrapper; @@ -135,6 +140,8 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, layers_.push_back(layer); } std::shared_ptr layer = layers_[available_layer_index_]; + // This condition can only happen with the Skia backend, which is due to be removed from + // iOS in short order. if (gr_context != layer->gr_context) { layer->gr_context = gr_context; // The overlay already exists, but the GrContext was changed so we need to recreate @@ -616,14 +623,6 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, } DlCanvas* FlutterPlatformViewsController::CompositeEmbeddedView(int64_t view_id) { - // Any UIKit related code has to run on main thread. - FML_DCHECK([[NSThread currentThread] isMainThread]); - // Do nothing if the view doesn't need to be composited. - if (views_to_recomposite_.count(view_id) == 0) { - return slices_[view_id]->canvas(); - } - CompositeWithParams(view_id, current_composition_params_[view_id]); - views_to_recomposite_.erase(view_id); return slices_[view_id]->canvas(); } @@ -668,8 +667,6 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, return frame->Submit(); } - DisposeViews(); - DlCanvas* background_canvas = frame->Canvas(); // Resolve all pending GPU operations before allocating a new surface. @@ -696,7 +693,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, // current platform view or any of the previous platform views. for (size_t j = i + 1; j > 0; j--) { int64_t current_platform_view_id = composition_order_[j - 1]; - SkRect platform_view_rect = GetPlatformViewRect(current_platform_view_id); + SkRect platform_view_rect = current_composition_params_[current_platform_view_id].finalBoundingRect(); std::vector intersection_rects = slice->region(platform_view_rect).getRects(); const SkIRect rounded_in_platform_view_rect = platform_view_rect.roundIn(); // Ignore intersections of single width/height on the edge of the platform view. @@ -768,6 +765,14 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, // Manually trigger the SkAutoCanvasRestore before we submit the frame save.Restore(); + // Dispose unused Flutter Views. + DisposeViews(); + + // Composite Platform Views. + for (auto view_id : views_to_recomposite_) { + CompositeWithParams(view_id, current_composition_params_[view_id]); + } + // If a layer was allocated in the previous frame, but it's not used in the current frame, // then it can be removed from the scene. RemoveUnusedLayers(); @@ -820,6 +825,16 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, } } + + +bool HasPlatformViewLayerAlready(GrDirectContext* gr_context, + const std::shared_ptr& ios_context, + MTLPixelFormat pixel_format) { + std::shared_ptr layer = + layer_pool_->GetLayer(gr_context, ios_context, pixel_format); + +} + std::shared_ptr FlutterPlatformViewsController::GetLayer( GrDirectContext* gr_context, const std::shared_ptr& ios_context, @@ -919,20 +934,9 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, views_to_dispose_ = std::move(views_to_delay_dispose); } -void FlutterPlatformViewsController::BeginCATransaction() { - FML_DCHECK([[NSThread currentThread] isMainThread]); - FML_DCHECK(!catransaction_added_); - [CATransaction begin]; - catransaction_added_ = true; -} +void FlutterPlatformViewsController::BeginCATransaction() { } -void FlutterPlatformViewsController::CommitCATransactionIfNeeded() { - if (catransaction_added_) { - FML_DCHECK([[NSThread currentThread] isMainThread]); - [CATransaction commit]; - catransaction_added_ = false; - } -} +void FlutterPlatformViewsController::CommitCATransactionIfNeeded() { } void FlutterPlatformViewsController::ResetFrameState() { slices_.clear(); diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index 6373e1feaa40c..4b1de1f693158 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -325,6 +325,10 @@ class FlutterPlatformViewsController { void CompositeWithParams(int64_t view_id, const EmbeddedViewParams& params); + bool HasPlatformViewLayerAlready(GrDirectContext* gr_context, + const std::shared_ptr& ios_context, + MTLPixelFormat pixel_format); + // Allocates a new FlutterPlatformViewLayer if needed, draws the pixels within the rect from // the picture on the layer's canvas. std::shared_ptr GetLayer(GrDirectContext* gr_context, From b3f6670fcf1aaf1e5cf75c9460257053851bcd8e Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Thu, 11 Jul 2024 13:54:53 -0700 Subject: [PATCH 02/49] move all state updates to end of compositing. --- .../framework/Source/FlutterOverlayView.mm | 1 - .../framework/Source/FlutterPlatformViews.mm | 264 ++++++++++-------- .../Source/FlutterPlatformViews_Internal.h | 36 ++- 3 files changed, 171 insertions(+), 130 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterOverlayView.mm b/shell/platform/darwin/ios/framework/Source/FlutterOverlayView.mm index 2e98396d603c0..76d81013dc90f 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterOverlayView.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterOverlayView.mm @@ -12,7 +12,6 @@ FLUTTER_ASSERT_ARC // This is mostly a duplication of FlutterView. -// TODO(amirh): once GL support is in evaluate if we can merge this with FlutterView. @implementation FlutterOverlayView { fml::CFRef _colorSpaceRef; } diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index d8c91dc8359d9..f9ee3cdda250e 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -79,78 +79,84 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, // Becomes NO if Apple's API changes and blurred backdrop filters cannot be applied. BOOL canApplyBlurBackdrop = YES; -bool FlutterPlatformViewLayerPool::HasLayer() const { - available_layer_index_ -} - std::shared_ptr FlutterPlatformViewLayerPool::GetLayer( GrDirectContext* gr_context, const std::shared_ptr& ios_context, - MTLPixelFormat pixel_format) { - if (available_layer_index_ >= layers_.size()) { - //FML_DCHECK([[NSThread] isMainThread]); - std::shared_ptr layer; - fml::scoped_nsobject overlay_view; - fml::scoped_nsobject overlay_view_wrapper; - - bool impeller_enabled = !!ios_context->GetImpellerContext(); - if (!gr_context && !impeller_enabled) { - overlay_view.reset([[FlutterOverlayView alloc] init]); - overlay_view_wrapper.reset([[FlutterOverlayView alloc] init]); - - auto ca_layer = fml::scoped_nsobject{[overlay_view.get() layer]}; - std::unique_ptr ios_surface = IOSSurface::Create(ios_context, ca_layer); - std::unique_ptr surface = ios_surface->CreateGPUSurface(); - - layer = std::make_shared( - std::move(overlay_view), std::move(overlay_view_wrapper), std::move(ios_surface), - std::move(surface)); - } else { - CGFloat screenScale = [UIScreen mainScreen].scale; - overlay_view.reset([[FlutterOverlayView alloc] initWithContentsScale:screenScale - pixelFormat:pixel_format]); - overlay_view_wrapper.reset([[FlutterOverlayView alloc] initWithContentsScale:screenScale - pixelFormat:pixel_format]); - - auto ca_layer = fml::scoped_nsobject{[overlay_view.get() layer]}; - std::unique_ptr ios_surface = IOSSurface::Create(ios_context, ca_layer); - std::unique_ptr surface = ios_surface->CreateGPUSurface(gr_context); - - layer = std::make_shared( - std::move(overlay_view), std::move(overlay_view_wrapper), std::move(ios_surface), - std::move(surface)); + MTLPixelFormat pixel_format, + bool create_if_missing) { + if (available_layer_index_ < layers_.size()) { + // TODO: Skia + std::shared_ptr layer = layers_[available_layer_index_]; + + // This condition can only happen with the Skia backend, which is due to be removed from + // iOS in short order. + if (gr_context != layer->gr_context) { layer->gr_context = gr_context; + // The overlay already exists, but the GrContext was changed so we need to recreate + // the rendering surface with the new GrContext. + IOSSurface* ios_surface = layer->ios_surface.get(); + std::unique_ptr surface = ios_surface->CreateGPUSurface(gr_context); + layer->surface = std::move(surface); } - // The overlay view wrapper masks the overlay view. - // This is required to keep the backing surface size unchanged between frames. - // - // Otherwise, changing the size of the overlay would require a new surface, - // which can be very expensive. - // - // This is the case of an animation in which the overlay size is changing in every frame. - // - // +------------------------+ - // | overlay_view | - // | +--------------+ | +--------------+ - // | | wrapper | | == mask => | overlay_view | - // | +--------------+ | +--------------+ - // +------------------------+ - layer->overlay_view_wrapper.get().clipsToBounds = YES; - [layer->overlay_view_wrapper.get() addSubview:layer->overlay_view]; - layers_.push_back(layer); + + available_layer_index_++; + return layer; } - std::shared_ptr layer = layers_[available_layer_index_]; - // This condition can only happen with the Skia backend, which is due to be removed from - // iOS in short order. - if (gr_context != layer->gr_context) { - layer->gr_context = gr_context; - // The overlay already exists, but the GrContext was changed so we need to recreate - // the rendering surface with the new GrContext. - IOSSurface* ios_surface = layer->ios_surface.get(); + + if (!create_if_missing) { + return nullptr; + } + + std::shared_ptr layer; + fml::scoped_nsobject overlay_view; + fml::scoped_nsobject overlay_view_wrapper; + + bool impeller_enabled = !!ios_context->GetImpellerContext(); + if (!gr_context && !impeller_enabled) { + overlay_view.reset([[FlutterOverlayView alloc] init]); + overlay_view_wrapper.reset([[FlutterOverlayView alloc] init]); + + auto ca_layer = fml::scoped_nsobject{[overlay_view.get() layer]}; + std::unique_ptr ios_surface = IOSSurface::Create(ios_context, ca_layer); + std::unique_ptr surface = ios_surface->CreateGPUSurface(); + + layer = std::make_shared(std::move(overlay_view), + std::move(overlay_view_wrapper), + std::move(ios_surface), std::move(surface)); + } else { + CGFloat screenScale = [UIScreen mainScreen].scale; + overlay_view.reset([[FlutterOverlayView alloc] initWithContentsScale:screenScale + pixelFormat:pixel_format]); + overlay_view_wrapper.reset([[FlutterOverlayView alloc] initWithContentsScale:screenScale + pixelFormat:pixel_format]); + + auto ca_layer = fml::scoped_nsobject{[overlay_view.get() layer]}; + std::unique_ptr ios_surface = IOSSurface::Create(ios_context, ca_layer); std::unique_ptr surface = ios_surface->CreateGPUSurface(gr_context); - layer->surface = std::move(surface); + + layer = std::make_shared(std::move(overlay_view), + std::move(overlay_view_wrapper), + std::move(ios_surface), std::move(surface)); + layer->gr_context = gr_context; } - available_layer_index_++; + // The overlay view wrapper masks the overlay view. + // This is required to keep the backing surface size unchanged between frames. + // + // Otherwise, changing the size of the overlay would require a new surface, + // which can be very expensive. + // + // This is the case of an animation in which the overlay size is changing in every frame. + // + // +------------------------+ + // | overlay_view | + // | +--------------+ | +--------------+ + // | | wrapper | | == mask => | overlay_view | + // | +--------------+ | +--------------+ + // +------------------------+ + layer->overlay_view_wrapper.get().clipsToBounds = YES; + [layer->overlay_view_wrapper.get() addSubview:layer->overlay_view]; + layers_.push_back(layer); + return layer; } @@ -682,8 +688,8 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, auto did_submit = true; auto num_platform_views = composition_order_.size(); - // TODO(hellohuanlin) this double for-loop is expensive with wasted computations. - // See: https://github.com/flutter/flutter/issues/145802 + size_t missing_layer_count = 0; + for (size_t i = 0; i < num_platform_views; i++) { int64_t platform_view_id = composition_order_[i]; EmbedderViewSlice* slice = slices_[platform_view_id].get(); @@ -693,7 +699,8 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, // current platform view or any of the previous platform views. for (size_t j = i + 1; j > 0; j--) { int64_t current_platform_view_id = composition_order_[j - 1]; - SkRect platform_view_rect = current_composition_params_[current_platform_view_id].finalBoundingRect(); + SkRect platform_view_rect = + current_composition_params_[current_platform_view_id].finalBoundingRect(); std::vector intersection_rects = slice->region(platform_view_rect).getRects(); const SkIRect rounded_in_platform_view_rect = platform_view_rect.roundIn(); // Ignore intersections of single width/height on the edge of the platform view. @@ -724,9 +731,6 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, // If the max number of allocations per platform view is exceeded, // then join all the rects into a single one. - // - // TODO(egarciad): Consider making this configurable. - // https://github.com/flutter/flutter/issues/52510 if (allocation_size > kMaxLayerAllocations) { SkIRect joined_rect = SkIRect::MakeEmpty(); for (const SkIRect& rect : intersection_rects) { @@ -746,14 +750,40 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, background_canvas->ClipRect(SkRect::Make(joined_rect), DlCanvas::ClipOp::kDifference); // Get a new host layer. std::shared_ptr layer = - GetLayer(gr_context, // - ios_context, // - slice, // - joined_rect, // - current_platform_view_id, // - overlay_id, // - ((FlutterView*)flutter_view_.get()).pixelFormat // + GetExistingLayer(gr_context, // + ios_context, // + MTLPixelFormatBGRA10_XR // ); + if (!layer) { + missing_layer_count++; + continue; + } + + layer->view_id = current_platform_view_id; + layer->overlay_id = overlay_id; + layer->rect = joined_rect; + + { + std::unique_ptr frame = layer->surface->AcquireFrame(frame_size_); + // If frame is null, AcquireFrame already printed out an error message. + if (!frame) { + continue; + } + DlCanvas* overlay_canvas = frame->Canvas(); + int restore_count = overlay_canvas->GetSaveCount(); + overlay_canvas->Save(); + overlay_canvas->ClipRect(SkRect::Make(joined_rect)); + // overlay_canvas->Clear(DlColor::kTransparent()); + slice->render_into(overlay_canvas); + overlay_canvas->RestoreToCount(restore_count); + + // This flutter view is never the last in a frame, since we always submit the + // underlay view last. + frame->set_submit_info({.frame_boundary = false}); + + layer->did_submit_last_frame = frame->Submit(); + } + did_submit &= layer->did_submit_last_frame; platform_view_layers[current_platform_view_id].push_back(layer); overlay_id++; @@ -773,6 +803,25 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, CompositeWithParams(view_id, current_composition_params_[view_id]); } + // Configure Flutter overlay views. + for (const auto& [key, layers] : platform_view_layers) { + for (const auto& layer : layers) { + layer->UpdateViewState(flutter_view_); + } + } + + // Create Missing Layers + for (auto i = 0u; i < missing_layer_count; i++) { + auto layer = GetOrCreateLayer(gr_context, // + ios_context, // + MTLPixelFormatBGRA10_XR // + ); + layer->did_submit_last_frame = true; + } + if (missing_layer_count > 0) { + FML_LOG(ERROR) << "Created " << missing_layer_count; + } + // If a layer was allocated in the previous frame, but it's not used in the current frame, // then it can be removed from the scene. RemoveUnusedLayers(); @@ -825,29 +874,31 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, } } - - -bool HasPlatformViewLayerAlready(GrDirectContext* gr_context, - const std::shared_ptr& ios_context, - MTLPixelFormat pixel_format) { +bool FlutterPlatformViewsController::HasPlatformViewLayerAlready( + GrDirectContext* gr_context, + const std::shared_ptr& ios_context, + MTLPixelFormat pixel_format) { std::shared_ptr layer = - layer_pool_->GetLayer(gr_context, ios_context, pixel_format); + layer_pool_->GetLayer(gr_context, ios_context, pixel_format, /*create_if_missing=*/false); + return !!layer; +} +std::shared_ptr FlutterPlatformViewsController::GetExistingLayer( + GrDirectContext* gr_context, + const std::shared_ptr& ios_context, + MTLPixelFormat pixel_format) { + return layer_pool_->GetLayer(gr_context, ios_context, pixel_format, /*create_if_missing=*/false); } -std::shared_ptr FlutterPlatformViewsController::GetLayer( +std::shared_ptr FlutterPlatformViewsController::GetOrCreateLayer( GrDirectContext* gr_context, const std::shared_ptr& ios_context, - EmbedderViewSlice* slice, - SkIRect rect, - int64_t view_id, - int64_t overlay_id, MTLPixelFormat pixel_format) { - FML_DCHECK(flutter_view_); - std::shared_ptr layer = - layer_pool_->GetLayer(gr_context, ios_context, pixel_format); + return layer_pool_->GetLayer(gr_context, ios_context, pixel_format, /*create_if_missing=*/true); +} - UIView* overlay_view_wrapper = layer->overlay_view_wrapper.get(); +void FlutterPlatformViewLayer::UpdateViewState(UIView* flutter_view) { + UIView* overlay_view_wrapper = this->overlay_view_wrapper.get(); auto screenScale = [UIScreen mainScreen].scale; // Set the size of the overlay view wrapper. // This wrapper view masks the overlay view. @@ -857,34 +908,13 @@ bool HasPlatformViewLayerAlready(GrDirectContext* gr_context, overlay_view_wrapper.accessibilityIdentifier = [NSString stringWithFormat:@"platform_view[%lld].overlay[%lld]", view_id, overlay_id]; - UIView* overlay_view = layer->overlay_view.get(); + UIView* overlay_view = this->overlay_view.get(); // Set the size of the overlay view. // This size is equal to the device screen size. - overlay_view.frame = [flutter_view_.get() convertRect:flutter_view_.get().bounds - toView:overlay_view_wrapper]; + overlay_view.frame = [flutter_view convertRect:flutter_view.bounds toView:overlay_view_wrapper]; // Set a unique view identifier, so the overlay_view can be identified in XCUITests. overlay_view.accessibilityIdentifier = [NSString stringWithFormat:@"platform_view[%lld].overlay_view[%lld]", view_id, overlay_id]; - - std::unique_ptr frame = layer->surface->AcquireFrame(frame_size_); - // If frame is null, AcquireFrame already printed out an error message. - if (!frame) { - return layer; - } - DlCanvas* overlay_canvas = frame->Canvas(); - int restore_count = overlay_canvas->GetSaveCount(); - overlay_canvas->Save(); - overlay_canvas->ClipRect(SkRect::Make(rect)); - overlay_canvas->Clear(DlColor::kTransparent()); - slice->render_into(overlay_canvas); - overlay_canvas->RestoreToCount(restore_count); - - // This flutter view is never the last in a frame, since we always submit the - // underlay view last. - frame->set_submit_info({.frame_boundary = false}); - - layer->did_submit_last_frame = frame->Submit(); - return layer; } void FlutterPlatformViewsController::RemoveUnusedLayers() { @@ -934,9 +964,9 @@ bool HasPlatformViewLayerAlready(GrDirectContext* gr_context, views_to_dispose_ = std::move(views_to_delay_dispose); } -void FlutterPlatformViewsController::BeginCATransaction() { } +void FlutterPlatformViewsController::BeginCATransaction() {} -void FlutterPlatformViewsController::CommitCATransactionIfNeeded() { } +void FlutterPlatformViewsController::CommitCATransactionIfNeeded() {} void FlutterPlatformViewsController::ResetFrameState() { slices_.clear(); diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index 4b1de1f693158..3f84587d515b1 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -6,6 +6,7 @@ #define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERPLATFORMVIEWS_INTERNAL_H_ #import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlatformViews.h" +#include "third_party/skia/include/core/SkRect.h" #include @@ -161,6 +162,12 @@ struct FlutterPlatformViewLayer { // We track this to know when the GrContext for the Flutter app has changed // so we can update the overlay with the new context. GrDirectContext* gr_context; + + SkIRect rect; + int64_t view_id; + int64_t overlay_id; + + void UpdateViewState(UIView* flutter_view); }; // This class isn't thread safe. @@ -170,11 +177,15 @@ class FlutterPlatformViewLayerPool { ~FlutterPlatformViewLayerPool() = default; - // Gets a layer from the pool if available, or allocates a new one. - // Finally, it marks the layer as used. That is, it increments `available_layer_index_`. + /// Gets a layer from the pool if available, or allocates a new one. + /// Finally, it marks the layer as used. That is, it increments `available_layer_index_`. + /// + /// If `create_if_missing` is false, nullptr will be returned if there is no existing + /// pooled layer. std::shared_ptr GetLayer(GrDirectContext* gr_context, const std::shared_ptr& ios_context, - MTLPixelFormat pixel_format); + MTLPixelFormat pixel_format, + bool create_if_missing); // Gets the layers in the pool that aren't currently used. // This method doesn't mark the layers as unused. @@ -329,15 +340,16 @@ class FlutterPlatformViewsController { const std::shared_ptr& ios_context, MTLPixelFormat pixel_format); - // Allocates a new FlutterPlatformViewLayer if needed, draws the pixels within the rect from - // the picture on the layer's canvas. - std::shared_ptr GetLayer(GrDirectContext* gr_context, - const std::shared_ptr& ios_context, - EmbedderViewSlice* slice, - SkIRect rect, - int64_t view_id, - int64_t overlay_id, - MTLPixelFormat pixel_format); + std::shared_ptr GetExistingLayer( + GrDirectContext* gr_context, + const std::shared_ptr& ios_context, + MTLPixelFormat pixel_format); + + std::shared_ptr GetOrCreateLayer( + GrDirectContext* gr_context, + const std::shared_ptr& ios_context, + MTLPixelFormat pixel_format); + // Removes overlay views and platform views that aren't needed in the current frame. // Must run on the platform thread. void RemoveUnusedLayers(); From b31aae6f8042e25cf30f21408eece3aa8327bf2d Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Thu, 11 Jul 2024 15:35:24 -0700 Subject: [PATCH 03/49] working but unsynchronized. --- flow/embedded_views.cc | 3 +- flow/embedded_views.h | 4 +- shell/common/rasterizer.cc | 5 +- .../external_view_embedder.h | 4 +- .../framework/Source/FlutterPlatformViews.mm | 129 +- .../Source/FlutterPlatformViewsTest.mm | 6634 ++++++++--------- .../Source/FlutterPlatformViews_Internal.h | 3 +- .../darwin/ios/ios_external_view_embedder.h | 3 +- .../darwin/ios/ios_external_view_embedder.mm | 5 +- .../embedder_external_view_embedder.cc | 3 +- .../embedder_external_view_embedder.h | 4 +- 11 files changed, 3396 insertions(+), 3401 deletions(-) diff --git a/flow/embedded_views.cc b/flow/embedded_views.cc index b0d17870a707b..80738886af18c 100644 --- a/flow/embedded_views.cc +++ b/flow/embedded_views.cc @@ -48,7 +48,8 @@ void ExternalViewEmbedder::SubmitFlutterView( int64_t flutter_view_id, GrDirectContext* context, const std::shared_ptr& aiks_context, - std::unique_ptr frame) { + std::unique_ptr frame, + fml::RefPtr platform_task_runner) { frame->Submit(); } diff --git a/flow/embedded_views.h b/flow/embedded_views.h index 22b2c1e2db967..d3de61c278d7a 100644 --- a/flow/embedded_views.h +++ b/flow/embedded_views.h @@ -14,6 +14,7 @@ #include "flutter/flow/surface_frame.h" #include "flutter/fml/memory/ref_counted.h" #include "flutter/fml/raster_thread_merger.h" +#include "fml/task_runner.h" #include "third_party/skia/include/core/SkMatrix.h" #include "third_party/skia/include/core/SkPath.h" #include "third_party/skia/include/core/SkRRect.h" @@ -459,7 +460,8 @@ class ExternalViewEmbedder { int64_t flutter_view_id, GrDirectContext* context, const std::shared_ptr& aiks_context, - std::unique_ptr frame); + std::unique_ptr frame, + fml::RefPtr platform_task_runner); // This method provides the embedder a way to do additional tasks after // |SubmitFrame|. For example, merge task runners if `should_resubmit_frame` diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index 00964a4ac18b7..dbf904f61794c 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -787,12 +787,11 @@ DrawSurfaceStatus Rasterizer::DrawToSurfaceUnsafe( frame->set_submit_info(submit_info); - if (external_view_embedder_ && - (!raster_thread_merger_ || raster_thread_merger_->IsMerged())) { + if (external_view_embedder_) { FML_DCHECK(!frame->IsSubmitted()); external_view_embedder_->SubmitFlutterView( view_id, surface_->GetContext(), surface_->GetAiksContext(), - std::move(frame)); + std::move(frame), delegate_.GetTaskRunners().GetPlatformTaskRunner()); } else { frame->Submit(); } diff --git a/shell/platform/android/external_view_embedder/external_view_embedder.h b/shell/platform/android/external_view_embedder/external_view_embedder.h index ab00870276ffc..b8c585450fca2 100644 --- a/shell/platform/android/external_view_embedder/external_view_embedder.h +++ b/shell/platform/android/external_view_embedder/external_view_embedder.h @@ -13,6 +13,7 @@ #include "flutter/shell/platform/android/external_view_embedder/surface_pool.h" #include "flutter/shell/platform/android/jni/platform_view_android_jni.h" #include "flutter/shell/platform/android/surface/android_surface.h" +#include "fml/memory/ref_ptr.h" namespace flutter { @@ -47,7 +48,8 @@ class AndroidExternalViewEmbedder final : public ExternalViewEmbedder { int64_t flutter_view_id, GrDirectContext* context, const std::shared_ptr& aiks_context, - std::unique_ptr frame) override; + std::unique_ptr frame, + fml::RefPtr platform_task_runner) override; // |ExternalViewEmbedder| PostPrerollResult PostPrerollAction( diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index f9ee3cdda250e..a1a931939d4f1 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -3,9 +3,11 @@ // found in the LICENSE file. #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h" +#include "fml/logging.h" #include +#include "flutter/fml/make_copyable.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterOverlayView.h" #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterView.h" @@ -344,39 +346,12 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, PostPrerollResult FlutterPlatformViewsController::PostPrerollAction( const fml::RefPtr& raster_thread_merger) { - // TODO(cyanglaz): https://github.com/flutter/flutter/issues/56474 - // Rename `has_platform_view` to `view_mutated` when the above issue is resolved. - if (!HasPlatformViewThisOrNextFrame()) { - return PostPrerollResult::kSuccess; - } - if (!raster_thread_merger->IsMerged()) { - // The raster thread merger may be disabled if the rasterizer is being - // created or teared down. - // - // In such cases, the current frame is dropped, and a new frame is attempted - // with the same layer tree. - // - // Eventually, the frame is submitted once this method returns `kSuccess`. - // At that point, the raster tasks are handled on the platform thread. - CancelFrame(); - return PostPrerollResult::kSkipAndRetryFrame; - } - // If the post preroll action is successful, we will display platform views in the current frame. - // In order to sync the rendering of the platform views (quartz) with skia's rendering, - // We need to begin an explicit CATransaction. This transaction needs to be submitted - // after the current frame is submitted. - BeginCATransaction(); - raster_thread_merger->ExtendLeaseTo(kDefaultMergedLeaseDuration); return PostPrerollResult::kSuccess; } void FlutterPlatformViewsController::EndFrame( bool should_resubmit_frame, - const fml::RefPtr& raster_thread_merger) { - if (should_resubmit_frame) { - raster_thread_merger->MergeWithLease(kDefaultMergedLeaseDuration); - } -} + const fml::RefPtr& raster_thread_merger) {} void FlutterPlatformViewsController::PushFilterToVisitedPlatformViews( const std::shared_ptr& filter, @@ -449,6 +424,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, } void FlutterPlatformViewsController::ClipViewSetMaskView(UIView* clipView) { + FML_CHECK([[NSThread currentThread] isMainThread]); if (clipView.maskView) { return; } @@ -464,6 +440,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, void FlutterPlatformViewsController::ApplyMutators(const MutatorsStack& mutators_stack, UIView* embedded_view, const SkRect& bounding_rect) { + FML_CHECK([[NSThread currentThread] isMainThread]); if (flutter_view_ == nullptr) { return; } @@ -591,6 +568,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, // included in the `views_to_recomposite_`. void FlutterPlatformViewsController::CompositeWithParams(int64_t view_id, const EmbeddedViewParams& params) { + FML_CHECK([[NSThread currentThread] isMainThread]); CGRect frame = CGRectMake(0, 0, params.sizePoints().width(), params.sizePoints().height()); FlutterTouchInterceptingView* touchInterceptor = touch_interceptors_[view_id].get(); #if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG @@ -633,6 +611,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, } void FlutterPlatformViewsController::Reset() { + FML_CHECK([[NSThread currentThread] isMainThread]); for (int64_t view_id : active_composition_order_) { UIView* sub_view = root_views_[view_id].get(); [sub_view removeFromSuperview]; @@ -651,6 +630,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, } SkRect FlutterPlatformViewsController::GetPlatformViewRect(int64_t view_id) { + FML_CHECK([[NSThread currentThread] isMainThread]); UIView* platform_view = GetPlatformViewByID(view_id); UIScreen* screen = [UIScreen mainScreen]; CGRect platform_view_cgrect = [platform_view convertRect:platform_view.bounds @@ -662,13 +642,13 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, ); } -bool FlutterPlatformViewsController::SubmitFrame(GrDirectContext* gr_context, - const std::shared_ptr& ios_context, - std::unique_ptr frame) { +bool FlutterPlatformViewsController::SubmitFrame( + GrDirectContext* gr_context, + const std::shared_ptr& ios_context, + std::unique_ptr frame, + fml::RefPtr platform_task_runner) { TRACE_EVENT0("flutter", "FlutterPlatformViewsController::SubmitFrame"); - // Any UIKit related code has to run on main thread. - FML_DCHECK([[NSThread currentThread] isMainThread]); if (flutter_view_ == nullptr) { return frame->Submit(); } @@ -795,48 +775,52 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, // Manually trigger the SkAutoCanvasRestore before we submit the frame save.Restore(); - // Dispose unused Flutter Views. - DisposeViews(); - - // Composite Platform Views. - for (auto view_id : views_to_recomposite_) { - CompositeWithParams(view_id, current_composition_params_[view_id]); - } - - // Configure Flutter overlay views. - for (const auto& [key, layers] : platform_view_layers) { - for (const auto& layer : layers) { - layer->UpdateViewState(flutter_view_); - } - } + // TODO + did_submit &= frame->Submit(); - // Create Missing Layers - for (auto i = 0u; i < missing_layer_count; i++) { - auto layer = GetOrCreateLayer(gr_context, // - ios_context, // - MTLPixelFormatBGRA10_XR // - ); - layer->did_submit_last_frame = true; - } - if (missing_layer_count > 0) { - FML_LOG(ERROR) << "Created " << missing_layer_count; - } + platform_task_runner->PostTask( + fml::MakeCopyable([&, platform_view_layers = std::move(platform_view_layers), + missing_layer_count, frame = std::move(frame), + current_composition_params = current_composition_params_, + views_to_recomposite = views_to_recomposite_]() mutable { + // Dispose unused Flutter Views. + DisposeViews(); + + // Composite Platform Views. + for (auto view_id : views_to_recomposite) { + CompositeWithParams(view_id, current_composition_params[view_id]); + } - // If a layer was allocated in the previous frame, but it's not used in the current frame, - // then it can be removed from the scene. - RemoveUnusedLayers(); - // Organize the layers by their z indexes. - BringLayersIntoView(platform_view_layers); - // Mark all layers as available, so they can be used in the next frame. - layer_pool_->RecycleLayers(); + // Configure Flutter overlay views. + for (const auto& [key, layers] : platform_view_layers) { + for (const auto& layer : layers) { + layer->UpdateViewState(flutter_view_); + } + } - did_submit &= frame->Submit(); + // Create Missing Layers + for (auto i = 0u; i < missing_layer_count; i++) { + auto layer = GetOrCreateLayer(gr_context, // + ios_context, // + MTLPixelFormatBGRA10_XR // + ); + layer->did_submit_last_frame = true; + } - // If the frame is submitted with embedded platform views, - // there should be a |[CATransaction begin]| call in this frame prior to all the drawing. - // If that case, we need to commit the transaction. - CommitCATransactionIfNeeded(); - return did_submit; + // If a layer was allocated in the previous frame, but it's not used in the current frame, + // then it can be removed from the scene. + RemoveUnusedLayers(); + // Organize the layers by their z indexes. + BringLayersIntoView(platform_view_layers); + // Mark all layers as available, so they can be used in the next frame. + layer_pool_->RecycleLayers(); + + // If the frame is submitted with embedded platform views, + // there should be a |[CATransaction begin]| call in this frame prior to all the drawing. + // If that case, we need to commit the transaction. + CommitCATransactionIfNeeded(); + })); + return true; } void FlutterPlatformViewsController::BringLayersIntoView(LayersMap layer_map) { @@ -941,7 +925,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, return; } - FML_DCHECK([[NSThread currentThread] isMainThread]); + FML_CHECK([[NSThread currentThread] isMainThread]); std::unordered_set views_to_composite(composition_order_.begin(), composition_order_.end()); @@ -969,6 +953,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, void FlutterPlatformViewsController::CommitCATransactionIfNeeded() {} void FlutterPlatformViewsController::ResetFrameState() { + // TODO: move this state when posting task to platform loop slices_.clear(); composition_order_.clear(); visited_platform_views_.clear(); diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm index 8a65d7c1cff16..da17bdc881a40 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm @@ -1,3317 +1,3317 @@ -// Copyright 2013 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 -#import -#import - -#import "flutter/fml/thread.h" -#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h" -#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlatformViews.h" -#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h" -#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h" -#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterTouchInterceptingView_Test.h" -#import "flutter/shell/platform/darwin/ios/platform_view_ios.h" - -FLUTTER_ASSERT_ARC - -@class FlutterPlatformViewsTestMockPlatformView; -__weak static FlutterPlatformViewsTestMockPlatformView* gMockPlatformView = nil; -const float kFloatCompareEpsilon = 0.001; - -@interface FlutterPlatformViewsTestMockPlatformView : UIView -@end -@implementation FlutterPlatformViewsTestMockPlatformView - -- (instancetype)init { - self = [super init]; - if (self) { - gMockPlatformView = self; - } - return self; -} - -- (void)dealloc { - gMockPlatformView = nil; -} - -@end - -@interface FlutterPlatformViewsTestMockFlutterPlatformView : NSObject -@property(nonatomic, strong) UIView* view; -@property(nonatomic, assign) BOOL viewCreated; -@end - -@implementation FlutterPlatformViewsTestMockFlutterPlatformView - -- (instancetype)init { - if (self = [super init]) { - _view = [[FlutterPlatformViewsTestMockPlatformView alloc] init]; - _viewCreated = NO; - } - return self; -} - -- (UIView*)view { - [self checkViewCreatedOnce]; - return _view; -} - -- (void)checkViewCreatedOnce { - if (self.viewCreated) { - abort(); - } - self.viewCreated = YES; -} - -@end - -@interface FlutterPlatformViewsTestMockFlutterPlatformFactory - : NSObject -@end - -@implementation FlutterPlatformViewsTestMockFlutterPlatformFactory -- (NSObject*)createWithFrame:(CGRect)frame - viewIdentifier:(int64_t)viewId - arguments:(id _Nullable)args { - return [[FlutterPlatformViewsTestMockFlutterPlatformView alloc] init]; -} - -@end - -namespace flutter { -namespace { -class FlutterPlatformViewsTestMockPlatformViewDelegate : public PlatformView::Delegate { - public: - void OnPlatformViewCreated(std::unique_ptr surface) override {} - void OnPlatformViewDestroyed() override {} - void OnPlatformViewScheduleFrame() override {} - void OnPlatformViewAddView(int64_t view_id, - const ViewportMetrics& viewport_metrics, - AddViewCallback callback) override {} - void OnPlatformViewRemoveView(int64_t view_id, RemoveViewCallback callback) override {} - void OnPlatformViewSetNextFrameCallback(const fml::closure& closure) override {} - void OnPlatformViewSetViewportMetrics(int64_t view_id, const ViewportMetrics& metrics) override {} - const flutter::Settings& OnPlatformViewGetSettings() const override { return settings_; } - void OnPlatformViewDispatchPlatformMessage(std::unique_ptr message) override {} - void OnPlatformViewDispatchPointerDataPacket(std::unique_ptr packet) override { - } - void OnPlatformViewDispatchSemanticsAction(int32_t id, - SemanticsAction action, - fml::MallocMapping args) override {} - void OnPlatformViewSetSemanticsEnabled(bool enabled) override {} - void OnPlatformViewSetAccessibilityFeatures(int32_t flags) override {} - void OnPlatformViewRegisterTexture(std::shared_ptr texture) override {} - void OnPlatformViewUnregisterTexture(int64_t texture_id) override {} - void OnPlatformViewMarkTextureFrameAvailable(int64_t texture_id) override {} - - void LoadDartDeferredLibrary(intptr_t loading_unit_id, - std::unique_ptr snapshot_data, - std::unique_ptr snapshot_instructions) override { - } - void LoadDartDeferredLibraryError(intptr_t loading_unit_id, - const std::string error_message, - bool transient) override {} - void UpdateAssetResolverByType(std::unique_ptr updated_asset_resolver, - flutter::AssetResolver::AssetResolverType type) override {} - - flutter::Settings settings_; -}; - -} // namespace -} // namespace flutter - -namespace { -fml::RefPtr CreateNewThread(const std::string& name) { - auto thread = std::make_unique(name); - auto runner = thread->GetTaskRunner(); - return runner; -} -} // namespace - -@interface FlutterPlatformViewsTest : XCTestCase -@end - -@implementation FlutterPlatformViewsTest - -- (void)testFlutterViewOnlyCreateOnceInOneFrame { - 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(); - auto platform_view = std::make_unique( - /*delegate=*/mock_delegate, - /*rendering_api=*/mock_delegate.settings_.enable_impeller - ? flutter::IOSRenderingAPI::kMetal - : flutter::IOSRenderingAPI::kSoftware, - /*platform_views_controller=*/flutterPlatformViewsController, - /*task_runners=*/runners, - /*worker_task_runner=*/nil, - /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - - FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = - [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; - flutterPlatformViewsController->RegisterViewFactory( - factory, @"MockFlutterPlatformView", - FlutterPlatformViewGestureRecognizersBlockingPolicyEager); - FlutterResult result = ^(id result) { - }; - flutterPlatformViewsController->OnMethodCall( - [FlutterMethodCall - methodCallWithMethodName:@"create" - arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], - result); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); - // Create embedded view params - flutter::MutatorsStack stack; - // Layer tree always pushes a screen scale factor to the stack - SkMatrix screenScaleMatrix = - SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); - stack.PushTransform(screenScaleMatrix); - // Push a translate matrix - SkMatrix translateMatrix = SkMatrix::Translate(100, 100); - stack.PushTransform(translateMatrix); - SkMatrix finalMatrix; - finalMatrix.setConcat(screenScaleMatrix, translateMatrix); - - auto embeddedViewParams = - std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); - - flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); - - flutterPlatformViewsController->GetPlatformViewRect(2); - - XCTAssertNotNil(gMockPlatformView); - - flutterPlatformViewsController->Reset(); -} - -- (void)testCanCreatePlatformViewWithoutFlutterView { - 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(); - auto platform_view = std::make_unique( - /*delegate=*/mock_delegate, - /*rendering_api=*/mock_delegate.settings_.enable_impeller - ? flutter::IOSRenderingAPI::kMetal - : flutter::IOSRenderingAPI::kSoftware, - /*platform_views_controller=*/flutterPlatformViewsController, - /*task_runners=*/runners, - /*worker_task_runner=*/nil, - /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - - FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = - [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; - flutterPlatformViewsController->RegisterViewFactory( - factory, @"MockFlutterPlatformView", - FlutterPlatformViewGestureRecognizersBlockingPolicyEager); - FlutterResult result = ^(id result) { - }; - flutterPlatformViewsController->OnMethodCall( - [FlutterMethodCall - methodCallWithMethodName:@"create" - arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], - result); - - XCTAssertNotNil(gMockPlatformView); -} - -- (void)testChildClippingViewHitTests { - ChildClippingView* childClippingView = - [[ChildClippingView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; - UIView* childView = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)]; - [childClippingView addSubview:childView]; - - XCTAssertFalse([childClippingView pointInside:CGPointMake(50, 50) withEvent:nil]); - XCTAssertFalse([childClippingView pointInside:CGPointMake(99, 100) withEvent:nil]); - XCTAssertFalse([childClippingView pointInside:CGPointMake(100, 99) withEvent:nil]); - XCTAssertFalse([childClippingView pointInside:CGPointMake(201, 200) withEvent:nil]); - XCTAssertFalse([childClippingView pointInside:CGPointMake(200, 201) withEvent:nil]); - XCTAssertFalse([childClippingView pointInside:CGPointMake(99, 200) withEvent:nil]); - XCTAssertFalse([childClippingView pointInside:CGPointMake(200, 299) withEvent:nil]); - - XCTAssertTrue([childClippingView pointInside:CGPointMake(150, 150) withEvent:nil]); - XCTAssertTrue([childClippingView pointInside:CGPointMake(100, 100) withEvent:nil]); - XCTAssertTrue([childClippingView pointInside:CGPointMake(199, 100) withEvent:nil]); - XCTAssertTrue([childClippingView pointInside:CGPointMake(100, 199) withEvent:nil]); - XCTAssertTrue([childClippingView pointInside:CGPointMake(199, 199) withEvent:nil]); -} - -- (void)testReleasesBackdropFilterSubviewsOnChildClippingViewDealloc { - __weak NSMutableArray* weakBackdropFilterSubviews = nil; - __weak UIVisualEffectView* weakVisualEffectView1 = nil; - __weak UIVisualEffectView* weakVisualEffectView2 = nil; - - @autoreleasepool { - ChildClippingView* clippingView = [[ChildClippingView alloc] initWithFrame:CGRectZero]; - UIVisualEffectView* visualEffectView1 = [[UIVisualEffectView alloc] - initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]]; - weakVisualEffectView1 = visualEffectView1; - PlatformViewFilter* platformViewFilter1 = - [[PlatformViewFilter alloc] initWithFrame:CGRectMake(0, 0, 10, 10) - blurRadius:5 - visualEffectView:visualEffectView1]; - - [clippingView applyBlurBackdropFilters:@[ platformViewFilter1 ]]; - - // Replace the blur filter to validate the original and new UIVisualEffectView are released. - UIVisualEffectView* visualEffectView2 = [[UIVisualEffectView alloc] - initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleDark]]; - weakVisualEffectView2 = visualEffectView2; - PlatformViewFilter* platformViewFilter2 = - [[PlatformViewFilter alloc] initWithFrame:CGRectMake(0, 0, 10, 10) - blurRadius:5 - visualEffectView:visualEffectView2]; - [clippingView applyBlurBackdropFilters:@[ platformViewFilter2 ]]; - - weakBackdropFilterSubviews = clippingView.backdropFilterSubviews; - XCTAssertNotNil(weakBackdropFilterSubviews); - clippingView = nil; - } - XCTAssertNil(weakBackdropFilterSubviews); - XCTAssertNil(weakVisualEffectView1); - XCTAssertNil(weakVisualEffectView2); -} - -- (void)testApplyBackdropFilter { - 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(); - auto platform_view = std::make_unique( - /*delegate=*/mock_delegate, - /*rendering_api=*/mock_delegate.settings_.enable_impeller - ? flutter::IOSRenderingAPI::kMetal - : flutter::IOSRenderingAPI::kSoftware, - /*platform_views_controller=*/flutterPlatformViewsController, - /*task_runners=*/runners, - /*worker_task_runner=*/nil, - /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - - FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = - [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; - flutterPlatformViewsController->RegisterViewFactory( - factory, @"MockFlutterPlatformView", - FlutterPlatformViewGestureRecognizersBlockingPolicyEager); - FlutterResult result = ^(id result) { - }; - flutterPlatformViewsController->OnMethodCall( - [FlutterMethodCall - methodCallWithMethodName:@"create" - arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], - result); - - XCTAssertNotNil(gMockPlatformView); - - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); - // Create embedded view params - flutter::MutatorsStack stack; - // Layer tree always pushes a screen scale factor to the stack - CGFloat screenScale = [UIScreen mainScreen].scale; - SkMatrix screenScaleMatrix = SkMatrix::Scale(screenScale, screenScale); - stack.PushTransform(screenScaleMatrix); - // Push a backdrop filter - auto filter = std::make_shared(5, 2, flutter::DlTileMode::kClamp); - stack.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); - - auto embeddedViewParams = - std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); - - flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); - XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]); - ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; - [mockFlutterView addSubview:childClippingView]; - - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; - - // childClippingView has visual effect view with the correct configurations. - NSUInteger numberOfExpectedVisualEffectView = 0; - for (UIView* subview in childClippingView.subviews) { - if (![subview isKindOfClass:[UIVisualEffectView class]]) { - continue; - } - XCTAssertLessThan(numberOfExpectedVisualEffectView, 1u); - if ([self validateOneVisualEffectView:subview - expectedFrame:CGRectMake(0, 0, 10, 10) - inputRadius:5]) { - numberOfExpectedVisualEffectView++; - } - } - XCTAssertEqual(numberOfExpectedVisualEffectView, 1u); -} - -- (void)testApplyBackdropFilterWithCorrectFrame { - 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(); - auto platform_view = std::make_unique( - /*delegate=*/mock_delegate, - /*rendering_api=*/mock_delegate.settings_.enable_impeller - ? flutter::IOSRenderingAPI::kMetal - : flutter::IOSRenderingAPI::kSoftware, - /*platform_views_controller=*/flutterPlatformViewsController, - /*task_runners=*/runners, - /*worker_task_runner=*/nil, - /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - - FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = - [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; - flutterPlatformViewsController->RegisterViewFactory( - factory, @"MockFlutterPlatformView", - FlutterPlatformViewGestureRecognizersBlockingPolicyEager); - FlutterResult result = ^(id result) { - }; - flutterPlatformViewsController->OnMethodCall( - [FlutterMethodCall - methodCallWithMethodName:@"create" - arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], - result); - - XCTAssertNotNil(gMockPlatformView); - - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); - // Create embedded view params - flutter::MutatorsStack stack; - // Layer tree always pushes a screen scale factor to the stack - CGFloat screenScale = [UIScreen mainScreen].scale; - SkMatrix screenScaleMatrix = SkMatrix::Scale(screenScale, screenScale); - stack.PushTransform(screenScaleMatrix); - // Push a backdrop filter - auto filter = std::make_shared(5, 2, flutter::DlTileMode::kClamp); - stack.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 8, screenScale * 8)); - - auto embeddedViewParams = - std::make_unique(screenScaleMatrix, SkSize::Make(5, 10), stack); - - flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); - XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]); - ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; - [mockFlutterView addSubview:childClippingView]; - - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; - - // childClippingView has visual effect view with the correct configurations. - NSUInteger numberOfExpectedVisualEffectView = 0; - for (UIView* subview in childClippingView.subviews) { - if (![subview isKindOfClass:[UIVisualEffectView class]]) { - continue; - } - XCTAssertLessThan(numberOfExpectedVisualEffectView, 1u); - if ([self validateOneVisualEffectView:subview - expectedFrame:CGRectMake(0, 0, 5, 8) - inputRadius:5]) { - numberOfExpectedVisualEffectView++; - } - } - XCTAssertEqual(numberOfExpectedVisualEffectView, 1u); -} - -- (void)testApplyMultipleBackdropFilters { - 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(); - auto platform_view = std::make_unique( - /*delegate=*/mock_delegate, - /*rendering_api=*/mock_delegate.settings_.enable_impeller - ? flutter::IOSRenderingAPI::kMetal - : flutter::IOSRenderingAPI::kSoftware, - /*platform_views_controller=*/flutterPlatformViewsController, - /*task_runners=*/runners, - /*worker_task_runner=*/nil, - /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - - FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = - [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; - flutterPlatformViewsController->RegisterViewFactory( - factory, @"MockFlutterPlatformView", - FlutterPlatformViewGestureRecognizersBlockingPolicyEager); - FlutterResult result = ^(id result) { - }; - flutterPlatformViewsController->OnMethodCall( - [FlutterMethodCall - methodCallWithMethodName:@"create" - arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], - result); - - XCTAssertNotNil(gMockPlatformView); - - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); - // Create embedded view params - flutter::MutatorsStack stack; - // Layer tree always pushes a screen scale factor to the stack - CGFloat screenScale = [UIScreen mainScreen].scale; - SkMatrix screenScaleMatrix = SkMatrix::Scale(screenScale, screenScale); - stack.PushTransform(screenScaleMatrix); - // Push backdrop filters - for (int i = 0; i < 50; i++) { - auto filter = std::make_shared(i, 2, flutter::DlTileMode::kClamp); - stack.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); - } - - auto embeddedViewParams = - std::make_unique(screenScaleMatrix, SkSize::Make(20, 20), stack); - - flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); - XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); - ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; - [mockFlutterView addSubview:childClippingView]; - - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; - - NSUInteger numberOfExpectedVisualEffectView = 0; - for (UIView* subview in childClippingView.subviews) { - if (![subview isKindOfClass:[UIVisualEffectView class]]) { - continue; - } - XCTAssertLessThan(numberOfExpectedVisualEffectView, 50u); - if ([self validateOneVisualEffectView:subview - expectedFrame:CGRectMake(0, 0, 10, 10) - inputRadius:(CGFloat)numberOfExpectedVisualEffectView]) { - numberOfExpectedVisualEffectView++; - } - } - XCTAssertEqual(numberOfExpectedVisualEffectView, (NSUInteger)numberOfExpectedVisualEffectView); -} - -- (void)testAddBackdropFilters { - 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(); - auto platform_view = std::make_unique( - /*delegate=*/mock_delegate, - /*rendering_api=*/mock_delegate.settings_.enable_impeller - ? flutter::IOSRenderingAPI::kMetal - : flutter::IOSRenderingAPI::kSoftware, - /*platform_views_controller=*/flutterPlatformViewsController, - /*task_runners=*/runners, - /*worker_task_runner=*/nil, - /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - - FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = - [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; - flutterPlatformViewsController->RegisterViewFactory( - factory, @"MockFlutterPlatformView", - FlutterPlatformViewGestureRecognizersBlockingPolicyEager); - FlutterResult result = ^(id result) { - }; - flutterPlatformViewsController->OnMethodCall( - [FlutterMethodCall - methodCallWithMethodName:@"create" - arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], - result); - - XCTAssertNotNil(gMockPlatformView); - - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); - // Create embedded view params - flutter::MutatorsStack stack; - // Layer tree always pushes a screen scale factor to the stack - CGFloat screenScale = [UIScreen mainScreen].scale; - SkMatrix screenScaleMatrix = SkMatrix::Scale(screenScale, screenScale); - stack.PushTransform(screenScaleMatrix); - // Push a backdrop filter - auto filter = std::make_shared(5, 2, flutter::DlTileMode::kClamp); - stack.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); - - auto embeddedViewParams = - std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); - - flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); - XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]); - ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; - [mockFlutterView addSubview:childClippingView]; - - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; - - NSMutableArray* originalVisualEffectViews = [[NSMutableArray alloc] init]; - for (UIView* subview in childClippingView.subviews) { - if (![subview isKindOfClass:[UIVisualEffectView class]]) { - continue; - } - XCTAssertLessThan(originalVisualEffectViews.count, 1u); - if ([self validateOneVisualEffectView:subview - expectedFrame:CGRectMake(0, 0, 10, 10) - inputRadius:(CGFloat)5]) { - [originalVisualEffectViews addObject:subview]; - } - } - XCTAssertEqual(originalVisualEffectViews.count, 1u); - - // - // Simulate adding 1 backdrop filter (create a new mutators stack) - // Create embedded view params - flutter::MutatorsStack stack2; - // Layer tree always pushes a screen scale factor to the stack - stack2.PushTransform(screenScaleMatrix); - // Push backdrop filters - for (int i = 0; i < 2; i++) { - stack2.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); - } - - embeddedViewParams = std::make_unique(screenScaleMatrix, - SkSize::Make(10, 10), stack2); - - flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; - - NSMutableArray* newVisualEffectViews = [[NSMutableArray alloc] init]; - for (UIView* subview in childClippingView.subviews) { - if (![subview isKindOfClass:[UIVisualEffectView class]]) { - continue; - } - XCTAssertLessThan(newVisualEffectViews.count, 2u); - - if ([self validateOneVisualEffectView:subview - expectedFrame:CGRectMake(0, 0, 10, 10) - inputRadius:(CGFloat)5]) { - [newVisualEffectViews addObject:subview]; - } - } - XCTAssertEqual(newVisualEffectViews.count, 2u); - for (NSUInteger i = 0; i < originalVisualEffectViews.count; i++) { - UIView* originalView = originalVisualEffectViews[i]; - UIView* newView = newVisualEffectViews[i]; - // Compare reference. - XCTAssertEqual(originalView, newView); - id mockOrignalView = OCMPartialMock(originalView); - OCMReject([mockOrignalView removeFromSuperview]); - [mockOrignalView stopMocking]; - } -} - -- (void)testRemoveBackdropFilters { - 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(); - auto platform_view = std::make_unique( - /*delegate=*/mock_delegate, - /*rendering_api=*/mock_delegate.settings_.enable_impeller - ? flutter::IOSRenderingAPI::kMetal - : flutter::IOSRenderingAPI::kSoftware, - /*platform_views_controller=*/flutterPlatformViewsController, - /*task_runners=*/runners, - /*worker_task_runner=*/nil, - /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - - FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = - [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; - flutterPlatformViewsController->RegisterViewFactory( - factory, @"MockFlutterPlatformView", - FlutterPlatformViewGestureRecognizersBlockingPolicyEager); - FlutterResult result = ^(id result) { - }; - flutterPlatformViewsController->OnMethodCall( - [FlutterMethodCall - methodCallWithMethodName:@"create" - arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], - result); - - XCTAssertNotNil(gMockPlatformView); - - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); - // Create embedded view params - flutter::MutatorsStack stack; - // Layer tree always pushes a screen scale factor to the stack - CGFloat screenScale = [UIScreen mainScreen].scale; - SkMatrix screenScaleMatrix = SkMatrix::Scale(screenScale, screenScale); - stack.PushTransform(screenScaleMatrix); - // Push backdrop filters - auto filter = std::make_shared(5, 2, flutter::DlTileMode::kClamp); - for (int i = 0; i < 5; i++) { - stack.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); - } - - auto embeddedViewParams = - std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); - - flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); - XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); - ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; - [mockFlutterView addSubview:childClippingView]; - - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; - - NSMutableArray* originalVisualEffectViews = [[NSMutableArray alloc] init]; - for (UIView* subview in childClippingView.subviews) { - if (![subview isKindOfClass:[UIVisualEffectView class]]) { - continue; - } - XCTAssertLessThan(originalVisualEffectViews.count, 5u); - if ([self validateOneVisualEffectView:subview - expectedFrame:CGRectMake(0, 0, 10, 10) - inputRadius:(CGFloat)5]) { - [originalVisualEffectViews addObject:subview]; - } - } - - // Simulate removing 1 backdrop filter (create a new mutators stack) - // Create embedded view params - flutter::MutatorsStack stack2; - // Layer tree always pushes a screen scale factor to the stack - stack2.PushTransform(screenScaleMatrix); - // Push backdrop filters - for (int i = 0; i < 4; i++) { - stack2.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); - } - - embeddedViewParams = std::make_unique(screenScaleMatrix, - SkSize::Make(10, 10), stack2); - - flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; - - NSMutableArray* newVisualEffectViews = [[NSMutableArray alloc] init]; - for (UIView* subview in childClippingView.subviews) { - if (![subview isKindOfClass:[UIVisualEffectView class]]) { - continue; - } - XCTAssertLessThan(newVisualEffectViews.count, 4u); - if ([self validateOneVisualEffectView:subview - expectedFrame:CGRectMake(0, 0, 10, 10) - inputRadius:(CGFloat)5]) { - [newVisualEffectViews addObject:subview]; - } - } - XCTAssertEqual(newVisualEffectViews.count, 4u); - - for (NSUInteger i = 0; i < newVisualEffectViews.count; i++) { - UIView* newView = newVisualEffectViews[i]; - id mockNewView = OCMPartialMock(newView); - UIView* originalView = originalVisualEffectViews[i]; - // Compare reference. - XCTAssertEqual(originalView, newView); - OCMReject([mockNewView removeFromSuperview]); - [mockNewView stopMocking]; - } - - // Simulate removing all backdrop filters (replace the mutators stack) - // Update embedded view params, delete except screenScaleMatrix - for (int i = 0; i < 5; i++) { - stack2.Pop(); - } - // No backdrop filters in the stack, so no nothing to push - - embeddedViewParams = std::make_unique(screenScaleMatrix, - SkSize::Make(10, 10), stack2); - - flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; - - NSUInteger numberOfExpectedVisualEffectView = 0u; - for (UIView* subview in childClippingView.subviews) { - if ([subview isKindOfClass:[UIVisualEffectView class]]) { - numberOfExpectedVisualEffectView++; - } - } - XCTAssertEqual(numberOfExpectedVisualEffectView, 0u); -} - -- (void)testEditBackdropFilters { - 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(); - auto platform_view = std::make_unique( - /*delegate=*/mock_delegate, - /*rendering_api=*/mock_delegate.settings_.enable_impeller - ? flutter::IOSRenderingAPI::kMetal - : flutter::IOSRenderingAPI::kSoftware, - /*platform_views_controller=*/flutterPlatformViewsController, - /*task_runners=*/runners, - /*worker_task_runner=*/nil, - /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - - FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = - [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; - flutterPlatformViewsController->RegisterViewFactory( - factory, @"MockFlutterPlatformView", - FlutterPlatformViewGestureRecognizersBlockingPolicyEager); - FlutterResult result = ^(id result) { - }; - flutterPlatformViewsController->OnMethodCall( - [FlutterMethodCall - methodCallWithMethodName:@"create" - arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], - result); - - XCTAssertNotNil(gMockPlatformView); - - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); - // Create embedded view params - flutter::MutatorsStack stack; - // Layer tree always pushes a screen scale factor to the stack - CGFloat screenScale = [UIScreen mainScreen].scale; - SkMatrix screenScaleMatrix = SkMatrix::Scale(screenScale, screenScale); - stack.PushTransform(screenScaleMatrix); - // Push backdrop filters - auto filter = std::make_shared(5, 2, flutter::DlTileMode::kClamp); - for (int i = 0; i < 5; i++) { - stack.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); - } - - auto embeddedViewParams = - std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); - - flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); - XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); - ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; - [mockFlutterView addSubview:childClippingView]; - - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; - - NSMutableArray* originalVisualEffectViews = [[NSMutableArray alloc] init]; - for (UIView* subview in childClippingView.subviews) { - if (![subview isKindOfClass:[UIVisualEffectView class]]) { - continue; - } - XCTAssertLessThan(originalVisualEffectViews.count, 5u); - if ([self validateOneVisualEffectView:subview - expectedFrame:CGRectMake(0, 0, 10, 10) - inputRadius:(CGFloat)5]) { - [originalVisualEffectViews addObject:subview]; - } - } - - // Simulate editing 1 backdrop filter in the middle of the stack (create a new mutators stack) - // Create embedded view params - flutter::MutatorsStack stack2; - // Layer tree always pushes a screen scale factor to the stack - stack2.PushTransform(screenScaleMatrix); - // Push backdrop filters - for (int i = 0; i < 5; i++) { - if (i == 3) { - auto filter2 = - std::make_shared(2, 5, flutter::DlTileMode::kClamp); - - stack2.PushBackdropFilter(filter2, - SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); - continue; - } - - stack2.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); - } - - embeddedViewParams = std::make_unique(screenScaleMatrix, - SkSize::Make(10, 10), stack2); - - flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; - - NSMutableArray* newVisualEffectViews = [[NSMutableArray alloc] init]; - for (UIView* subview in childClippingView.subviews) { - if (![subview isKindOfClass:[UIVisualEffectView class]]) { - continue; - } - XCTAssertLessThan(newVisualEffectViews.count, 5u); - CGFloat expectInputRadius = 5; - if (newVisualEffectViews.count == 3) { - expectInputRadius = 2; - } - if ([self validateOneVisualEffectView:subview - expectedFrame:CGRectMake(0, 0, 10, 10) - inputRadius:(CGFloat)expectInputRadius]) { - [newVisualEffectViews addObject:subview]; - } - } - XCTAssertEqual(newVisualEffectViews.count, 5u); - for (NSUInteger i = 0; i < newVisualEffectViews.count; i++) { - UIView* newView = newVisualEffectViews[i]; - id mockNewView = OCMPartialMock(newView); - UIView* originalView = originalVisualEffectViews[i]; - // Compare reference. - XCTAssertEqual(originalView, newView); - OCMReject([mockNewView removeFromSuperview]); - [mockNewView stopMocking]; - } - [newVisualEffectViews removeAllObjects]; - - // Simulate editing 1 backdrop filter in the beginning of the stack (replace the mutators stack) - // Update embedded view params, delete except screenScaleMatrix - for (int i = 0; i < 5; i++) { - stack2.Pop(); - } - // Push backdrop filters - for (int i = 0; i < 5; i++) { - if (i == 0) { - auto filter2 = - std::make_shared(2, 5, flutter::DlTileMode::kClamp); - stack2.PushBackdropFilter(filter2, - SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); - continue; - } - - stack2.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); - } - - embeddedViewParams = std::make_unique(screenScaleMatrix, - SkSize::Make(10, 10), stack2); - - flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; - - for (UIView* subview in childClippingView.subviews) { - if (![subview isKindOfClass:[UIVisualEffectView class]]) { - continue; - } - XCTAssertLessThan(newVisualEffectViews.count, 5u); - CGFloat expectInputRadius = 5; - if (newVisualEffectViews.count == 0) { - expectInputRadius = 2; - } - if ([self validateOneVisualEffectView:subview - expectedFrame:CGRectMake(0, 0, 10, 10) - inputRadius:(CGFloat)expectInputRadius]) { - [newVisualEffectViews addObject:subview]; - } - } - for (NSUInteger i = 0; i < newVisualEffectViews.count; i++) { - UIView* newView = newVisualEffectViews[i]; - id mockNewView = OCMPartialMock(newView); - UIView* originalView = originalVisualEffectViews[i]; - // Compare reference. - XCTAssertEqual(originalView, newView); - OCMReject([mockNewView removeFromSuperview]); - [mockNewView stopMocking]; - } - [newVisualEffectViews removeAllObjects]; - - // Simulate editing 1 backdrop filter in the end of the stack (replace the mutators stack) - // Update embedded view params, delete except screenScaleMatrix - for (int i = 0; i < 5; i++) { - stack2.Pop(); - } - // Push backdrop filters - for (int i = 0; i < 5; i++) { - if (i == 4) { - auto filter2 = - std::make_shared(2, 5, flutter::DlTileMode::kClamp); - stack2.PushBackdropFilter(filter2, - SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); - continue; - } - - stack2.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); - } - - embeddedViewParams = std::make_unique(screenScaleMatrix, - SkSize::Make(10, 10), stack2); - - flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; - - for (UIView* subview in childClippingView.subviews) { - if (![subview isKindOfClass:[UIVisualEffectView class]]) { - continue; - } - XCTAssertLessThan(newVisualEffectViews.count, 5u); - CGFloat expectInputRadius = 5; - if (newVisualEffectViews.count == 4) { - expectInputRadius = 2; - } - if ([self validateOneVisualEffectView:subview - expectedFrame:CGRectMake(0, 0, 10, 10) - inputRadius:(CGFloat)expectInputRadius]) { - [newVisualEffectViews addObject:subview]; - } - } - XCTAssertEqual(newVisualEffectViews.count, 5u); - - for (NSUInteger i = 0; i < newVisualEffectViews.count; i++) { - UIView* newView = newVisualEffectViews[i]; - id mockNewView = OCMPartialMock(newView); - UIView* originalView = originalVisualEffectViews[i]; - // Compare reference. - XCTAssertEqual(originalView, newView); - OCMReject([mockNewView removeFromSuperview]); - [mockNewView stopMocking]; - } - [newVisualEffectViews removeAllObjects]; - - // Simulate editing all backdrop filters in the stack (replace the mutators stack) - // Update embedded view params, delete except screenScaleMatrix - for (int i = 0; i < 5; i++) { - stack2.Pop(); - } - // Push backdrop filters - for (int i = 0; i < 5; i++) { - auto filter2 = std::make_shared(i, 2, flutter::DlTileMode::kClamp); - - stack2.PushBackdropFilter(filter2, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); - } - - embeddedViewParams = std::make_unique(screenScaleMatrix, - SkSize::Make(10, 10), stack2); - - flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; - - for (UIView* subview in childClippingView.subviews) { - if (![subview isKindOfClass:[UIVisualEffectView class]]) { - continue; - } - XCTAssertLessThan(newVisualEffectViews.count, 5u); - if ([self validateOneVisualEffectView:subview - expectedFrame:CGRectMake(0, 0, 10, 10) - inputRadius:(CGFloat)newVisualEffectViews.count]) { - [newVisualEffectViews addObject:subview]; - } - } - XCTAssertEqual(newVisualEffectViews.count, 5u); - - for (NSUInteger i = 0; i < newVisualEffectViews.count; i++) { - UIView* newView = newVisualEffectViews[i]; - id mockNewView = OCMPartialMock(newView); - UIView* originalView = originalVisualEffectViews[i]; - // Compare reference. - XCTAssertEqual(originalView, newView); - OCMReject([mockNewView removeFromSuperview]); - [mockNewView stopMocking]; - } - [newVisualEffectViews removeAllObjects]; -} - -- (void)testApplyBackdropFilterNotDlBlurImageFilter { - 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(); - auto platform_view = std::make_unique( - /*delegate=*/mock_delegate, - /*rendering_api=*/mock_delegate.settings_.enable_impeller - ? flutter::IOSRenderingAPI::kMetal - : flutter::IOSRenderingAPI::kSoftware, - /*platform_views_controller=*/flutterPlatformViewsController, - /*task_runners=*/runners, - /*worker_task_runner=*/nil, - /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - - FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = - [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; - flutterPlatformViewsController->RegisterViewFactory( - factory, @"MockFlutterPlatformView", - FlutterPlatformViewGestureRecognizersBlockingPolicyEager); - FlutterResult result = ^(id result) { - }; - flutterPlatformViewsController->OnMethodCall( - [FlutterMethodCall - methodCallWithMethodName:@"create" - arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], - result); - - XCTAssertNotNil(gMockPlatformView); - - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); - // Create embedded view params - flutter::MutatorsStack stack; - // Layer tree always pushes a screen scale factor to the stack - CGFloat screenScale = [UIScreen mainScreen].scale; - SkMatrix screenScaleMatrix = SkMatrix::Scale(screenScale, screenScale); - stack.PushTransform(screenScaleMatrix); - // Push a dilate backdrop filter - auto dilateFilter = std::make_shared(5, 2); - stack.PushBackdropFilter(dilateFilter, SkRect::MakeEmpty()); - - auto embeddedViewParams = - std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); - - flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); - XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]); - ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; - - [mockFlutterView addSubview:childClippingView]; - - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; - - NSUInteger numberOfExpectedVisualEffectView = 0; - for (UIView* subview in childClippingView.subviews) { - if ([subview isKindOfClass:[UIVisualEffectView class]]) { - numberOfExpectedVisualEffectView++; - } - } - XCTAssertEqual(numberOfExpectedVisualEffectView, 0u); - - // Simulate adding a non-DlBlurImageFilter in the middle of the stack (create a new mutators - // stack) Create embedded view params - flutter::MutatorsStack stack2; - // Layer tree always pushes a screen scale factor to the stack - stack2.PushTransform(screenScaleMatrix); - // Push backdrop filters and dilate filter - auto blurFilter = std::make_shared(5, 2, flutter::DlTileMode::kClamp); - - for (int i = 0; i < 5; i++) { - if (i == 2) { - stack2.PushBackdropFilter(dilateFilter, - SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); - continue; - } - - stack2.PushBackdropFilter(blurFilter, - SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); - } - - embeddedViewParams = std::make_unique(screenScaleMatrix, - SkSize::Make(10, 10), stack2); - - flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; - - numberOfExpectedVisualEffectView = 0; - for (UIView* subview in childClippingView.subviews) { - if (![subview isKindOfClass:[UIVisualEffectView class]]) { - continue; - } - XCTAssertLessThan(numberOfExpectedVisualEffectView, 4u); - if ([self validateOneVisualEffectView:subview - expectedFrame:CGRectMake(0, 0, 10, 10) - inputRadius:(CGFloat)5]) { - numberOfExpectedVisualEffectView++; - } - } - XCTAssertEqual(numberOfExpectedVisualEffectView, 4u); - - // Simulate adding a non-DlBlurImageFilter to the beginning of the stack (replace the mutators - // stack) Update embedded view params, delete except screenScaleMatrix - for (int i = 0; i < 5; i++) { - stack2.Pop(); - } - // Push backdrop filters and dilate filter - for (int i = 0; i < 5; i++) { - if (i == 0) { - stack2.PushBackdropFilter(dilateFilter, - SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); - continue; - } - - stack2.PushBackdropFilter(blurFilter, - SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); - } - - embeddedViewParams = std::make_unique(screenScaleMatrix, - SkSize::Make(10, 10), stack2); - - flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; - - numberOfExpectedVisualEffectView = 0; - for (UIView* subview in childClippingView.subviews) { - if (![subview isKindOfClass:[UIVisualEffectView class]]) { - continue; - } - XCTAssertLessThan(numberOfExpectedVisualEffectView, 4u); - if ([self validateOneVisualEffectView:subview - expectedFrame:CGRectMake(0, 0, 10, 10) - inputRadius:(CGFloat)5]) { - numberOfExpectedVisualEffectView++; - } - } - XCTAssertEqual(numberOfExpectedVisualEffectView, 4u); - - // Simulate adding a non-DlBlurImageFilter to the end of the stack (replace the mutators stack) - // Update embedded view params, delete except screenScaleMatrix - for (int i = 0; i < 5; i++) { - stack2.Pop(); - } - // Push backdrop filters and dilate filter - for (int i = 0; i < 5; i++) { - if (i == 4) { - stack2.PushBackdropFilter(dilateFilter, - SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); - continue; - } - - stack2.PushBackdropFilter(blurFilter, - SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); - } - - embeddedViewParams = std::make_unique(screenScaleMatrix, - SkSize::Make(10, 10), stack2); - - flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; - - numberOfExpectedVisualEffectView = 0; - for (UIView* subview in childClippingView.subviews) { - if (![subview isKindOfClass:[UIVisualEffectView class]]) { - continue; - } - XCTAssertLessThan(numberOfExpectedVisualEffectView, 4u); - if ([self validateOneVisualEffectView:subview - expectedFrame:CGRectMake(0, 0, 10, 10) - inputRadius:(CGFloat)5]) { - numberOfExpectedVisualEffectView++; - } - } - XCTAssertEqual(numberOfExpectedVisualEffectView, 4u); - - // Simulate adding only non-DlBlurImageFilter to the stack (replace the mutators stack) - // Update embedded view params, delete except screenScaleMatrix - for (int i = 0; i < 5; i++) { - stack2.Pop(); - } - // Push dilate filters - for (int i = 0; i < 5; i++) { - stack2.PushBackdropFilter(dilateFilter, - SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); - } - - embeddedViewParams = std::make_unique(screenScaleMatrix, - SkSize::Make(10, 10), stack2); - - flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; - - numberOfExpectedVisualEffectView = 0; - for (UIView* subview in childClippingView.subviews) { - if ([subview isKindOfClass:[UIVisualEffectView class]]) { - numberOfExpectedVisualEffectView++; - } - } - XCTAssertEqual(numberOfExpectedVisualEffectView, 0u); -} - -- (void)testApplyBackdropFilterCorrectAPI { - [PlatformViewFilter resetPreparation]; - // The gaussianBlur filter is extracted from UIVisualEffectView. - // Each test requires a new PlatformViewFilter - // Valid UIVisualEffectView API - UIVisualEffectView* visualEffectView = [[UIVisualEffectView alloc] - initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]]; - PlatformViewFilter* platformViewFilter = - [[PlatformViewFilter alloc] initWithFrame:CGRectMake(0, 0, 10, 10) - blurRadius:5 - visualEffectView:visualEffectView]; - XCTAssertNotNil(platformViewFilter); -} - -- (void)testApplyBackdropFilterAPIChangedInvalidUIVisualEffectView { - [PlatformViewFilter resetPreparation]; - UIVisualEffectView* visualEffectView = [[UIVisualEffectView alloc] init]; - PlatformViewFilter* platformViewFilter = - [[PlatformViewFilter alloc] initWithFrame:CGRectMake(0, 0, 10, 10) - blurRadius:5 - visualEffectView:visualEffectView]; - XCTAssertNil(platformViewFilter); -} - -- (void)testApplyBackdropFilterAPIChangedNoGaussianBlurFilter { - [PlatformViewFilter resetPreparation]; - UIVisualEffectView* editedUIVisualEffectView = [[UIVisualEffectView alloc] - initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]]; - NSArray* subviews = editedUIVisualEffectView.subviews; - for (UIView* view in subviews) { - if ([NSStringFromClass([view class]) hasSuffix:@"BackdropView"]) { - for (CIFilter* filter in view.layer.filters) { - if ([[filter valueForKey:@"name"] isEqual:@"gaussianBlur"]) { - [filter setValue:@"notGaussianBlur" forKey:@"name"]; - break; - } - } - break; - } - } - PlatformViewFilter* platformViewFilter = - [[PlatformViewFilter alloc] initWithFrame:CGRectMake(0, 0, 10, 10) - blurRadius:5 - visualEffectView:editedUIVisualEffectView]; - XCTAssertNil(platformViewFilter); -} - -- (void)testApplyBackdropFilterAPIChangedInvalidInputRadius { - [PlatformViewFilter resetPreparation]; - UIVisualEffectView* editedUIVisualEffectView = [[UIVisualEffectView alloc] - initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]]; - NSArray* subviews = editedUIVisualEffectView.subviews; - for (UIView* view in subviews) { - if ([NSStringFromClass([view class]) hasSuffix:@"BackdropView"]) { - for (CIFilter* filter in view.layer.filters) { - if ([[filter valueForKey:@"name"] isEqual:@"gaussianBlur"]) { - [filter setValue:@"invalidInputRadius" forKey:@"inputRadius"]; - break; - } - } - break; - } - } - - PlatformViewFilter* platformViewFilter = - [[PlatformViewFilter alloc] initWithFrame:CGRectMake(0, 0, 10, 10) - blurRadius:5 - visualEffectView:editedUIVisualEffectView]; - XCTAssertNil(platformViewFilter); -} - -- (void)testBackdropFilterVisualEffectSubviewBackgroundColor { - __weak UIVisualEffectView* weakVisualEffectView; - - @autoreleasepool { - UIVisualEffectView* visualEffectView = [[UIVisualEffectView alloc] - initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]]; - weakVisualEffectView = visualEffectView; - PlatformViewFilter* platformViewFilter = - [[PlatformViewFilter alloc] initWithFrame:CGRectMake(0, 0, 10, 10) - blurRadius:5 - visualEffectView:visualEffectView]; - CGColorRef visualEffectSubviewBackgroundColor = nil; - for (UIView* view in [platformViewFilter backdropFilterView].subviews) { - if ([NSStringFromClass([view class]) hasSuffix:@"VisualEffectSubview"]) { - visualEffectSubviewBackgroundColor = view.layer.backgroundColor; - } - } - XCTAssertTrue( - CGColorEqualToColor(visualEffectSubviewBackgroundColor, UIColor.clearColor.CGColor)); - } - XCTAssertNil(weakVisualEffectView); -} - -- (void)testCompositePlatformView { - 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(); - auto platform_view = std::make_unique( - /*delegate=*/mock_delegate, - /*rendering_api=*/mock_delegate.settings_.enable_impeller - ? flutter::IOSRenderingAPI::kMetal - : flutter::IOSRenderingAPI::kSoftware, - /*platform_views_controller=*/flutterPlatformViewsController, - /*task_runners=*/runners, - /*worker_task_runner=*/nil, - /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - - FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = - [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; - flutterPlatformViewsController->RegisterViewFactory( - factory, @"MockFlutterPlatformView", - FlutterPlatformViewGestureRecognizersBlockingPolicyEager); - FlutterResult result = ^(id result) { - }; - flutterPlatformViewsController->OnMethodCall( - [FlutterMethodCall - methodCallWithMethodName:@"create" - arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], - result); - - XCTAssertNotNil(gMockPlatformView); - - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); - // Create embedded view params - flutter::MutatorsStack stack; - // Layer tree always pushes a screen scale factor to the stack - SkMatrix screenScaleMatrix = - SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); - stack.PushTransform(screenScaleMatrix); - // Push a translate matrix - SkMatrix translateMatrix = SkMatrix::Translate(100, 100); - stack.PushTransform(translateMatrix); - SkMatrix finalMatrix; - finalMatrix.setConcat(screenScaleMatrix, translateMatrix); - - auto embeddedViewParams = - std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); - - flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); - CGRect platformViewRectInFlutterView = [gMockPlatformView convertRect:gMockPlatformView.bounds - toView:mockFlutterView]; - XCTAssertTrue(CGRectEqualToRect(platformViewRectInFlutterView, CGRectMake(100, 100, 300, 300))); -} - -- (void)testBackdropFilterCorrectlyPushedAndReset { - 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(); - auto platform_view = std::make_unique( - /*delegate=*/mock_delegate, - /*rendering_api=*/mock_delegate.settings_.enable_impeller - ? flutter::IOSRenderingAPI::kMetal - : flutter::IOSRenderingAPI::kSoftware, - /*platform_views_controller=*/flutterPlatformViewsController, - /*task_runners=*/runners, - /*worker_task_runner=*/nil, - /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - - FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = - [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; - flutterPlatformViewsController->RegisterViewFactory( - factory, @"MockFlutterPlatformView", - FlutterPlatformViewGestureRecognizersBlockingPolicyEager); - FlutterResult result = ^(id result) { - }; - flutterPlatformViewsController->OnMethodCall( - [FlutterMethodCall - methodCallWithMethodName:@"create" - arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], - result); - - XCTAssertNotNil(gMockPlatformView); - - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); - // Create embedded view params - flutter::MutatorsStack stack; - // Layer tree always pushes a screen scale factor to the stack - CGFloat screenScale = [UIScreen mainScreen].scale; - SkMatrix screenScaleMatrix = SkMatrix::Scale(screenScale, screenScale); - stack.PushTransform(screenScaleMatrix); - - auto embeddedViewParams = - std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); - - flutterPlatformViewsController->BeginFrame(SkISize::Make(0, 0)); - flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->PushVisitedPlatformView(2); - auto filter = std::make_shared(5, 2, flutter::DlTileMode::kClamp); - flutterPlatformViewsController->PushFilterToVisitedPlatformViews( - filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); - flutterPlatformViewsController->CompositeEmbeddedView(2); - XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]); - ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; - [mockFlutterView addSubview:childClippingView]; - - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; - - // childClippingView has visual effect view with the correct configurations. - NSUInteger numberOfExpectedVisualEffectView = 0; - for (UIView* subview in childClippingView.subviews) { - if (![subview isKindOfClass:[UIVisualEffectView class]]) { - continue; - } - XCTAssertLessThan(numberOfExpectedVisualEffectView, 1u); - if ([self validateOneVisualEffectView:subview - expectedFrame:CGRectMake(0, 0, 10, 10) - inputRadius:5]) { - numberOfExpectedVisualEffectView++; - } - } - XCTAssertEqual(numberOfExpectedVisualEffectView, 1u); - - // New frame, with no filter pushed. - auto embeddedViewParams2 = - std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); - flutterPlatformViewsController->BeginFrame(SkISize::Make(0, 0)); - flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams2)); - flutterPlatformViewsController->CompositeEmbeddedView(2); - XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]); - - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; - - numberOfExpectedVisualEffectView = 0; - for (UIView* subview in childClippingView.subviews) { - if (![subview isKindOfClass:[UIVisualEffectView class]]) { - continue; - } - numberOfExpectedVisualEffectView++; - } - XCTAssertEqual(numberOfExpectedVisualEffectView, 0u); -} - -- (void)testChildClippingViewShouldBeTheBoundingRectOfPlatformView { - 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(); - auto platform_view = std::make_unique( - /*delegate=*/mock_delegate, - /*rendering_api=*/mock_delegate.settings_.enable_impeller - ? flutter::IOSRenderingAPI::kMetal - : flutter::IOSRenderingAPI::kSoftware, - /*platform_views_controller=*/flutterPlatformViewsController, - /*task_runners=*/runners, - /*worker_task_runner=*/nil, - /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - - FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = - [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; - flutterPlatformViewsController->RegisterViewFactory( - factory, @"MockFlutterPlatformView", - FlutterPlatformViewGestureRecognizersBlockingPolicyEager); - FlutterResult result = ^(id result) { - }; - flutterPlatformViewsController->OnMethodCall( - [FlutterMethodCall - methodCallWithMethodName:@"create" - arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], - result); - - XCTAssertNotNil(gMockPlatformView); - - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); - // Create embedded view params - flutter::MutatorsStack stack; - // Layer tree always pushes a screen scale factor to the stack - SkMatrix screenScaleMatrix = - SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); - stack.PushTransform(screenScaleMatrix); - // Push a rotate matrix - SkMatrix rotateMatrix; - rotateMatrix.setRotate(10); - stack.PushTransform(rotateMatrix); - SkMatrix finalMatrix; - finalMatrix.setConcat(screenScaleMatrix, rotateMatrix); - - auto embeddedViewParams = - std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); - - flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); - CGRect platformViewRectInFlutterView = [gMockPlatformView convertRect:gMockPlatformView.bounds - toView:mockFlutterView]; - XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); - ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; - // The childclippingview's frame is set based on flow, but the platform view's frame is set based - // on quartz. Although they should be the same, but we should tolerate small floating point - // errors. - XCTAssertLessThan(fabs(platformViewRectInFlutterView.origin.x - childClippingView.frame.origin.x), - kFloatCompareEpsilon); - XCTAssertLessThan(fabs(platformViewRectInFlutterView.origin.y - childClippingView.frame.origin.y), - kFloatCompareEpsilon); - XCTAssertLessThan( - fabs(platformViewRectInFlutterView.size.width - childClippingView.frame.size.width), - kFloatCompareEpsilon); - XCTAssertLessThan( - fabs(platformViewRectInFlutterView.size.height - childClippingView.frame.size.height), - kFloatCompareEpsilon); -} - -- (void)testClipsDoNotInterceptWithPlatformViewShouldNotAddMaskView { - 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(); - auto platform_view = std::make_unique( - /*delegate=*/mock_delegate, - /*rendering_api=*/mock_delegate.settings_.enable_impeller - ? flutter::IOSRenderingAPI::kMetal - : flutter::IOSRenderingAPI::kSoftware, - /*platform_views_controller=*/flutterPlatformViewsController, - /*task_runners=*/runners, - /*worker_task_runner=*/nil, - /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - - FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = - [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; - flutterPlatformViewsController->RegisterViewFactory( - factory, @"MockFlutterPlatformView", - FlutterPlatformViewGestureRecognizersBlockingPolicyEager); - FlutterResult result = ^(id result) { - }; - flutterPlatformViewsController->OnMethodCall( - [FlutterMethodCall - methodCallWithMethodName:@"create" - arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], - result); - - XCTAssertNotNil(gMockPlatformView); - - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 30, 30)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); - // Create embedded view params. - flutter::MutatorsStack stack; - // Layer tree always pushes a screen scale factor to the stack. - SkMatrix screenScaleMatrix = - SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); - stack.PushTransform(screenScaleMatrix); - SkMatrix translateMatrix = SkMatrix::Translate(5, 5); - // The platform view's rect for this test will be (5, 5, 10, 10). - stack.PushTransform(translateMatrix); - // Push a clip rect, big enough to contain the entire platform view bound. - SkRect rect = SkRect::MakeXYWH(0, 0, 25, 25); - stack.PushClipRect(rect); - // Push a clip rrect, big enough to contain the entire platform view bound without clipping it. - // Make the origin (-1, -1) so that the top left rounded corner isn't clipping the PlatformView. - SkRect rect_for_rrect = SkRect::MakeXYWH(-1, -1, 25, 25); - SkRRect rrect = SkRRect::MakeRectXY(rect_for_rrect, 1, 1); - stack.PushClipRRect(rrect); - - auto embeddedViewParams = std::make_unique( - SkMatrix::Concat(screenScaleMatrix, translateMatrix), SkSize::Make(5, 5), stack); - - flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); - gMockPlatformView.backgroundColor = UIColor.redColor; - XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); - ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; - [mockFlutterView addSubview:childClippingView]; - - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; - XCTAssertNil(childClippingView.maskView); -} - -- (void)testClipRRectOnlyHasCornersInterceptWithPlatformViewShouldAddMaskView { - 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(); - auto platform_view = std::make_unique( - /*delegate=*/mock_delegate, - /*rendering_api=*/mock_delegate.settings_.enable_impeller - ? flutter::IOSRenderingAPI::kMetal - : flutter::IOSRenderingAPI::kSoftware, - /*platform_views_controller=*/flutterPlatformViewsController, - /*task_runners=*/runners, - /*worker_task_runner=*/nil, - /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - - FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = - [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; - flutterPlatformViewsController->RegisterViewFactory( - factory, @"MockFlutterPlatformView", - FlutterPlatformViewGestureRecognizersBlockingPolicyEager); - FlutterResult result = ^(id result) { - }; - flutterPlatformViewsController->OnMethodCall( - [FlutterMethodCall - methodCallWithMethodName:@"create" - arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], - result); - - XCTAssertNotNil(gMockPlatformView); - - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 30, 30)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); - // Create embedded view params - flutter::MutatorsStack stack; - // Layer tree always pushes a screen scale factor to the stack. - SkMatrix screenScaleMatrix = - SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); - stack.PushTransform(screenScaleMatrix); - SkMatrix translateMatrix = SkMatrix::Translate(5, 5); - // The platform view's rect for this test will be (5, 5, 10, 10). - stack.PushTransform(translateMatrix); - - // Push a clip rrect, the rect of the rrect is the same as the PlatformView of the corner should. - // clip the PlatformView. - SkRect rect_for_rrect = SkRect::MakeXYWH(0, 0, 10, 10); - SkRRect rrect = SkRRect::MakeRectXY(rect_for_rrect, 1, 1); - stack.PushClipRRect(rrect); - - auto embeddedViewParams = std::make_unique( - SkMatrix::Concat(screenScaleMatrix, translateMatrix), SkSize::Make(5, 5), stack); - - flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); - gMockPlatformView.backgroundColor = UIColor.redColor; - XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); - ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; - [mockFlutterView addSubview:childClippingView]; - - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; - - XCTAssertNotNil(childClippingView.maskView); -} - -- (void)testClipRect { - 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(); - auto platform_view = std::make_unique( - /*delegate=*/mock_delegate, - /*rendering_api=*/mock_delegate.settings_.enable_impeller - ? flutter::IOSRenderingAPI::kMetal - : flutter::IOSRenderingAPI::kSoftware, - /*platform_views_controller=*/flutterPlatformViewsController, - /*task_runners=*/runners, - /*worker_task_runner=*/nil, - /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - - FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = - [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; - flutterPlatformViewsController->RegisterViewFactory( - factory, @"MockFlutterPlatformView", - FlutterPlatformViewGestureRecognizersBlockingPolicyEager); - FlutterResult result = ^(id result) { - }; - flutterPlatformViewsController->OnMethodCall( - [FlutterMethodCall - methodCallWithMethodName:@"create" - arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], - result); - - XCTAssertNotNil(gMockPlatformView); - - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); - // Create embedded view params - flutter::MutatorsStack stack; - // Layer tree always pushes a screen scale factor to the stack - SkMatrix screenScaleMatrix = - SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); - stack.PushTransform(screenScaleMatrix); - // Push a clip rect - SkRect rect = SkRect::MakeXYWH(2, 2, 3, 3); - stack.PushClipRect(rect); - - auto embeddedViewParams = - std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); - - flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); - gMockPlatformView.backgroundColor = UIColor.redColor; - XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); - ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; - [mockFlutterView addSubview:childClippingView]; - - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; - - CGRect insideClipping = CGRectMake(2, 2, 3, 3); - for (int i = 0; i < 10; i++) { - for (int j = 0; j < 10; j++) { - CGPoint point = CGPointMake(i, j); - int alpha = [self alphaOfPoint:CGPointMake(i, j) onView:mockFlutterView]; - if (CGRectContainsPoint(insideClipping, point)) { - XCTAssertEqual(alpha, 255); - } else { - XCTAssertEqual(alpha, 0); - } - } - } -} - -- (void)testClipRRect { - 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(); - auto platform_view = std::make_unique( - /*delegate=*/mock_delegate, - /*rendering_api=*/mock_delegate.settings_.enable_impeller - ? flutter::IOSRenderingAPI::kMetal - : flutter::IOSRenderingAPI::kSoftware, - /*platform_views_controller=*/flutterPlatformViewsController, - /*task_runners=*/runners, - /*worker_task_runner=*/nil, - /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - - FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = - [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; - flutterPlatformViewsController->RegisterViewFactory( - factory, @"MockFlutterPlatformView", - FlutterPlatformViewGestureRecognizersBlockingPolicyEager); - FlutterResult result = ^(id result) { - }; - flutterPlatformViewsController->OnMethodCall( - [FlutterMethodCall - methodCallWithMethodName:@"create" - arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], - result); - - XCTAssertNotNil(gMockPlatformView); - - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); - // Create embedded view params - flutter::MutatorsStack stack; - // Layer tree always pushes a screen scale factor to the stack - SkMatrix screenScaleMatrix = - SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); - stack.PushTransform(screenScaleMatrix); - // Push a clip rrect - SkRRect rrect = SkRRect::MakeRectXY(SkRect::MakeXYWH(2, 2, 6, 6), 1, 1); - stack.PushClipRRect(rrect); - - auto embeddedViewParams = - std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); - - flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); - gMockPlatformView.backgroundColor = UIColor.redColor; - XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); - ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; - [mockFlutterView addSubview:childClippingView]; - - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; - - /* - ClippingMask outterClipping - 2 3 4 5 6 7 2 3 4 5 6 7 - 2 / - - - - \ 2 + - - - - + - 3 | | 3 | | - 4 | | 4 | | - 5 | | 5 | | - 6 | | 6 | | - 7 \ - - - - / 7 + - - - - + - - innerClipping1 innerClipping2 - 2 3 4 5 6 7 2 3 4 5 6 7 - 2 + - - + 2 - 3 | | 3 + - - - - + - 4 | | 4 | | - 5 | | 5 | | - 6 | | 6 + - - - - + - 7 + - - + 7 - */ - CGRect innerClipping1 = CGRectMake(3, 2, 4, 6); - CGRect innerClipping2 = CGRectMake(2, 3, 6, 4); - CGRect outterClipping = CGRectMake(2, 2, 6, 6); - for (int i = 0; i < 10; i++) { - for (int j = 0; j < 10; j++) { - CGPoint point = CGPointMake(i, j); - int alpha = [self alphaOfPoint:CGPointMake(i, j) onView:mockFlutterView]; - if (CGRectContainsPoint(innerClipping1, point) || - CGRectContainsPoint(innerClipping2, point)) { - // Pixels inside either of the 2 inner clippings should be fully opaque. - XCTAssertEqual(alpha, 255); - } else if (CGRectContainsPoint(outterClipping, point)) { - // Corner pixels (i.e. (2, 2), (2, 7), (7, 2) and (7, 7)) should be partially transparent. - XCTAssert(0 < alpha && alpha < 255); - } else { - // Pixels outside outterClipping should be fully transparent. - XCTAssertEqual(alpha, 0); - } - } - } -} - -- (void)testClipPath { - 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(); - auto platform_view = std::make_unique( - /*delegate=*/mock_delegate, - /*rendering_api=*/mock_delegate.settings_.enable_impeller - ? flutter::IOSRenderingAPI::kMetal - : flutter::IOSRenderingAPI::kSoftware, - /*platform_views_controller=*/flutterPlatformViewsController, - /*task_runners=*/runners, - /*worker_task_runner=*/nil, - /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - - FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = - [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; - flutterPlatformViewsController->RegisterViewFactory( - factory, @"MockFlutterPlatformView", - FlutterPlatformViewGestureRecognizersBlockingPolicyEager); - FlutterResult result = ^(id result) { - }; - flutterPlatformViewsController->OnMethodCall( - [FlutterMethodCall - methodCallWithMethodName:@"create" - arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], - result); - - XCTAssertNotNil(gMockPlatformView); - - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); - // Create embedded view params - flutter::MutatorsStack stack; - // Layer tree always pushes a screen scale factor to the stack - SkMatrix screenScaleMatrix = - SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); - stack.PushTransform(screenScaleMatrix); - // Push a clip path - SkPath path; - path.addRoundRect(SkRect::MakeXYWH(2, 2, 6, 6), 1, 1); - stack.PushClipPath(path); - - auto embeddedViewParams = - std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); - - flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); - gMockPlatformView.backgroundColor = UIColor.redColor; - XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); - ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; - [mockFlutterView addSubview:childClippingView]; - - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; - - /* - ClippingMask outterClipping - 2 3 4 5 6 7 2 3 4 5 6 7 - 2 / - - - - \ 2 + - - - - + - 3 | | 3 | | - 4 | | 4 | | - 5 | | 5 | | - 6 | | 6 | | - 7 \ - - - - / 7 + - - - - + - - innerClipping1 innerClipping2 - 2 3 4 5 6 7 2 3 4 5 6 7 - 2 + - - + 2 - 3 | | 3 + - - - - + - 4 | | 4 | | - 5 | | 5 | | - 6 | | 6 + - - - - + - 7 + - - + 7 - */ - CGRect innerClipping1 = CGRectMake(3, 2, 4, 6); - CGRect innerClipping2 = CGRectMake(2, 3, 6, 4); - CGRect outterClipping = CGRectMake(2, 2, 6, 6); - for (int i = 0; i < 10; i++) { - for (int j = 0; j < 10; j++) { - CGPoint point = CGPointMake(i, j); - int alpha = [self alphaOfPoint:CGPointMake(i, j) onView:mockFlutterView]; - if (CGRectContainsPoint(innerClipping1, point) || - CGRectContainsPoint(innerClipping2, point)) { - // Pixels inside either of the 2 inner clippings should be fully opaque. - XCTAssertEqual(alpha, 255); - } else if (CGRectContainsPoint(outterClipping, point)) { - // Corner pixels (i.e. (2, 2), (2, 7), (7, 2) and (7, 7)) should be partially transparent. - XCTAssert(0 < alpha && alpha < 255); - } else { - // Pixels outside outterClipping should be fully transparent. - XCTAssertEqual(alpha, 0); - } - } - } -} - -- (void)testSetFlutterViewControllerAfterCreateCanStillDispatchTouchEvents { - 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(); - auto platform_view = std::make_unique( - /*delegate=*/mock_delegate, - /*rendering_api=*/mock_delegate.settings_.enable_impeller - ? flutter::IOSRenderingAPI::kMetal - : flutter::IOSRenderingAPI::kSoftware, - /*platform_views_controller=*/flutterPlatformViewsController, - /*task_runners=*/runners, - /*worker_task_runner=*/nil, - /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - - FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = - [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; - flutterPlatformViewsController->RegisterViewFactory( - factory, @"MockFlutterPlatformView", - FlutterPlatformViewGestureRecognizersBlockingPolicyEager); - FlutterResult result = ^(id result) { - }; - flutterPlatformViewsController->OnMethodCall( - [FlutterMethodCall - methodCallWithMethodName:@"create" - arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], - result); - - XCTAssertNotNil(gMockPlatformView); - - // Find touch inteceptor view - UIView* touchInteceptorView = gMockPlatformView; - while (touchInteceptorView != nil && - ![touchInteceptorView isKindOfClass:[FlutterTouchInterceptingView class]]) { - touchInteceptorView = touchInteceptorView.superview; - } - XCTAssertNotNil(touchInteceptorView); - - // Find ForwardGestureRecognizer - UIGestureRecognizer* forwardGectureRecognizer = nil; - for (UIGestureRecognizer* gestureRecognizer in touchInteceptorView.gestureRecognizers) { - if ([gestureRecognizer isKindOfClass:NSClassFromString(@"ForwardingGestureRecognizer")]) { - forwardGectureRecognizer = gestureRecognizer; - break; - } - } - - // Before setting flutter view controller, events are not dispatched. - NSSet* touches1 = [[NSSet alloc] init]; - id event1 = OCMClassMock([UIEvent class]); - id mockFlutterViewContoller = OCMClassMock([FlutterViewController class]); - [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1]; - OCMReject([mockFlutterViewContoller touchesBegan:touches1 withEvent:event1]); - - // Set flutter view controller allows events to be dispatched. - NSSet* touches2 = [[NSSet alloc] init]; - id event2 = OCMClassMock([UIEvent class]); - flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller); - [forwardGectureRecognizer touchesBegan:touches2 withEvent:event2]; - OCMVerify([mockFlutterViewContoller touchesBegan:touches2 withEvent:event2]); -} - -- (void)testSetFlutterViewControllerInTheMiddleOfTouchEventShouldStillAllowGesturesToBeHandled { - 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(); - auto platform_view = std::make_unique( - /*delegate=*/mock_delegate, - /*rendering_api=*/mock_delegate.settings_.enable_impeller - ? flutter::IOSRenderingAPI::kMetal - : flutter::IOSRenderingAPI::kSoftware, - /*platform_views_controller=*/flutterPlatformViewsController, - /*task_runners=*/runners, - /*worker_task_runner=*/nil, - /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - - FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = - [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; - flutterPlatformViewsController->RegisterViewFactory( - factory, @"MockFlutterPlatformView", - FlutterPlatformViewGestureRecognizersBlockingPolicyEager); - FlutterResult result = ^(id result) { - }; - flutterPlatformViewsController->OnMethodCall( - [FlutterMethodCall - methodCallWithMethodName:@"create" - arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], - result); - - XCTAssertNotNil(gMockPlatformView); - - // Find touch inteceptor view - UIView* touchInteceptorView = gMockPlatformView; - while (touchInteceptorView != nil && - ![touchInteceptorView isKindOfClass:[FlutterTouchInterceptingView class]]) { - touchInteceptorView = touchInteceptorView.superview; - } - XCTAssertNotNil(touchInteceptorView); - - // Find ForwardGestureRecognizer - UIGestureRecognizer* forwardGectureRecognizer = nil; - for (UIGestureRecognizer* gestureRecognizer in touchInteceptorView.gestureRecognizers) { - if ([gestureRecognizer isKindOfClass:NSClassFromString(@"ForwardingGestureRecognizer")]) { - forwardGectureRecognizer = gestureRecognizer; - break; - } - } - id mockFlutterViewContoller = OCMClassMock([FlutterViewController class]); - { - // ***** Sequence 1, finishing touch event with touchEnded ***** // - flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller); - - NSSet* touches1 = [[NSSet alloc] init]; - id event1 = OCMClassMock([UIEvent class]); - [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1]; - OCMVerify([mockFlutterViewContoller touchesBegan:touches1 withEvent:event1]); - - flutterPlatformViewsController->SetFlutterViewController(nil); - - // Allow the touch events to finish - NSSet* touches2 = [[NSSet alloc] init]; - id event2 = OCMClassMock([UIEvent class]); - [forwardGectureRecognizer touchesMoved:touches2 withEvent:event2]; - OCMVerify([mockFlutterViewContoller touchesMoved:touches2 withEvent:event2]); - - NSSet* touches3 = [[NSSet alloc] init]; - id event3 = OCMClassMock([UIEvent class]); - [forwardGectureRecognizer touchesEnded:touches3 withEvent:event3]; - OCMVerify([mockFlutterViewContoller touchesEnded:touches3 withEvent:event3]); - - // Now the 2nd touch sequence should not be allowed. - NSSet* touches4 = [[NSSet alloc] init]; - id event4 = OCMClassMock([UIEvent class]); - [forwardGectureRecognizer touchesBegan:touches4 withEvent:event4]; - OCMReject([mockFlutterViewContoller touchesBegan:touches4 withEvent:event4]); - - NSSet* touches5 = [[NSSet alloc] init]; - id event5 = OCMClassMock([UIEvent class]); - [forwardGectureRecognizer touchesEnded:touches5 withEvent:event5]; - OCMReject([mockFlutterViewContoller touchesEnded:touches5 withEvent:event5]); - } - - { - // ***** Sequence 2, finishing touch event with touchCancelled ***** // - flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller); - - NSSet* touches1 = [[NSSet alloc] init]; - id event1 = OCMClassMock([UIEvent class]); - [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1]; - OCMVerify([mockFlutterViewContoller touchesBegan:touches1 withEvent:event1]); - - flutterPlatformViewsController->SetFlutterViewController(nil); - - // Allow the touch events to finish - NSSet* touches2 = [[NSSet alloc] init]; - id event2 = OCMClassMock([UIEvent class]); - [forwardGectureRecognizer touchesMoved:touches2 withEvent:event2]; - OCMVerify([mockFlutterViewContoller touchesMoved:touches2 withEvent:event2]); - - NSSet* touches3 = [[NSSet alloc] init]; - id event3 = OCMClassMock([UIEvent class]); - [forwardGectureRecognizer touchesCancelled:touches3 withEvent:event3]; - OCMVerify([mockFlutterViewContoller forceTouchesCancelled:touches3]); - - // Now the 2nd touch sequence should not be allowed. - NSSet* touches4 = [[NSSet alloc] init]; - id event4 = OCMClassMock([UIEvent class]); - [forwardGectureRecognizer touchesBegan:touches4 withEvent:event4]; - OCMReject([mockFlutterViewContoller touchesBegan:touches4 withEvent:event4]); - - NSSet* touches5 = [[NSSet alloc] init]; - id event5 = OCMClassMock([UIEvent class]); - [forwardGectureRecognizer touchesEnded:touches5 withEvent:event5]; - OCMReject([mockFlutterViewContoller touchesEnded:touches5 withEvent:event5]); - } - - flutterPlatformViewsController->Reset(); -} - -- (void) - testSetFlutterViewControllerInTheMiddleOfTouchEventAllowsTheNewControllerToHandleSecondTouchSequence { - 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(); - auto platform_view = std::make_unique( - /*delegate=*/mock_delegate, - /*rendering_api=*/mock_delegate.settings_.enable_impeller - ? flutter::IOSRenderingAPI::kMetal - : flutter::IOSRenderingAPI::kSoftware, - /*platform_views_controller=*/flutterPlatformViewsController, - /*task_runners=*/runners, - /*worker_task_runner=*/nil, - /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - - FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = - [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; - flutterPlatformViewsController->RegisterViewFactory( - factory, @"MockFlutterPlatformView", - FlutterPlatformViewGestureRecognizersBlockingPolicyEager); - FlutterResult result = ^(id result) { - }; - flutterPlatformViewsController->OnMethodCall( - [FlutterMethodCall - methodCallWithMethodName:@"create" - arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], - result); - - XCTAssertNotNil(gMockPlatformView); - - // Find touch inteceptor view - UIView* touchInteceptorView = gMockPlatformView; - while (touchInteceptorView != nil && - ![touchInteceptorView isKindOfClass:[FlutterTouchInterceptingView class]]) { - touchInteceptorView = touchInteceptorView.superview; - } - XCTAssertNotNil(touchInteceptorView); - - // Find ForwardGestureRecognizer - UIGestureRecognizer* forwardGectureRecognizer = nil; - for (UIGestureRecognizer* gestureRecognizer in touchInteceptorView.gestureRecognizers) { - if ([gestureRecognizer isKindOfClass:NSClassFromString(@"ForwardingGestureRecognizer")]) { - forwardGectureRecognizer = gestureRecognizer; - break; - } - } - id mockFlutterViewContoller = OCMClassMock([FlutterViewController class]); - - flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller); - - // The touches in this sequence requires 1 touch object, we always create the NSSet with one item. - NSSet* touches1 = [NSSet setWithObject:@1]; - id event1 = OCMClassMock([UIEvent class]); - [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1]; - OCMVerify([mockFlutterViewContoller touchesBegan:touches1 withEvent:event1]); - - FlutterViewController* mockFlutterViewContoller2 = OCMClassMock([FlutterViewController class]); - flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller2); - - // Touch events should still send to the old FlutterViewController if FlutterViewController - // is updated in between. - NSSet* touches2 = [NSSet setWithObject:@1]; - id event2 = OCMClassMock([UIEvent class]); - [forwardGectureRecognizer touchesBegan:touches2 withEvent:event2]; - OCMVerify([mockFlutterViewContoller touchesBegan:touches2 withEvent:event2]); - OCMReject([mockFlutterViewContoller2 touchesBegan:touches2 withEvent:event2]); - - NSSet* touches3 = [NSSet setWithObject:@1]; - id event3 = OCMClassMock([UIEvent class]); - [forwardGectureRecognizer touchesMoved:touches3 withEvent:event3]; - OCMVerify([mockFlutterViewContoller touchesMoved:touches3 withEvent:event3]); - OCMReject([mockFlutterViewContoller2 touchesMoved:touches3 withEvent:event3]); - - NSSet* touches4 = [NSSet setWithObject:@1]; - id event4 = OCMClassMock([UIEvent class]); - [forwardGectureRecognizer touchesEnded:touches4 withEvent:event4]; - OCMVerify([mockFlutterViewContoller touchesEnded:touches4 withEvent:event4]); - OCMReject([mockFlutterViewContoller2 touchesEnded:touches4 withEvent:event4]); - - NSSet* touches5 = [NSSet setWithObject:@1]; - id event5 = OCMClassMock([UIEvent class]); - [forwardGectureRecognizer touchesEnded:touches5 withEvent:event5]; - OCMVerify([mockFlutterViewContoller touchesEnded:touches5 withEvent:event5]); - OCMReject([mockFlutterViewContoller2 touchesEnded:touches5 withEvent:event5]); - - // Now the 2nd touch sequence should go to the new FlutterViewController - - NSSet* touches6 = [NSSet setWithObject:@1]; - id event6 = OCMClassMock([UIEvent class]); - [forwardGectureRecognizer touchesBegan:touches6 withEvent:event6]; - OCMVerify([mockFlutterViewContoller2 touchesBegan:touches6 withEvent:event6]); - OCMReject([mockFlutterViewContoller touchesBegan:touches6 withEvent:event6]); - - // Allow the touch events to finish - NSSet* touches7 = [NSSet setWithObject:@1]; - id event7 = OCMClassMock([UIEvent class]); - [forwardGectureRecognizer touchesMoved:touches7 withEvent:event7]; - OCMVerify([mockFlutterViewContoller2 touchesMoved:touches7 withEvent:event7]); - OCMReject([mockFlutterViewContoller touchesMoved:touches7 withEvent:event7]); - - NSSet* touches8 = [NSSet setWithObject:@1]; - id event8 = OCMClassMock([UIEvent class]); - [forwardGectureRecognizer touchesEnded:touches8 withEvent:event8]; - OCMVerify([mockFlutterViewContoller2 touchesEnded:touches8 withEvent:event8]); - OCMReject([mockFlutterViewContoller touchesEnded:touches8 withEvent:event8]); - - flutterPlatformViewsController->Reset(); -} - -- (void)testFlutterPlatformViewTouchesCancelledEventAreForcedToBeCancelled { - 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(); - auto platform_view = std::make_unique( - /*delegate=*/mock_delegate, - /*rendering_api=*/mock_delegate.settings_.enable_impeller - ? flutter::IOSRenderingAPI::kMetal - : flutter::IOSRenderingAPI::kSoftware, - /*platform_views_controller=*/flutterPlatformViewsController, - /*task_runners=*/runners, - /*worker_task_runner=*/nil, - /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - - FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = - [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; - flutterPlatformViewsController->RegisterViewFactory( - factory, @"MockFlutterPlatformView", - FlutterPlatformViewGestureRecognizersBlockingPolicyEager); - FlutterResult result = ^(id result) { - }; - flutterPlatformViewsController->OnMethodCall( - [FlutterMethodCall - methodCallWithMethodName:@"create" - arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], - result); - - XCTAssertNotNil(gMockPlatformView); - - // Find touch inteceptor view - UIView* touchInteceptorView = gMockPlatformView; - while (touchInteceptorView != nil && - ![touchInteceptorView isKindOfClass:[FlutterTouchInterceptingView class]]) { - touchInteceptorView = touchInteceptorView.superview; - } - XCTAssertNotNil(touchInteceptorView); - - // Find ForwardGestureRecognizer - UIGestureRecognizer* forwardGectureRecognizer = nil; - for (UIGestureRecognizer* gestureRecognizer in touchInteceptorView.gestureRecognizers) { - if ([gestureRecognizer isKindOfClass:NSClassFromString(@"ForwardingGestureRecognizer")]) { - forwardGectureRecognizer = gestureRecognizer; - break; - } - } - id mockFlutterViewContoller = OCMClassMock([FlutterViewController class]); - - flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller); - - NSSet* touches1 = [NSSet setWithObject:@1]; - id event1 = OCMClassMock([UIEvent class]); - [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1]; - - [forwardGectureRecognizer touchesCancelled:touches1 withEvent:event1]; - OCMVerify([mockFlutterViewContoller forceTouchesCancelled:touches1]); - - flutterPlatformViewsController->Reset(); -} - -- (void)testFlutterPlatformViewControllerSubmitFrameWithoutFlutterViewNotCrashing { - 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(); - auto platform_view = std::make_unique( - /*delegate=*/mock_delegate, - /*rendering_api=*/mock_delegate.settings_.enable_impeller - ? flutter::IOSRenderingAPI::kMetal - : flutter::IOSRenderingAPI::kSoftware, - /*platform_views_controller=*/flutterPlatformViewsController, - /*task_runners=*/runners, - /*worker_task_runner=*/nil, - /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - - FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = - [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; - flutterPlatformViewsController->RegisterViewFactory( - factory, @"MockFlutterPlatformView", - FlutterPlatformViewGestureRecognizersBlockingPolicyEager); - FlutterResult result = ^(id result) { - }; - flutterPlatformViewsController->OnMethodCall( - [FlutterMethodCall - methodCallWithMethodName:@"create" - arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], - result); - - XCTAssertNotNil(gMockPlatformView); - - // Create embedded view params - flutter::MutatorsStack stack; - SkMatrix finalMatrix; - - auto embeddedViewParams_1 = - std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); - - flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams_1)); - flutterPlatformViewsController->CompositeEmbeddedView(2); - flutter::SurfaceFrame::FramebufferInfo framebuffer_info; - auto mock_surface = std::make_unique( - nullptr, framebuffer_info, - [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return false; }, - /*frame_size=*/SkISize::Make(800, 600)); - XCTAssertFalse( - flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface))); - - auto embeddedViewParams_2 = - std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); - flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams_2)); - flutterPlatformViewsController->CompositeEmbeddedView(2); - auto mock_surface_submit_true = std::make_unique( - nullptr, 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_submit_true))); -} - -- (void) - testFlutterPlatformViewControllerResetDeallocsPlatformViewWhenRootViewsNotBindedToFlutterView { - 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(); - auto platform_view = std::make_unique( - /*delegate=*/mock_delegate, - /*rendering_api=*/mock_delegate.settings_.enable_impeller - ? flutter::IOSRenderingAPI::kMetal - : flutter::IOSRenderingAPI::kSoftware, - /*platform_views_controller=*/flutterPlatformViewsController, - /*task_runners=*/runners, - /*worker_task_runner=*/nil, - /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); - - FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = - [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; - flutterPlatformViewsController->RegisterViewFactory( - factory, @"MockFlutterPlatformView", - FlutterPlatformViewGestureRecognizersBlockingPolicyEager); - FlutterResult result = ^(id result) { - }; - // autorelease pool to trigger an autorelease for all the root_views_ and touch_interceptors_. - @autoreleasepool { - flutterPlatformViewsController->OnMethodCall( - [FlutterMethodCall - methodCallWithMethodName:@"create" - arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], - result); - - flutter::MutatorsStack stack; - SkMatrix finalMatrix; - auto embeddedViewParams = - std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); - flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); - // Not calling |flutterPlatformViewsController::SubmitFrame| so that the platform views are not - // added to flutter_view_. - - XCTAssertNotNil(gMockPlatformView); - flutterPlatformViewsController->Reset(); - } - XCTAssertNil(gMockPlatformView); -} - -- (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { - 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(); - auto platform_view = std::make_unique( - /*delegate=*/mock_delegate, - /*rendering_api=*/mock_delegate.settings_.enable_impeller - ? flutter::IOSRenderingAPI::kMetal - : flutter::IOSRenderingAPI::kSoftware, - /*platform_views_controller=*/flutterPlatformViewsController, - /*task_runners=*/runners, - /*worker_task_runner=*/nil, - /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); - - FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = - [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; - flutterPlatformViewsController->RegisterViewFactory( - factory, @"MockFlutterPlatformView", - FlutterPlatformViewGestureRecognizersBlockingPolicyEager); - FlutterResult result = ^(id result) { - }; - - flutterPlatformViewsController->OnMethodCall( - [FlutterMethodCall - methodCallWithMethodName:@"create" - arguments:@{@"id" : @0, @"viewType" : @"MockFlutterPlatformView"}], - result); - - // First frame, |EmbeddedViewCount| is not empty after composite. - flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300)); - flutter::MutatorsStack stack; - SkMatrix finalMatrix; - auto embeddedViewParams1 = - std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); - flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1)); - flutterPlatformViewsController->CompositeEmbeddedView(0); - XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 1UL); - - // Second frame, |EmbeddedViewCount| should be empty at the start - flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300)); - XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 0UL); - - auto embeddedViewParams2 = - std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); - flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams2)); - flutterPlatformViewsController->CompositeEmbeddedView(0); - XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 1UL); -} - -- (void) - testFlutterPlatformViewControllerSubmitFrameShouldOrderSubviewsCorrectlyWithDifferentViewHierarchy { - 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(); - auto platform_view = std::make_unique( - /*delegate=*/mock_delegate, - /*rendering_api=*/mock_delegate.settings_.enable_impeller - ? flutter::IOSRenderingAPI::kMetal - : flutter::IOSRenderingAPI::kSoftware, - /*platform_views_controller=*/flutterPlatformViewsController, - /*task_runners=*/runners, - /*worker_task_runner=*/nil, - /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); - - FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = - [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; - 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(finalMatrix, SkSize::Make(300, 300), stack); - flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1)); - flutterPlatformViewsController->CompositeEmbeddedView(0); - auto embeddedViewParams2 = - std::make_unique(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 mock_sk_surface = SkSurfaces::Raster(image_info); - flutter::SurfaceFrame::FramebufferInfo framebuffer_info; - auto mock_surface = std::make_unique( - 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 opposite order. - embeddedViewParams2 = - std::make_unique(finalMatrix, SkSize::Make(500, 500), stack); - flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams2)); - flutterPlatformViewsController->CompositeEmbeddedView(1); - embeddedViewParams1 = - std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); - flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1)); - flutterPlatformViewsController->CompositeEmbeddedView(0); - - mock_sk_surface = SkSurfaces::Raster(image_info); - mock_surface = std::make_unique( - 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 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(); - auto platform_view = std::make_unique( - /*delegate=*/mock_delegate, - /*rendering_api=*/mock_delegate.settings_.enable_impeller - ? flutter::IOSRenderingAPI::kMetal - : flutter::IOSRenderingAPI::kSoftware, - /*platform_views_controller=*/flutterPlatformViewsController, - /*task_runners=*/runners, - /*worker_task_runner=*/nil, - /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); - - FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = - [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; - 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(finalMatrix, SkSize::Make(300, 300), stack); - flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1)); - flutterPlatformViewsController->CompositeEmbeddedView(0); - auto embeddedViewParams2 = - std::make_unique(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 mock_sk_surface = SkSurfaces::Raster(image_info); - flutter::SurfaceFrame::FramebufferInfo framebuffer_info; - auto mock_surface = std::make_unique( - 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(finalMatrix, SkSize::Make(300, 300), stack); - flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1)); - flutterPlatformViewsController->CompositeEmbeddedView(0); - embeddedViewParams2 = - std::make_unique(finalMatrix, SkSize::Make(500, 500), stack); - flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams2)); - flutterPlatformViewsController->CompositeEmbeddedView(1); - - mock_sk_surface = SkSurfaces::Raster(image_info); - mock_surface = std::make_unique( - 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"); - auto thread_task_runner_other = CreateNewThread("FlutterPlatformViewsTest2"); - flutter::TaskRunners runners(/*label=*/self.name.UTF8String, - /*platform=*/thread_task_runner_platform, - /*raster=*/thread_task_runner_other, - /*ui=*/thread_task_runner_other, - /*io=*/thread_task_runner_other); - auto flutterPlatformViewsController = std::make_shared(); - auto platform_view = std::make_unique( - /*delegate=*/mock_delegate, - /*rendering_api=*/mock_delegate.settings_.enable_impeller - ? flutter::IOSRenderingAPI::kMetal - : flutter::IOSRenderingAPI::kSoftware, - /*platform_views_controller=*/flutterPlatformViewsController, - /*task_runners=*/runners, - /*worker_task_runner=*/nil, - /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); - - FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = - [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; - flutterPlatformViewsController->RegisterViewFactory( - factory, @"MockFlutterPlatformView", - FlutterPlatformViewGestureRecognizersBlockingPolicyEager); - XCTestExpectation* waitForPlatformView = - [self expectationWithDescription:@"wait for platform view to be created"]; - FlutterResult result = ^(id result) { - [waitForPlatformView fulfill]; - }; - - flutterPlatformViewsController->OnMethodCall( - [FlutterMethodCall - methodCallWithMethodName:@"create" - arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], - result); - [self waitForExpectations:@[ waitForPlatformView ] timeout:30]; - XCTAssertNotNil(gMockPlatformView); - - flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300)); - SkMatrix finalMatrix; - flutter::MutatorsStack stack; - auto embeddedViewParams = - std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); - flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - - fml::RefPtr raster_thread_merger = - fml::MakeRefCounted(thread_task_runner_platform->GetTaskQueueId(), - thread_task_runner_other->GetTaskQueueId()); - XCTAssertEqual(flutterPlatformViewsController->PostPrerollAction(raster_thread_merger), - flutter::PostPrerollResult::kSkipAndRetryFrame); - XCTAssertFalse(raster_thread_merger->IsMerged()); - - flutterPlatformViewsController->EndFrame(true, raster_thread_merger); - XCTAssertTrue(raster_thread_merger->IsMerged()); - - // Unmerge threads before the end of the test - // TaskRunners are required to be unmerged before destruction. - while (raster_thread_merger->DecrementLease() != fml::RasterThreadStatus::kUnmergedNow) { - } -} - -- (int)alphaOfPoint:(CGPoint)point onView:(UIView*)view { - unsigned char pixel[4] = {0}; - - CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); - - // Draw the pixel on `point` in the context. - CGContextRef context = CGBitmapContextCreate( - pixel, 1, 1, 8, 4, colorSpace, kCGBitmapAlphaInfoMask & kCGImageAlphaPremultipliedLast); - CGContextTranslateCTM(context, -point.x, -point.y); - [view.layer renderInContext:context]; - - CGContextRelease(context); - CGColorSpaceRelease(colorSpace); - // Get the alpha from the pixel that we just rendered. - return pixel[3]; -} - -- (void)testHasFirstResponderInViewHierarchySubtree_viewItselfBecomesFirstResponder { - // For view to become the first responder, it must be a descendant of a UIWindow - UIWindow* window = [[UIWindow alloc] init]; - UITextField* textField = [[UITextField alloc] init]; - [window addSubview:textField]; - - [textField becomeFirstResponder]; - XCTAssertTrue(textField.isFirstResponder); - XCTAssertTrue(textField.flt_hasFirstResponderInViewHierarchySubtree); - [textField resignFirstResponder]; - XCTAssertFalse(textField.isFirstResponder); - XCTAssertFalse(textField.flt_hasFirstResponderInViewHierarchySubtree); -} - -- (void)testHasFirstResponderInViewHierarchySubtree_descendantViewBecomesFirstResponder { - // For view to become the first responder, it must be a descendant of a UIWindow - UIWindow* window = [[UIWindow alloc] init]; - UIView* view = [[UIView alloc] init]; - UIView* childView = [[UIView alloc] init]; - UITextField* textField = [[UITextField alloc] init]; - [window addSubview:view]; - [view addSubview:childView]; - [childView addSubview:textField]; - - [textField becomeFirstResponder]; - XCTAssertTrue(textField.isFirstResponder); - XCTAssertTrue(view.flt_hasFirstResponderInViewHierarchySubtree); - [textField resignFirstResponder]; - XCTAssertFalse(textField.isFirstResponder); - XCTAssertFalse(view.flt_hasFirstResponderInViewHierarchySubtree); -} - -- (void)testFlutterClippingMaskViewPoolReuseViewsAfterRecycle { - FlutterClippingMaskViewPool* pool = [[FlutterClippingMaskViewPool alloc] initWithCapacity:2]; - FlutterClippingMaskView* view1 = [pool getMaskViewWithFrame:CGRectZero]; - FlutterClippingMaskView* view2 = [pool getMaskViewWithFrame:CGRectZero]; - [pool insertViewToPoolIfNeeded:view1]; - [pool insertViewToPoolIfNeeded:view2]; - CGRect newRect = CGRectMake(0, 0, 10, 10); - FlutterClippingMaskView* view3 = [pool getMaskViewWithFrame:newRect]; - FlutterClippingMaskView* view4 = [pool getMaskViewWithFrame:newRect]; - // view3 and view4 should randomly get either of view1 and view2. - NSSet* set1 = [NSSet setWithObjects:view1, view2, nil]; - NSSet* set2 = [NSSet setWithObjects:view3, view4, nil]; - XCTAssertEqualObjects(set1, set2); - XCTAssertTrue(CGRectEqualToRect(view3.frame, newRect)); - XCTAssertTrue(CGRectEqualToRect(view4.frame, newRect)); -} - -- (void)testFlutterClippingMaskViewPoolAllocsNewMaskViewsAfterReachingCapacity { - FlutterClippingMaskViewPool* pool = [[FlutterClippingMaskViewPool alloc] initWithCapacity:2]; - FlutterClippingMaskView* view1 = [pool getMaskViewWithFrame:CGRectZero]; - FlutterClippingMaskView* view2 = [pool getMaskViewWithFrame:CGRectZero]; - FlutterClippingMaskView* view3 = [pool getMaskViewWithFrame:CGRectZero]; - XCTAssertNotEqual(view1, view3); - XCTAssertNotEqual(view2, view3); -} - -- (void)testMaskViewsReleasedWhenPoolIsReleased { - __weak UIView* weakView; - @autoreleasepool { - FlutterClippingMaskViewPool* pool = [[FlutterClippingMaskViewPool alloc] initWithCapacity:2]; - FlutterClippingMaskView* view = [pool getMaskViewWithFrame:CGRectZero]; - weakView = view; - XCTAssertNotNil(weakView); - } - XCTAssertNil(weakView); -} - -- (void)testClipMaskViewIsReused { - 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(); - auto platform_view = std::make_unique( - /*delegate=*/mock_delegate, - /*rendering_api=*/mock_delegate.settings_.enable_impeller - ? flutter::IOSRenderingAPI::kMetal - : flutter::IOSRenderingAPI::kSoftware, - /*platform_views_controller=*/flutterPlatformViewsController, - /*task_runners=*/runners, - /*worker_task_runner=*/nil, - /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - - FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = - [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; - flutterPlatformViewsController->RegisterViewFactory( - factory, @"MockFlutterPlatformView", - FlutterPlatformViewGestureRecognizersBlockingPolicyEager); - FlutterResult result = ^(id result) { - }; - flutterPlatformViewsController->OnMethodCall( - [FlutterMethodCall - methodCallWithMethodName:@"create" - arguments:@{@"id" : @1, @"viewType" : @"MockFlutterPlatformView"}], - result); - - XCTAssertNotNil(gMockPlatformView); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); - // Create embedded view params - flutter::MutatorsStack stack1; - // Layer tree always pushes a screen scale factor to the stack - SkMatrix screenScaleMatrix = - SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); - stack1.PushTransform(screenScaleMatrix); - // Push a clip rect - SkRect rect = SkRect::MakeXYWH(2, 2, 3, 3); - stack1.PushClipRect(rect); - - auto embeddedViewParams1 = std::make_unique( - screenScaleMatrix, SkSize::Make(10, 10), stack1); - - flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1)); - flutterPlatformViewsController->CompositeEmbeddedView(1); - UIView* childClippingView1 = gMockPlatformView.superview.superview; - UIView* maskView1 = childClippingView1.maskView; - XCTAssertNotNil(maskView1); - - // Composite a new frame. - flutterPlatformViewsController->BeginFrame(SkISize::Make(100, 100)); - flutter::MutatorsStack stack2; - auto embeddedViewParams2 = std::make_unique( - screenScaleMatrix, SkSize::Make(10, 10), stack2); - auto embeddedViewParams3 = std::make_unique( - screenScaleMatrix, SkSize::Make(10, 10), stack2); - flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams3)); - flutterPlatformViewsController->CompositeEmbeddedView(1); - childClippingView1 = gMockPlatformView.superview.superview; - - // This overrides gMockPlatformView to point to the newly created platform view. - flutterPlatformViewsController->OnMethodCall( - [FlutterMethodCall - methodCallWithMethodName:@"create" - arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], - result); - - auto embeddedViewParams4 = std::make_unique( - screenScaleMatrix, SkSize::Make(10, 10), stack1); - flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams4)); - flutterPlatformViewsController->CompositeEmbeddedView(2); - UIView* childClippingView2 = gMockPlatformView.superview.superview; - - UIView* maskView2 = childClippingView2.maskView; - XCTAssertEqual(maskView1, maskView2); - XCTAssertNotNil(childClippingView2.maskView); - XCTAssertNil(childClippingView1.maskView); -} - -- (void)testDifferentClipMaskViewIsUsedForEachView { - 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(); - auto platform_view = std::make_unique( - /*delegate=*/mock_delegate, - /*rendering_api=*/mock_delegate.settings_.enable_impeller - ? flutter::IOSRenderingAPI::kMetal - : flutter::IOSRenderingAPI::kSoftware, - /*platform_views_controller=*/flutterPlatformViewsController, - /*task_runners=*/runners, - /*worker_task_runner=*/nil, - /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - - FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = - [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; - flutterPlatformViewsController->RegisterViewFactory( - factory, @"MockFlutterPlatformView", - FlutterPlatformViewGestureRecognizersBlockingPolicyEager); - FlutterResult result = ^(id result) { - }; - - flutterPlatformViewsController->OnMethodCall( - [FlutterMethodCall - methodCallWithMethodName:@"create" - arguments:@{@"id" : @1, @"viewType" : @"MockFlutterPlatformView"}], - result); - UIView* view1 = gMockPlatformView; - - // This overwrites `gMockPlatformView` to another view. - flutterPlatformViewsController->OnMethodCall( - [FlutterMethodCall - methodCallWithMethodName:@"create" - arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], - result); - UIView* view2 = gMockPlatformView; - - XCTAssertNotNil(gMockPlatformView); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); - // Create embedded view params - flutter::MutatorsStack stack1; - // Layer tree always pushes a screen scale factor to the stack - SkMatrix screenScaleMatrix = - SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); - stack1.PushTransform(screenScaleMatrix); - // Push a clip rect - SkRect rect = SkRect::MakeXYWH(2, 2, 3, 3); - stack1.PushClipRect(rect); - - auto embeddedViewParams1 = std::make_unique( - screenScaleMatrix, SkSize::Make(10, 10), stack1); - - flutter::MutatorsStack stack2; - stack2.PushClipRect(rect); - auto embeddedViewParams2 = std::make_unique( - screenScaleMatrix, SkSize::Make(10, 10), stack2); - - flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1)); - flutterPlatformViewsController->CompositeEmbeddedView(1); - UIView* childClippingView1 = view1.superview.superview; - - flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams2)); - flutterPlatformViewsController->CompositeEmbeddedView(2); - UIView* childClippingView2 = view2.superview.superview; - UIView* maskView1 = childClippingView1.maskView; - UIView* maskView2 = childClippingView2.maskView; - XCTAssertNotEqual(maskView1, maskView2); -} - -- (void)testMaskViewUsesCAShapeLayerAsTheBackingLayer { - 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(); - auto platform_view = std::make_unique( - /*delegate=*/mock_delegate, - /*rendering_api=*/mock_delegate.settings_.enable_impeller - ? flutter::IOSRenderingAPI::kMetal - : flutter::IOSRenderingAPI::kSoftware, - /*platform_views_controller=*/flutterPlatformViewsController, - /*task_runners=*/runners, - /*worker_task_runner=*/nil, - /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - - FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = - [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; - flutterPlatformViewsController->RegisterViewFactory( - factory, @"MockFlutterPlatformView", - FlutterPlatformViewGestureRecognizersBlockingPolicyEager); - FlutterResult result = ^(id result) { - }; - - flutterPlatformViewsController->OnMethodCall( - [FlutterMethodCall - methodCallWithMethodName:@"create" - arguments:@{@"id" : @1, @"viewType" : @"MockFlutterPlatformView"}], - result); - - XCTAssertNotNil(gMockPlatformView); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); - // Create embedded view params - flutter::MutatorsStack stack1; - // Layer tree always pushes a screen scale factor to the stack - SkMatrix screenScaleMatrix = - SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); - stack1.PushTransform(screenScaleMatrix); - // Push a clip rect - SkRect rect = SkRect::MakeXYWH(2, 2, 3, 3); - stack1.PushClipRect(rect); - - auto embeddedViewParams1 = std::make_unique( - screenScaleMatrix, SkSize::Make(10, 10), stack1); - - flutter::MutatorsStack stack2; - stack2.PushClipRect(rect); - auto embeddedViewParams2 = std::make_unique( - screenScaleMatrix, SkSize::Make(10, 10), stack2); - - flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1)); - flutterPlatformViewsController->CompositeEmbeddedView(1); - UIView* childClippingView = gMockPlatformView.superview.superview; - - UIView* maskView = childClippingView.maskView; - XCTAssert([maskView.layer isKindOfClass:[CAShapeLayer class]], - @"Mask view must use CAShapeLayer as its backing layer."); -} - -// Return true if a correct visual effect view is found. It also implies all the validation in this -// method passes. -// -// There are two fail states for this method. 1. One of the XCTAssert method failed; or 2. No -// correct visual effect view found. -- (BOOL)validateOneVisualEffectView:(UIView*)visualEffectView - expectedFrame:(CGRect)frame - inputRadius:(CGFloat)inputRadius { - XCTAssertTrue(CGRectEqualToRect(visualEffectView.frame, frame)); - for (UIView* view in visualEffectView.subviews) { - if (![NSStringFromClass([view class]) hasSuffix:@"BackdropView"]) { - continue; - } - XCTAssertEqual(view.layer.filters.count, 1u); - NSObject* filter = view.layer.filters.firstObject; - - XCTAssertEqualObjects([filter valueForKey:@"name"], @"gaussianBlur"); - - NSObject* inputRadiusInFilter = [filter valueForKey:@"inputRadius"]; - XCTAssertTrue([inputRadiusInFilter isKindOfClass:[NSNumber class]] && - flutter::BlurRadiusEqualToBlurRadius(((NSNumber*)inputRadiusInFilter).floatValue, - inputRadius)); - return YES; - } - return NO; -} - -- (void)testDisposingViewInCompositionOrderDoNotCrash { - 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(); - auto platform_view = std::make_unique( - /*delegate=*/mock_delegate, - /*rendering_api=*/mock_delegate.settings_.enable_impeller - ? flutter::IOSRenderingAPI::kMetal - : flutter::IOSRenderingAPI::kSoftware, - /*platform_views_controller=*/flutterPlatformViewsController, - /*task_runners=*/runners, - /*worker_task_runner=*/nil, - /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); - - FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = - [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; - flutterPlatformViewsController->RegisterViewFactory( - factory, @"MockFlutterPlatformView", - FlutterPlatformViewGestureRecognizersBlockingPolicyEager); - FlutterResult result = ^(id result) { - }; - - flutterPlatformViewsController->OnMethodCall( - [FlutterMethodCall - methodCallWithMethodName:@"create" - arguments:@{@"id" : @0, @"viewType" : @"MockFlutterPlatformView"}], - result); - flutterPlatformViewsController->OnMethodCall( - [FlutterMethodCall - methodCallWithMethodName:@"create" - arguments:@{@"id" : @1, @"viewType" : @"MockFlutterPlatformView"}], - result); - - { - // **** First frame, view id 0, 1 in the composition_order_, disposing view 0 is called. **** // - // No view should be disposed, or removed from the composition order. - flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300)); - flutter::MutatorsStack stack; - SkMatrix finalMatrix; - auto embeddedViewParams0 = - std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); - flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams0)); - flutterPlatformViewsController->CompositeEmbeddedView(0); - - auto embeddedViewParams1 = - std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); - flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1)); - flutterPlatformViewsController->CompositeEmbeddedView(1); - XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 2UL); - - XCTestExpectation* expectation = [self expectationWithDescription:@"dispose call ended."]; - FlutterResult disposeResult = ^(id result) { - [expectation fulfill]; - }; - - flutterPlatformViewsController->OnMethodCall( - [FlutterMethodCall methodCallWithMethodName:@"dispose" arguments:@0], disposeResult); - [self waitForExpectationsWithTimeout:30 handler:nil]; - - const SkImageInfo image_info = SkImageInfo::MakeN32Premul(1000, 1000); - sk_sp mock_sk_surface = SkSurfaces::Raster(image_info); - flutter::SurfaceFrame::FramebufferInfo framebuffer_info; - auto mock_surface = std::make_unique( - 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))); - - // Disposing won't remove embedded views until the view is removed from the composition_order_ - XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 2UL); - XCTAssertNotNil(flutterPlatformViewsController->GetPlatformViewByID(0)); - XCTAssertNotNil(flutterPlatformViewsController->GetPlatformViewByID(1)); - } - - { - // **** Second frame, view id 1 in the composition_order_, no disposing view is called, **** // - // View 0 is removed from the composition order in this frame, hence also disposed. - flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300)); - flutter::MutatorsStack stack; - SkMatrix finalMatrix; - auto embeddedViewParams1 = - std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); - flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1)); - flutterPlatformViewsController->CompositeEmbeddedView(1); - - const SkImageInfo image_info = SkImageInfo::MakeN32Premul(1000, 1000); - sk_sp mock_sk_surface = SkSurfaces::Raster(image_info); - flutter::SurfaceFrame::FramebufferInfo framebuffer_info; - auto mock_surface = std::make_unique( - 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))); - - // Disposing won't remove embedded views until the view is removed from the composition_order_ - XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 1UL); - XCTAssertNil(flutterPlatformViewsController->GetPlatformViewByID(0)); - XCTAssertNotNil(flutterPlatformViewsController->GetPlatformViewByID(1)); - } -} -- (void)testOnlyPlatformViewsAreRemovedWhenReset { - 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(); - auto platform_view = std::make_unique( - /*delegate=*/mock_delegate, - /*rendering_api=*/mock_delegate.settings_.enable_impeller - ? flutter::IOSRenderingAPI::kMetal - : flutter::IOSRenderingAPI::kSoftware, - /*platform_views_controller=*/flutterPlatformViewsController, - /*task_runners=*/runners, - /*worker_task_runner=*/nil, - /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - - FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = - [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; - flutterPlatformViewsController->RegisterViewFactory( - factory, @"MockFlutterPlatformView", - FlutterPlatformViewGestureRecognizersBlockingPolicyEager); - FlutterResult result = ^(id result) { - }; - flutterPlatformViewsController->OnMethodCall( - [FlutterMethodCall - methodCallWithMethodName:@"create" - arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], - result); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); - // Create embedded view params - flutter::MutatorsStack stack; - // Layer tree always pushes a screen scale factor to the stack - SkMatrix screenScaleMatrix = - SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); - stack.PushTransform(screenScaleMatrix); - // Push a translate matrix - SkMatrix translateMatrix = SkMatrix::Translate(100, 100); - stack.PushTransform(translateMatrix); - SkMatrix finalMatrix; - finalMatrix.setConcat(screenScaleMatrix, translateMatrix); - - auto embeddedViewParams = - std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); - - flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); - - // SKSurface is required if the root FlutterView is present. - const SkImageInfo image_info = SkImageInfo::MakeN32Premul(1000, 1000); - sk_sp mock_sk_surface = SkSurfaces::Raster(image_info); - flutter::SurfaceFrame::FramebufferInfo framebuffer_info; - auto mock_surface = std::make_unique( - std::move(mock_sk_surface), framebuffer_info, - [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; }, - /*frame_size=*/SkISize::Make(800, 600)); - - flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface)); - - UIView* someView = [[UIView alloc] init]; - [mockFlutterView addSubview:someView]; - - flutterPlatformViewsController->Reset(); - XCTAssertEqual(mockFlutterView.subviews.count, 1u); - XCTAssertEqual(mockFlutterView.subviews.firstObject, someView); -} - -- (void)testFlutterTouchInterceptingViewLinksToAccessibilityContainer { - FlutterTouchInterceptingView* touchInteceptorView = [[FlutterTouchInterceptingView alloc] init]; - NSObject* container = [[NSObject alloc] init]; - [touchInteceptorView setFlutterAccessibilityContainer:container]; - XCTAssertEqualObjects([touchInteceptorView accessibilityContainer], container); -} - -@end +// // Copyright 2013 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 +// #import +// #import + +// #import "flutter/fml/thread.h" +// #import "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h" +// #import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlatformViews.h" +// #import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h" +// #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h" +// #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterTouchInterceptingView_Test.h" +// #import "flutter/shell/platform/darwin/ios/platform_view_ios.h" + +// FLUTTER_ASSERT_ARC + +// @class FlutterPlatformViewsTestMockPlatformView; +// __weak static FlutterPlatformViewsTestMockPlatformView* gMockPlatformView = nil; +// const float kFloatCompareEpsilon = 0.001; + +// @interface FlutterPlatformViewsTestMockPlatformView : UIView +// @end +// @implementation FlutterPlatformViewsTestMockPlatformView + +// - (instancetype)init { +// self = [super init]; +// if (self) { +// gMockPlatformView = self; +// } +// return self; +// } + +// - (void)dealloc { +// gMockPlatformView = nil; +// } + +// @end + +// @interface FlutterPlatformViewsTestMockFlutterPlatformView : NSObject +// @property(nonatomic, strong) UIView* view; +// @property(nonatomic, assign) BOOL viewCreated; +// @end + +// @implementation FlutterPlatformViewsTestMockFlutterPlatformView + +// - (instancetype)init { +// if (self = [super init]) { +// _view = [[FlutterPlatformViewsTestMockPlatformView alloc] init]; +// _viewCreated = NO; +// } +// return self; +// } + +// - (UIView*)view { +// [self checkViewCreatedOnce]; +// return _view; +// } + +// - (void)checkViewCreatedOnce { +// if (self.viewCreated) { +// abort(); +// } +// self.viewCreated = YES; +// } + +// @end + +// @interface FlutterPlatformViewsTestMockFlutterPlatformFactory +// : NSObject +// @end + +// @implementation FlutterPlatformViewsTestMockFlutterPlatformFactory +// - (NSObject*)createWithFrame:(CGRect)frame +// viewIdentifier:(int64_t)viewId +// arguments:(id _Nullable)args { +// return [[FlutterPlatformViewsTestMockFlutterPlatformView alloc] init]; +// } + +// @end + +// namespace flutter { +// namespace { +// class FlutterPlatformViewsTestMockPlatformViewDelegate : public PlatformView::Delegate { +// public: +// void OnPlatformViewCreated(std::unique_ptr surface) override {} +// void OnPlatformViewDestroyed() override {} +// void OnPlatformViewScheduleFrame() override {} +// void OnPlatformViewAddView(int64_t view_id, +// const ViewportMetrics& viewport_metrics, +// AddViewCallback callback) override {} +// void OnPlatformViewRemoveView(int64_t view_id, RemoveViewCallback callback) override {} +// void OnPlatformViewSetNextFrameCallback(const fml::closure& closure) override {} +// void OnPlatformViewSetViewportMetrics(int64_t view_id, const ViewportMetrics& metrics) override {} +// const flutter::Settings& OnPlatformViewGetSettings() const override { return settings_; } +// void OnPlatformViewDispatchPlatformMessage(std::unique_ptr message) override {} +// void OnPlatformViewDispatchPointerDataPacket(std::unique_ptr packet) override { +// } +// void OnPlatformViewDispatchSemanticsAction(int32_t id, +// SemanticsAction action, +// fml::MallocMapping args) override {} +// void OnPlatformViewSetSemanticsEnabled(bool enabled) override {} +// void OnPlatformViewSetAccessibilityFeatures(int32_t flags) override {} +// void OnPlatformViewRegisterTexture(std::shared_ptr texture) override {} +// void OnPlatformViewUnregisterTexture(int64_t texture_id) override {} +// void OnPlatformViewMarkTextureFrameAvailable(int64_t texture_id) override {} + +// void LoadDartDeferredLibrary(intptr_t loading_unit_id, +// std::unique_ptr snapshot_data, +// std::unique_ptr snapshot_instructions) override { +// } +// void LoadDartDeferredLibraryError(intptr_t loading_unit_id, +// const std::string error_message, +// bool transient) override {} +// void UpdateAssetResolverByType(std::unique_ptr updated_asset_resolver, +// flutter::AssetResolver::AssetResolverType type) override {} + +// flutter::Settings settings_; +// }; + +// } // namespace +// } // namespace flutter + +// namespace { +// fml::RefPtr CreateNewThread(const std::string& name) { +// auto thread = std::make_unique(name); +// auto runner = thread->GetTaskRunner(); +// return runner; +// } +// } // namespace + +// @interface FlutterPlatformViewsTest : XCTestCase +// @end + +// @implementation FlutterPlatformViewsTest + +// - (void)testFlutterViewOnlyCreateOnceInOneFrame { +// 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(); +// auto platform_view = std::make_unique( +// /*delegate=*/mock_delegate, +// /*rendering_api=*/mock_delegate.settings_.enable_impeller +// ? flutter::IOSRenderingAPI::kMetal +// : flutter::IOSRenderingAPI::kSoftware, +// /*platform_views_controller=*/flutterPlatformViewsController, +// /*task_runners=*/runners, +// /*worker_task_runner=*/nil, +// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + +// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = +// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; +// flutterPlatformViewsController->RegisterViewFactory( +// factory, @"MockFlutterPlatformView", +// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); +// FlutterResult result = ^(id result) { +// }; +// flutterPlatformViewsController->OnMethodCall( +// [FlutterMethodCall +// methodCallWithMethodName:@"create" +// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], +// result); +// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; +// flutterPlatformViewsController->SetFlutterView(mockFlutterView); +// // Create embedded view params +// flutter::MutatorsStack stack; +// // Layer tree always pushes a screen scale factor to the stack +// SkMatrix screenScaleMatrix = +// SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); +// stack.PushTransform(screenScaleMatrix); +// // Push a translate matrix +// SkMatrix translateMatrix = SkMatrix::Translate(100, 100); +// stack.PushTransform(translateMatrix); +// SkMatrix finalMatrix; +// finalMatrix.setConcat(screenScaleMatrix, translateMatrix); + +// auto embeddedViewParams = +// std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); + +// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); +// flutterPlatformViewsController->CompositeEmbeddedView(2); + +// flutterPlatformViewsController->GetPlatformViewRect(2); + +// XCTAssertNotNil(gMockPlatformView); + +// flutterPlatformViewsController->Reset(); +// } + +// - (void)testCanCreatePlatformViewWithoutFlutterView { +// 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(); +// auto platform_view = std::make_unique( +// /*delegate=*/mock_delegate, +// /*rendering_api=*/mock_delegate.settings_.enable_impeller +// ? flutter::IOSRenderingAPI::kMetal +// : flutter::IOSRenderingAPI::kSoftware, +// /*platform_views_controller=*/flutterPlatformViewsController, +// /*task_runners=*/runners, +// /*worker_task_runner=*/nil, +// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + +// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = +// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; +// flutterPlatformViewsController->RegisterViewFactory( +// factory, @"MockFlutterPlatformView", +// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); +// FlutterResult result = ^(id result) { +// }; +// flutterPlatformViewsController->OnMethodCall( +// [FlutterMethodCall +// methodCallWithMethodName:@"create" +// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], +// result); + +// XCTAssertNotNil(gMockPlatformView); +// } + +// - (void)testChildClippingViewHitTests { +// ChildClippingView* childClippingView = +// [[ChildClippingView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; +// UIView* childView = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)]; +// [childClippingView addSubview:childView]; + +// XCTAssertFalse([childClippingView pointInside:CGPointMake(50, 50) withEvent:nil]); +// XCTAssertFalse([childClippingView pointInside:CGPointMake(99, 100) withEvent:nil]); +// XCTAssertFalse([childClippingView pointInside:CGPointMake(100, 99) withEvent:nil]); +// XCTAssertFalse([childClippingView pointInside:CGPointMake(201, 200) withEvent:nil]); +// XCTAssertFalse([childClippingView pointInside:CGPointMake(200, 201) withEvent:nil]); +// XCTAssertFalse([childClippingView pointInside:CGPointMake(99, 200) withEvent:nil]); +// XCTAssertFalse([childClippingView pointInside:CGPointMake(200, 299) withEvent:nil]); + +// XCTAssertTrue([childClippingView pointInside:CGPointMake(150, 150) withEvent:nil]); +// XCTAssertTrue([childClippingView pointInside:CGPointMake(100, 100) withEvent:nil]); +// XCTAssertTrue([childClippingView pointInside:CGPointMake(199, 100) withEvent:nil]); +// XCTAssertTrue([childClippingView pointInside:CGPointMake(100, 199) withEvent:nil]); +// XCTAssertTrue([childClippingView pointInside:CGPointMake(199, 199) withEvent:nil]); +// } + +// - (void)testReleasesBackdropFilterSubviewsOnChildClippingViewDealloc { +// __weak NSMutableArray* weakBackdropFilterSubviews = nil; +// __weak UIVisualEffectView* weakVisualEffectView1 = nil; +// __weak UIVisualEffectView* weakVisualEffectView2 = nil; + +// @autoreleasepool { +// ChildClippingView* clippingView = [[ChildClippingView alloc] initWithFrame:CGRectZero]; +// UIVisualEffectView* visualEffectView1 = [[UIVisualEffectView alloc] +// initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]]; +// weakVisualEffectView1 = visualEffectView1; +// PlatformViewFilter* platformViewFilter1 = +// [[PlatformViewFilter alloc] initWithFrame:CGRectMake(0, 0, 10, 10) +// blurRadius:5 +// visualEffectView:visualEffectView1]; + +// [clippingView applyBlurBackdropFilters:@[ platformViewFilter1 ]]; + +// // Replace the blur filter to validate the original and new UIVisualEffectView are released. +// UIVisualEffectView* visualEffectView2 = [[UIVisualEffectView alloc] +// initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleDark]]; +// weakVisualEffectView2 = visualEffectView2; +// PlatformViewFilter* platformViewFilter2 = +// [[PlatformViewFilter alloc] initWithFrame:CGRectMake(0, 0, 10, 10) +// blurRadius:5 +// visualEffectView:visualEffectView2]; +// [clippingView applyBlurBackdropFilters:@[ platformViewFilter2 ]]; + +// weakBackdropFilterSubviews = clippingView.backdropFilterSubviews; +// XCTAssertNotNil(weakBackdropFilterSubviews); +// clippingView = nil; +// } +// XCTAssertNil(weakBackdropFilterSubviews); +// XCTAssertNil(weakVisualEffectView1); +// XCTAssertNil(weakVisualEffectView2); +// } + +// - (void)testApplyBackdropFilter { +// 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(); +// auto platform_view = std::make_unique( +// /*delegate=*/mock_delegate, +// /*rendering_api=*/mock_delegate.settings_.enable_impeller +// ? flutter::IOSRenderingAPI::kMetal +// : flutter::IOSRenderingAPI::kSoftware, +// /*platform_views_controller=*/flutterPlatformViewsController, +// /*task_runners=*/runners, +// /*worker_task_runner=*/nil, +// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + +// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = +// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; +// flutterPlatformViewsController->RegisterViewFactory( +// factory, @"MockFlutterPlatformView", +// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); +// FlutterResult result = ^(id result) { +// }; +// flutterPlatformViewsController->OnMethodCall( +// [FlutterMethodCall +// methodCallWithMethodName:@"create" +// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], +// result); + +// XCTAssertNotNil(gMockPlatformView); + +// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; +// flutterPlatformViewsController->SetFlutterView(mockFlutterView); +// // Create embedded view params +// flutter::MutatorsStack stack; +// // Layer tree always pushes a screen scale factor to the stack +// CGFloat screenScale = [UIScreen mainScreen].scale; +// SkMatrix screenScaleMatrix = SkMatrix::Scale(screenScale, screenScale); +// stack.PushTransform(screenScaleMatrix); +// // Push a backdrop filter +// auto filter = std::make_shared(5, 2, flutter::DlTileMode::kClamp); +// stack.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); + +// auto embeddedViewParams = +// std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); + +// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); +// flutterPlatformViewsController->CompositeEmbeddedView(2); +// XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]); +// ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; +// [mockFlutterView addSubview:childClippingView]; + +// [mockFlutterView setNeedsLayout]; +// [mockFlutterView layoutIfNeeded]; + +// // childClippingView has visual effect view with the correct configurations. +// NSUInteger numberOfExpectedVisualEffectView = 0; +// for (UIView* subview in childClippingView.subviews) { +// if (![subview isKindOfClass:[UIVisualEffectView class]]) { +// continue; +// } +// XCTAssertLessThan(numberOfExpectedVisualEffectView, 1u); +// if ([self validateOneVisualEffectView:subview +// expectedFrame:CGRectMake(0, 0, 10, 10) +// inputRadius:5]) { +// numberOfExpectedVisualEffectView++; +// } +// } +// XCTAssertEqual(numberOfExpectedVisualEffectView, 1u); +// } + +// - (void)testApplyBackdropFilterWithCorrectFrame { +// 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(); +// auto platform_view = std::make_unique( +// /*delegate=*/mock_delegate, +// /*rendering_api=*/mock_delegate.settings_.enable_impeller +// ? flutter::IOSRenderingAPI::kMetal +// : flutter::IOSRenderingAPI::kSoftware, +// /*platform_views_controller=*/flutterPlatformViewsController, +// /*task_runners=*/runners, +// /*worker_task_runner=*/nil, +// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + +// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = +// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; +// flutterPlatformViewsController->RegisterViewFactory( +// factory, @"MockFlutterPlatformView", +// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); +// FlutterResult result = ^(id result) { +// }; +// flutterPlatformViewsController->OnMethodCall( +// [FlutterMethodCall +// methodCallWithMethodName:@"create" +// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], +// result); + +// XCTAssertNotNil(gMockPlatformView); + +// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; +// flutterPlatformViewsController->SetFlutterView(mockFlutterView); +// // Create embedded view params +// flutter::MutatorsStack stack; +// // Layer tree always pushes a screen scale factor to the stack +// CGFloat screenScale = [UIScreen mainScreen].scale; +// SkMatrix screenScaleMatrix = SkMatrix::Scale(screenScale, screenScale); +// stack.PushTransform(screenScaleMatrix); +// // Push a backdrop filter +// auto filter = std::make_shared(5, 2, flutter::DlTileMode::kClamp); +// stack.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 8, screenScale * 8)); + +// auto embeddedViewParams = +// std::make_unique(screenScaleMatrix, SkSize::Make(5, 10), stack); + +// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); +// flutterPlatformViewsController->CompositeEmbeddedView(2); +// XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]); +// ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; +// [mockFlutterView addSubview:childClippingView]; + +// [mockFlutterView setNeedsLayout]; +// [mockFlutterView layoutIfNeeded]; + +// // childClippingView has visual effect view with the correct configurations. +// NSUInteger numberOfExpectedVisualEffectView = 0; +// for (UIView* subview in childClippingView.subviews) { +// if (![subview isKindOfClass:[UIVisualEffectView class]]) { +// continue; +// } +// XCTAssertLessThan(numberOfExpectedVisualEffectView, 1u); +// if ([self validateOneVisualEffectView:subview +// expectedFrame:CGRectMake(0, 0, 5, 8) +// inputRadius:5]) { +// numberOfExpectedVisualEffectView++; +// } +// } +// XCTAssertEqual(numberOfExpectedVisualEffectView, 1u); +// } + +// - (void)testApplyMultipleBackdropFilters { +// 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(); +// auto platform_view = std::make_unique( +// /*delegate=*/mock_delegate, +// /*rendering_api=*/mock_delegate.settings_.enable_impeller +// ? flutter::IOSRenderingAPI::kMetal +// : flutter::IOSRenderingAPI::kSoftware, +// /*platform_views_controller=*/flutterPlatformViewsController, +// /*task_runners=*/runners, +// /*worker_task_runner=*/nil, +// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + +// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = +// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; +// flutterPlatformViewsController->RegisterViewFactory( +// factory, @"MockFlutterPlatformView", +// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); +// FlutterResult result = ^(id result) { +// }; +// flutterPlatformViewsController->OnMethodCall( +// [FlutterMethodCall +// methodCallWithMethodName:@"create" +// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], +// result); + +// XCTAssertNotNil(gMockPlatformView); + +// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; +// flutterPlatformViewsController->SetFlutterView(mockFlutterView); +// // Create embedded view params +// flutter::MutatorsStack stack; +// // Layer tree always pushes a screen scale factor to the stack +// CGFloat screenScale = [UIScreen mainScreen].scale; +// SkMatrix screenScaleMatrix = SkMatrix::Scale(screenScale, screenScale); +// stack.PushTransform(screenScaleMatrix); +// // Push backdrop filters +// for (int i = 0; i < 50; i++) { +// auto filter = std::make_shared(i, 2, flutter::DlTileMode::kClamp); +// stack.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); +// } + +// auto embeddedViewParams = +// std::make_unique(screenScaleMatrix, SkSize::Make(20, 20), stack); + +// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); +// flutterPlatformViewsController->CompositeEmbeddedView(2); +// XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); +// ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; +// [mockFlutterView addSubview:childClippingView]; + +// [mockFlutterView setNeedsLayout]; +// [mockFlutterView layoutIfNeeded]; + +// NSUInteger numberOfExpectedVisualEffectView = 0; +// for (UIView* subview in childClippingView.subviews) { +// if (![subview isKindOfClass:[UIVisualEffectView class]]) { +// continue; +// } +// XCTAssertLessThan(numberOfExpectedVisualEffectView, 50u); +// if ([self validateOneVisualEffectView:subview +// expectedFrame:CGRectMake(0, 0, 10, 10) +// inputRadius:(CGFloat)numberOfExpectedVisualEffectView]) { +// numberOfExpectedVisualEffectView++; +// } +// } +// XCTAssertEqual(numberOfExpectedVisualEffectView, (NSUInteger)numberOfExpectedVisualEffectView); +// } + +// - (void)testAddBackdropFilters { +// 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(); +// auto platform_view = std::make_unique( +// /*delegate=*/mock_delegate, +// /*rendering_api=*/mock_delegate.settings_.enable_impeller +// ? flutter::IOSRenderingAPI::kMetal +// : flutter::IOSRenderingAPI::kSoftware, +// /*platform_views_controller=*/flutterPlatformViewsController, +// /*task_runners=*/runners, +// /*worker_task_runner=*/nil, +// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + +// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = +// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; +// flutterPlatformViewsController->RegisterViewFactory( +// factory, @"MockFlutterPlatformView", +// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); +// FlutterResult result = ^(id result) { +// }; +// flutterPlatformViewsController->OnMethodCall( +// [FlutterMethodCall +// methodCallWithMethodName:@"create" +// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], +// result); + +// XCTAssertNotNil(gMockPlatformView); + +// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; +// flutterPlatformViewsController->SetFlutterView(mockFlutterView); +// // Create embedded view params +// flutter::MutatorsStack stack; +// // Layer tree always pushes a screen scale factor to the stack +// CGFloat screenScale = [UIScreen mainScreen].scale; +// SkMatrix screenScaleMatrix = SkMatrix::Scale(screenScale, screenScale); +// stack.PushTransform(screenScaleMatrix); +// // Push a backdrop filter +// auto filter = std::make_shared(5, 2, flutter::DlTileMode::kClamp); +// stack.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); + +// auto embeddedViewParams = +// std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); + +// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); +// flutterPlatformViewsController->CompositeEmbeddedView(2); +// XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]); +// ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; +// [mockFlutterView addSubview:childClippingView]; + +// [mockFlutterView setNeedsLayout]; +// [mockFlutterView layoutIfNeeded]; + +// NSMutableArray* originalVisualEffectViews = [[NSMutableArray alloc] init]; +// for (UIView* subview in childClippingView.subviews) { +// if (![subview isKindOfClass:[UIVisualEffectView class]]) { +// continue; +// } +// XCTAssertLessThan(originalVisualEffectViews.count, 1u); +// if ([self validateOneVisualEffectView:subview +// expectedFrame:CGRectMake(0, 0, 10, 10) +// inputRadius:(CGFloat)5]) { +// [originalVisualEffectViews addObject:subview]; +// } +// } +// XCTAssertEqual(originalVisualEffectViews.count, 1u); + +// // +// // Simulate adding 1 backdrop filter (create a new mutators stack) +// // Create embedded view params +// flutter::MutatorsStack stack2; +// // Layer tree always pushes a screen scale factor to the stack +// stack2.PushTransform(screenScaleMatrix); +// // Push backdrop filters +// for (int i = 0; i < 2; i++) { +// stack2.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); +// } + +// embeddedViewParams = std::make_unique(screenScaleMatrix, +// SkSize::Make(10, 10), stack2); + +// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); +// flutterPlatformViewsController->CompositeEmbeddedView(2); +// [mockFlutterView setNeedsLayout]; +// [mockFlutterView layoutIfNeeded]; + +// NSMutableArray* newVisualEffectViews = [[NSMutableArray alloc] init]; +// for (UIView* subview in childClippingView.subviews) { +// if (![subview isKindOfClass:[UIVisualEffectView class]]) { +// continue; +// } +// XCTAssertLessThan(newVisualEffectViews.count, 2u); + +// if ([self validateOneVisualEffectView:subview +// expectedFrame:CGRectMake(0, 0, 10, 10) +// inputRadius:(CGFloat)5]) { +// [newVisualEffectViews addObject:subview]; +// } +// } +// XCTAssertEqual(newVisualEffectViews.count, 2u); +// for (NSUInteger i = 0; i < originalVisualEffectViews.count; i++) { +// UIView* originalView = originalVisualEffectViews[i]; +// UIView* newView = newVisualEffectViews[i]; +// // Compare reference. +// XCTAssertEqual(originalView, newView); +// id mockOrignalView = OCMPartialMock(originalView); +// OCMReject([mockOrignalView removeFromSuperview]); +// [mockOrignalView stopMocking]; +// } +// } + +// - (void)testRemoveBackdropFilters { +// 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(); +// auto platform_view = std::make_unique( +// /*delegate=*/mock_delegate, +// /*rendering_api=*/mock_delegate.settings_.enable_impeller +// ? flutter::IOSRenderingAPI::kMetal +// : flutter::IOSRenderingAPI::kSoftware, +// /*platform_views_controller=*/flutterPlatformViewsController, +// /*task_runners=*/runners, +// /*worker_task_runner=*/nil, +// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + +// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = +// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; +// flutterPlatformViewsController->RegisterViewFactory( +// factory, @"MockFlutterPlatformView", +// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); +// FlutterResult result = ^(id result) { +// }; +// flutterPlatformViewsController->OnMethodCall( +// [FlutterMethodCall +// methodCallWithMethodName:@"create" +// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], +// result); + +// XCTAssertNotNil(gMockPlatformView); + +// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; +// flutterPlatformViewsController->SetFlutterView(mockFlutterView); +// // Create embedded view params +// flutter::MutatorsStack stack; +// // Layer tree always pushes a screen scale factor to the stack +// CGFloat screenScale = [UIScreen mainScreen].scale; +// SkMatrix screenScaleMatrix = SkMatrix::Scale(screenScale, screenScale); +// stack.PushTransform(screenScaleMatrix); +// // Push backdrop filters +// auto filter = std::make_shared(5, 2, flutter::DlTileMode::kClamp); +// for (int i = 0; i < 5; i++) { +// stack.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); +// } + +// auto embeddedViewParams = +// std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); + +// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); +// flutterPlatformViewsController->CompositeEmbeddedView(2); +// XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); +// ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; +// [mockFlutterView addSubview:childClippingView]; + +// [mockFlutterView setNeedsLayout]; +// [mockFlutterView layoutIfNeeded]; + +// NSMutableArray* originalVisualEffectViews = [[NSMutableArray alloc] init]; +// for (UIView* subview in childClippingView.subviews) { +// if (![subview isKindOfClass:[UIVisualEffectView class]]) { +// continue; +// } +// XCTAssertLessThan(originalVisualEffectViews.count, 5u); +// if ([self validateOneVisualEffectView:subview +// expectedFrame:CGRectMake(0, 0, 10, 10) +// inputRadius:(CGFloat)5]) { +// [originalVisualEffectViews addObject:subview]; +// } +// } + +// // Simulate removing 1 backdrop filter (create a new mutators stack) +// // Create embedded view params +// flutter::MutatorsStack stack2; +// // Layer tree always pushes a screen scale factor to the stack +// stack2.PushTransform(screenScaleMatrix); +// // Push backdrop filters +// for (int i = 0; i < 4; i++) { +// stack2.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); +// } + +// embeddedViewParams = std::make_unique(screenScaleMatrix, +// SkSize::Make(10, 10), stack2); + +// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); +// flutterPlatformViewsController->CompositeEmbeddedView(2); +// [mockFlutterView setNeedsLayout]; +// [mockFlutterView layoutIfNeeded]; + +// NSMutableArray* newVisualEffectViews = [[NSMutableArray alloc] init]; +// for (UIView* subview in childClippingView.subviews) { +// if (![subview isKindOfClass:[UIVisualEffectView class]]) { +// continue; +// } +// XCTAssertLessThan(newVisualEffectViews.count, 4u); +// if ([self validateOneVisualEffectView:subview +// expectedFrame:CGRectMake(0, 0, 10, 10) +// inputRadius:(CGFloat)5]) { +// [newVisualEffectViews addObject:subview]; +// } +// } +// XCTAssertEqual(newVisualEffectViews.count, 4u); + +// for (NSUInteger i = 0; i < newVisualEffectViews.count; i++) { +// UIView* newView = newVisualEffectViews[i]; +// id mockNewView = OCMPartialMock(newView); +// UIView* originalView = originalVisualEffectViews[i]; +// // Compare reference. +// XCTAssertEqual(originalView, newView); +// OCMReject([mockNewView removeFromSuperview]); +// [mockNewView stopMocking]; +// } + +// // Simulate removing all backdrop filters (replace the mutators stack) +// // Update embedded view params, delete except screenScaleMatrix +// for (int i = 0; i < 5; i++) { +// stack2.Pop(); +// } +// // No backdrop filters in the stack, so no nothing to push + +// embeddedViewParams = std::make_unique(screenScaleMatrix, +// SkSize::Make(10, 10), stack2); + +// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); +// flutterPlatformViewsController->CompositeEmbeddedView(2); +// [mockFlutterView setNeedsLayout]; +// [mockFlutterView layoutIfNeeded]; + +// NSUInteger numberOfExpectedVisualEffectView = 0u; +// for (UIView* subview in childClippingView.subviews) { +// if ([subview isKindOfClass:[UIVisualEffectView class]]) { +// numberOfExpectedVisualEffectView++; +// } +// } +// XCTAssertEqual(numberOfExpectedVisualEffectView, 0u); +// } + +// - (void)testEditBackdropFilters { +// 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(); +// auto platform_view = std::make_unique( +// /*delegate=*/mock_delegate, +// /*rendering_api=*/mock_delegate.settings_.enable_impeller +// ? flutter::IOSRenderingAPI::kMetal +// : flutter::IOSRenderingAPI::kSoftware, +// /*platform_views_controller=*/flutterPlatformViewsController, +// /*task_runners=*/runners, +// /*worker_task_runner=*/nil, +// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + +// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = +// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; +// flutterPlatformViewsController->RegisterViewFactory( +// factory, @"MockFlutterPlatformView", +// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); +// FlutterResult result = ^(id result) { +// }; +// flutterPlatformViewsController->OnMethodCall( +// [FlutterMethodCall +// methodCallWithMethodName:@"create" +// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], +// result); + +// XCTAssertNotNil(gMockPlatformView); + +// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; +// flutterPlatformViewsController->SetFlutterView(mockFlutterView); +// // Create embedded view params +// flutter::MutatorsStack stack; +// // Layer tree always pushes a screen scale factor to the stack +// CGFloat screenScale = [UIScreen mainScreen].scale; +// SkMatrix screenScaleMatrix = SkMatrix::Scale(screenScale, screenScale); +// stack.PushTransform(screenScaleMatrix); +// // Push backdrop filters +// auto filter = std::make_shared(5, 2, flutter::DlTileMode::kClamp); +// for (int i = 0; i < 5; i++) { +// stack.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); +// } + +// auto embeddedViewParams = +// std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); + +// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); +// flutterPlatformViewsController->CompositeEmbeddedView(2); +// XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); +// ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; +// [mockFlutterView addSubview:childClippingView]; + +// [mockFlutterView setNeedsLayout]; +// [mockFlutterView layoutIfNeeded]; + +// NSMutableArray* originalVisualEffectViews = [[NSMutableArray alloc] init]; +// for (UIView* subview in childClippingView.subviews) { +// if (![subview isKindOfClass:[UIVisualEffectView class]]) { +// continue; +// } +// XCTAssertLessThan(originalVisualEffectViews.count, 5u); +// if ([self validateOneVisualEffectView:subview +// expectedFrame:CGRectMake(0, 0, 10, 10) +// inputRadius:(CGFloat)5]) { +// [originalVisualEffectViews addObject:subview]; +// } +// } + +// // Simulate editing 1 backdrop filter in the middle of the stack (create a new mutators stack) +// // Create embedded view params +// flutter::MutatorsStack stack2; +// // Layer tree always pushes a screen scale factor to the stack +// stack2.PushTransform(screenScaleMatrix); +// // Push backdrop filters +// for (int i = 0; i < 5; i++) { +// if (i == 3) { +// auto filter2 = +// std::make_shared(2, 5, flutter::DlTileMode::kClamp); + +// stack2.PushBackdropFilter(filter2, +// SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); +// continue; +// } + +// stack2.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); +// } + +// embeddedViewParams = std::make_unique(screenScaleMatrix, +// SkSize::Make(10, 10), stack2); + +// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); +// flutterPlatformViewsController->CompositeEmbeddedView(2); +// [mockFlutterView setNeedsLayout]; +// [mockFlutterView layoutIfNeeded]; + +// NSMutableArray* newVisualEffectViews = [[NSMutableArray alloc] init]; +// for (UIView* subview in childClippingView.subviews) { +// if (![subview isKindOfClass:[UIVisualEffectView class]]) { +// continue; +// } +// XCTAssertLessThan(newVisualEffectViews.count, 5u); +// CGFloat expectInputRadius = 5; +// if (newVisualEffectViews.count == 3) { +// expectInputRadius = 2; +// } +// if ([self validateOneVisualEffectView:subview +// expectedFrame:CGRectMake(0, 0, 10, 10) +// inputRadius:(CGFloat)expectInputRadius]) { +// [newVisualEffectViews addObject:subview]; +// } +// } +// XCTAssertEqual(newVisualEffectViews.count, 5u); +// for (NSUInteger i = 0; i < newVisualEffectViews.count; i++) { +// UIView* newView = newVisualEffectViews[i]; +// id mockNewView = OCMPartialMock(newView); +// UIView* originalView = originalVisualEffectViews[i]; +// // Compare reference. +// XCTAssertEqual(originalView, newView); +// OCMReject([mockNewView removeFromSuperview]); +// [mockNewView stopMocking]; +// } +// [newVisualEffectViews removeAllObjects]; + +// // Simulate editing 1 backdrop filter in the beginning of the stack (replace the mutators stack) +// // Update embedded view params, delete except screenScaleMatrix +// for (int i = 0; i < 5; i++) { +// stack2.Pop(); +// } +// // Push backdrop filters +// for (int i = 0; i < 5; i++) { +// if (i == 0) { +// auto filter2 = +// std::make_shared(2, 5, flutter::DlTileMode::kClamp); +// stack2.PushBackdropFilter(filter2, +// SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); +// continue; +// } + +// stack2.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); +// } + +// embeddedViewParams = std::make_unique(screenScaleMatrix, +// SkSize::Make(10, 10), stack2); + +// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); +// flutterPlatformViewsController->CompositeEmbeddedView(2); +// [mockFlutterView setNeedsLayout]; +// [mockFlutterView layoutIfNeeded]; + +// for (UIView* subview in childClippingView.subviews) { +// if (![subview isKindOfClass:[UIVisualEffectView class]]) { +// continue; +// } +// XCTAssertLessThan(newVisualEffectViews.count, 5u); +// CGFloat expectInputRadius = 5; +// if (newVisualEffectViews.count == 0) { +// expectInputRadius = 2; +// } +// if ([self validateOneVisualEffectView:subview +// expectedFrame:CGRectMake(0, 0, 10, 10) +// inputRadius:(CGFloat)expectInputRadius]) { +// [newVisualEffectViews addObject:subview]; +// } +// } +// for (NSUInteger i = 0; i < newVisualEffectViews.count; i++) { +// UIView* newView = newVisualEffectViews[i]; +// id mockNewView = OCMPartialMock(newView); +// UIView* originalView = originalVisualEffectViews[i]; +// // Compare reference. +// XCTAssertEqual(originalView, newView); +// OCMReject([mockNewView removeFromSuperview]); +// [mockNewView stopMocking]; +// } +// [newVisualEffectViews removeAllObjects]; + +// // Simulate editing 1 backdrop filter in the end of the stack (replace the mutators stack) +// // Update embedded view params, delete except screenScaleMatrix +// for (int i = 0; i < 5; i++) { +// stack2.Pop(); +// } +// // Push backdrop filters +// for (int i = 0; i < 5; i++) { +// if (i == 4) { +// auto filter2 = +// std::make_shared(2, 5, flutter::DlTileMode::kClamp); +// stack2.PushBackdropFilter(filter2, +// SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); +// continue; +// } + +// stack2.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); +// } + +// embeddedViewParams = std::make_unique(screenScaleMatrix, +// SkSize::Make(10, 10), stack2); + +// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); +// flutterPlatformViewsController->CompositeEmbeddedView(2); +// [mockFlutterView setNeedsLayout]; +// [mockFlutterView layoutIfNeeded]; + +// for (UIView* subview in childClippingView.subviews) { +// if (![subview isKindOfClass:[UIVisualEffectView class]]) { +// continue; +// } +// XCTAssertLessThan(newVisualEffectViews.count, 5u); +// CGFloat expectInputRadius = 5; +// if (newVisualEffectViews.count == 4) { +// expectInputRadius = 2; +// } +// if ([self validateOneVisualEffectView:subview +// expectedFrame:CGRectMake(0, 0, 10, 10) +// inputRadius:(CGFloat)expectInputRadius]) { +// [newVisualEffectViews addObject:subview]; +// } +// } +// XCTAssertEqual(newVisualEffectViews.count, 5u); + +// for (NSUInteger i = 0; i < newVisualEffectViews.count; i++) { +// UIView* newView = newVisualEffectViews[i]; +// id mockNewView = OCMPartialMock(newView); +// UIView* originalView = originalVisualEffectViews[i]; +// // Compare reference. +// XCTAssertEqual(originalView, newView); +// OCMReject([mockNewView removeFromSuperview]); +// [mockNewView stopMocking]; +// } +// [newVisualEffectViews removeAllObjects]; + +// // Simulate editing all backdrop filters in the stack (replace the mutators stack) +// // Update embedded view params, delete except screenScaleMatrix +// for (int i = 0; i < 5; i++) { +// stack2.Pop(); +// } +// // Push backdrop filters +// for (int i = 0; i < 5; i++) { +// auto filter2 = std::make_shared(i, 2, flutter::DlTileMode::kClamp); + +// stack2.PushBackdropFilter(filter2, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); +// } + +// embeddedViewParams = std::make_unique(screenScaleMatrix, +// SkSize::Make(10, 10), stack2); + +// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); +// flutterPlatformViewsController->CompositeEmbeddedView(2); +// [mockFlutterView setNeedsLayout]; +// [mockFlutterView layoutIfNeeded]; + +// for (UIView* subview in childClippingView.subviews) { +// if (![subview isKindOfClass:[UIVisualEffectView class]]) { +// continue; +// } +// XCTAssertLessThan(newVisualEffectViews.count, 5u); +// if ([self validateOneVisualEffectView:subview +// expectedFrame:CGRectMake(0, 0, 10, 10) +// inputRadius:(CGFloat)newVisualEffectViews.count]) { +// [newVisualEffectViews addObject:subview]; +// } +// } +// XCTAssertEqual(newVisualEffectViews.count, 5u); + +// for (NSUInteger i = 0; i < newVisualEffectViews.count; i++) { +// UIView* newView = newVisualEffectViews[i]; +// id mockNewView = OCMPartialMock(newView); +// UIView* originalView = originalVisualEffectViews[i]; +// // Compare reference. +// XCTAssertEqual(originalView, newView); +// OCMReject([mockNewView removeFromSuperview]); +// [mockNewView stopMocking]; +// } +// [newVisualEffectViews removeAllObjects]; +// } + +// - (void)testApplyBackdropFilterNotDlBlurImageFilter { +// 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(); +// auto platform_view = std::make_unique( +// /*delegate=*/mock_delegate, +// /*rendering_api=*/mock_delegate.settings_.enable_impeller +// ? flutter::IOSRenderingAPI::kMetal +// : flutter::IOSRenderingAPI::kSoftware, +// /*platform_views_controller=*/flutterPlatformViewsController, +// /*task_runners=*/runners, +// /*worker_task_runner=*/nil, +// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + +// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = +// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; +// flutterPlatformViewsController->RegisterViewFactory( +// factory, @"MockFlutterPlatformView", +// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); +// FlutterResult result = ^(id result) { +// }; +// flutterPlatformViewsController->OnMethodCall( +// [FlutterMethodCall +// methodCallWithMethodName:@"create" +// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], +// result); + +// XCTAssertNotNil(gMockPlatformView); + +// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; +// flutterPlatformViewsController->SetFlutterView(mockFlutterView); +// // Create embedded view params +// flutter::MutatorsStack stack; +// // Layer tree always pushes a screen scale factor to the stack +// CGFloat screenScale = [UIScreen mainScreen].scale; +// SkMatrix screenScaleMatrix = SkMatrix::Scale(screenScale, screenScale); +// stack.PushTransform(screenScaleMatrix); +// // Push a dilate backdrop filter +// auto dilateFilter = std::make_shared(5, 2); +// stack.PushBackdropFilter(dilateFilter, SkRect::MakeEmpty()); + +// auto embeddedViewParams = +// std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); + +// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); +// flutterPlatformViewsController->CompositeEmbeddedView(2); +// XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]); +// ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; + +// [mockFlutterView addSubview:childClippingView]; + +// [mockFlutterView setNeedsLayout]; +// [mockFlutterView layoutIfNeeded]; + +// NSUInteger numberOfExpectedVisualEffectView = 0; +// for (UIView* subview in childClippingView.subviews) { +// if ([subview isKindOfClass:[UIVisualEffectView class]]) { +// numberOfExpectedVisualEffectView++; +// } +// } +// XCTAssertEqual(numberOfExpectedVisualEffectView, 0u); + +// // Simulate adding a non-DlBlurImageFilter in the middle of the stack (create a new mutators +// // stack) Create embedded view params +// flutter::MutatorsStack stack2; +// // Layer tree always pushes a screen scale factor to the stack +// stack2.PushTransform(screenScaleMatrix); +// // Push backdrop filters and dilate filter +// auto blurFilter = std::make_shared(5, 2, flutter::DlTileMode::kClamp); + +// for (int i = 0; i < 5; i++) { +// if (i == 2) { +// stack2.PushBackdropFilter(dilateFilter, +// SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); +// continue; +// } + +// stack2.PushBackdropFilter(blurFilter, +// SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); +// } + +// embeddedViewParams = std::make_unique(screenScaleMatrix, +// SkSize::Make(10, 10), stack2); + +// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); +// flutterPlatformViewsController->CompositeEmbeddedView(2); +// [mockFlutterView setNeedsLayout]; +// [mockFlutterView layoutIfNeeded]; + +// numberOfExpectedVisualEffectView = 0; +// for (UIView* subview in childClippingView.subviews) { +// if (![subview isKindOfClass:[UIVisualEffectView class]]) { +// continue; +// } +// XCTAssertLessThan(numberOfExpectedVisualEffectView, 4u); +// if ([self validateOneVisualEffectView:subview +// expectedFrame:CGRectMake(0, 0, 10, 10) +// inputRadius:(CGFloat)5]) { +// numberOfExpectedVisualEffectView++; +// } +// } +// XCTAssertEqual(numberOfExpectedVisualEffectView, 4u); + +// // Simulate adding a non-DlBlurImageFilter to the beginning of the stack (replace the mutators +// // stack) Update embedded view params, delete except screenScaleMatrix +// for (int i = 0; i < 5; i++) { +// stack2.Pop(); +// } +// // Push backdrop filters and dilate filter +// for (int i = 0; i < 5; i++) { +// if (i == 0) { +// stack2.PushBackdropFilter(dilateFilter, +// SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); +// continue; +// } + +// stack2.PushBackdropFilter(blurFilter, +// SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); +// } + +// embeddedViewParams = std::make_unique(screenScaleMatrix, +// SkSize::Make(10, 10), stack2); + +// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); +// flutterPlatformViewsController->CompositeEmbeddedView(2); +// [mockFlutterView setNeedsLayout]; +// [mockFlutterView layoutIfNeeded]; + +// numberOfExpectedVisualEffectView = 0; +// for (UIView* subview in childClippingView.subviews) { +// if (![subview isKindOfClass:[UIVisualEffectView class]]) { +// continue; +// } +// XCTAssertLessThan(numberOfExpectedVisualEffectView, 4u); +// if ([self validateOneVisualEffectView:subview +// expectedFrame:CGRectMake(0, 0, 10, 10) +// inputRadius:(CGFloat)5]) { +// numberOfExpectedVisualEffectView++; +// } +// } +// XCTAssertEqual(numberOfExpectedVisualEffectView, 4u); + +// // Simulate adding a non-DlBlurImageFilter to the end of the stack (replace the mutators stack) +// // Update embedded view params, delete except screenScaleMatrix +// for (int i = 0; i < 5; i++) { +// stack2.Pop(); +// } +// // Push backdrop filters and dilate filter +// for (int i = 0; i < 5; i++) { +// if (i == 4) { +// stack2.PushBackdropFilter(dilateFilter, +// SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); +// continue; +// } + +// stack2.PushBackdropFilter(blurFilter, +// SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); +// } + +// embeddedViewParams = std::make_unique(screenScaleMatrix, +// SkSize::Make(10, 10), stack2); + +// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); +// flutterPlatformViewsController->CompositeEmbeddedView(2); +// [mockFlutterView setNeedsLayout]; +// [mockFlutterView layoutIfNeeded]; + +// numberOfExpectedVisualEffectView = 0; +// for (UIView* subview in childClippingView.subviews) { +// if (![subview isKindOfClass:[UIVisualEffectView class]]) { +// continue; +// } +// XCTAssertLessThan(numberOfExpectedVisualEffectView, 4u); +// if ([self validateOneVisualEffectView:subview +// expectedFrame:CGRectMake(0, 0, 10, 10) +// inputRadius:(CGFloat)5]) { +// numberOfExpectedVisualEffectView++; +// } +// } +// XCTAssertEqual(numberOfExpectedVisualEffectView, 4u); + +// // Simulate adding only non-DlBlurImageFilter to the stack (replace the mutators stack) +// // Update embedded view params, delete except screenScaleMatrix +// for (int i = 0; i < 5; i++) { +// stack2.Pop(); +// } +// // Push dilate filters +// for (int i = 0; i < 5; i++) { +// stack2.PushBackdropFilter(dilateFilter, +// SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); +// } + +// embeddedViewParams = std::make_unique(screenScaleMatrix, +// SkSize::Make(10, 10), stack2); + +// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); +// flutterPlatformViewsController->CompositeEmbeddedView(2); +// [mockFlutterView setNeedsLayout]; +// [mockFlutterView layoutIfNeeded]; + +// numberOfExpectedVisualEffectView = 0; +// for (UIView* subview in childClippingView.subviews) { +// if ([subview isKindOfClass:[UIVisualEffectView class]]) { +// numberOfExpectedVisualEffectView++; +// } +// } +// XCTAssertEqual(numberOfExpectedVisualEffectView, 0u); +// } + +// - (void)testApplyBackdropFilterCorrectAPI { +// [PlatformViewFilter resetPreparation]; +// // The gaussianBlur filter is extracted from UIVisualEffectView. +// // Each test requires a new PlatformViewFilter +// // Valid UIVisualEffectView API +// UIVisualEffectView* visualEffectView = [[UIVisualEffectView alloc] +// initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]]; +// PlatformViewFilter* platformViewFilter = +// [[PlatformViewFilter alloc] initWithFrame:CGRectMake(0, 0, 10, 10) +// blurRadius:5 +// visualEffectView:visualEffectView]; +// XCTAssertNotNil(platformViewFilter); +// } + +// - (void)testApplyBackdropFilterAPIChangedInvalidUIVisualEffectView { +// [PlatformViewFilter resetPreparation]; +// UIVisualEffectView* visualEffectView = [[UIVisualEffectView alloc] init]; +// PlatformViewFilter* platformViewFilter = +// [[PlatformViewFilter alloc] initWithFrame:CGRectMake(0, 0, 10, 10) +// blurRadius:5 +// visualEffectView:visualEffectView]; +// XCTAssertNil(platformViewFilter); +// } + +// - (void)testApplyBackdropFilterAPIChangedNoGaussianBlurFilter { +// [PlatformViewFilter resetPreparation]; +// UIVisualEffectView* editedUIVisualEffectView = [[UIVisualEffectView alloc] +// initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]]; +// NSArray* subviews = editedUIVisualEffectView.subviews; +// for (UIView* view in subviews) { +// if ([NSStringFromClass([view class]) hasSuffix:@"BackdropView"]) { +// for (CIFilter* filter in view.layer.filters) { +// if ([[filter valueForKey:@"name"] isEqual:@"gaussianBlur"]) { +// [filter setValue:@"notGaussianBlur" forKey:@"name"]; +// break; +// } +// } +// break; +// } +// } +// PlatformViewFilter* platformViewFilter = +// [[PlatformViewFilter alloc] initWithFrame:CGRectMake(0, 0, 10, 10) +// blurRadius:5 +// visualEffectView:editedUIVisualEffectView]; +// XCTAssertNil(platformViewFilter); +// } + +// - (void)testApplyBackdropFilterAPIChangedInvalidInputRadius { +// [PlatformViewFilter resetPreparation]; +// UIVisualEffectView* editedUIVisualEffectView = [[UIVisualEffectView alloc] +// initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]]; +// NSArray* subviews = editedUIVisualEffectView.subviews; +// for (UIView* view in subviews) { +// if ([NSStringFromClass([view class]) hasSuffix:@"BackdropView"]) { +// for (CIFilter* filter in view.layer.filters) { +// if ([[filter valueForKey:@"name"] isEqual:@"gaussianBlur"]) { +// [filter setValue:@"invalidInputRadius" forKey:@"inputRadius"]; +// break; +// } +// } +// break; +// } +// } + +// PlatformViewFilter* platformViewFilter = +// [[PlatformViewFilter alloc] initWithFrame:CGRectMake(0, 0, 10, 10) +// blurRadius:5 +// visualEffectView:editedUIVisualEffectView]; +// XCTAssertNil(platformViewFilter); +// } + +// - (void)testBackdropFilterVisualEffectSubviewBackgroundColor { +// __weak UIVisualEffectView* weakVisualEffectView; + +// @autoreleasepool { +// UIVisualEffectView* visualEffectView = [[UIVisualEffectView alloc] +// initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]]; +// weakVisualEffectView = visualEffectView; +// PlatformViewFilter* platformViewFilter = +// [[PlatformViewFilter alloc] initWithFrame:CGRectMake(0, 0, 10, 10) +// blurRadius:5 +// visualEffectView:visualEffectView]; +// CGColorRef visualEffectSubviewBackgroundColor = nil; +// for (UIView* view in [platformViewFilter backdropFilterView].subviews) { +// if ([NSStringFromClass([view class]) hasSuffix:@"VisualEffectSubview"]) { +// visualEffectSubviewBackgroundColor = view.layer.backgroundColor; +// } +// } +// XCTAssertTrue( +// CGColorEqualToColor(visualEffectSubviewBackgroundColor, UIColor.clearColor.CGColor)); +// } +// XCTAssertNil(weakVisualEffectView); +// } + +// - (void)testCompositePlatformView { +// 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(); +// auto platform_view = std::make_unique( +// /*delegate=*/mock_delegate, +// /*rendering_api=*/mock_delegate.settings_.enable_impeller +// ? flutter::IOSRenderingAPI::kMetal +// : flutter::IOSRenderingAPI::kSoftware, +// /*platform_views_controller=*/flutterPlatformViewsController, +// /*task_runners=*/runners, +// /*worker_task_runner=*/nil, +// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + +// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = +// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; +// flutterPlatformViewsController->RegisterViewFactory( +// factory, @"MockFlutterPlatformView", +// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); +// FlutterResult result = ^(id result) { +// }; +// flutterPlatformViewsController->OnMethodCall( +// [FlutterMethodCall +// methodCallWithMethodName:@"create" +// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], +// result); + +// XCTAssertNotNil(gMockPlatformView); + +// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; +// flutterPlatformViewsController->SetFlutterView(mockFlutterView); +// // Create embedded view params +// flutter::MutatorsStack stack; +// // Layer tree always pushes a screen scale factor to the stack +// SkMatrix screenScaleMatrix = +// SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); +// stack.PushTransform(screenScaleMatrix); +// // Push a translate matrix +// SkMatrix translateMatrix = SkMatrix::Translate(100, 100); +// stack.PushTransform(translateMatrix); +// SkMatrix finalMatrix; +// finalMatrix.setConcat(screenScaleMatrix, translateMatrix); + +// auto embeddedViewParams = +// std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); + +// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); +// flutterPlatformViewsController->CompositeEmbeddedView(2); +// CGRect platformViewRectInFlutterView = [gMockPlatformView convertRect:gMockPlatformView.bounds +// toView:mockFlutterView]; +// XCTAssertTrue(CGRectEqualToRect(platformViewRectInFlutterView, CGRectMake(100, 100, 300, 300))); +// } + +// - (void)testBackdropFilterCorrectlyPushedAndReset { +// 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(); +// auto platform_view = std::make_unique( +// /*delegate=*/mock_delegate, +// /*rendering_api=*/mock_delegate.settings_.enable_impeller +// ? flutter::IOSRenderingAPI::kMetal +// : flutter::IOSRenderingAPI::kSoftware, +// /*platform_views_controller=*/flutterPlatformViewsController, +// /*task_runners=*/runners, +// /*worker_task_runner=*/nil, +// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + +// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = +// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; +// flutterPlatformViewsController->RegisterViewFactory( +// factory, @"MockFlutterPlatformView", +// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); +// FlutterResult result = ^(id result) { +// }; +// flutterPlatformViewsController->OnMethodCall( +// [FlutterMethodCall +// methodCallWithMethodName:@"create" +// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], +// result); + +// XCTAssertNotNil(gMockPlatformView); + +// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; +// flutterPlatformViewsController->SetFlutterView(mockFlutterView); +// // Create embedded view params +// flutter::MutatorsStack stack; +// // Layer tree always pushes a screen scale factor to the stack +// CGFloat screenScale = [UIScreen mainScreen].scale; +// SkMatrix screenScaleMatrix = SkMatrix::Scale(screenScale, screenScale); +// stack.PushTransform(screenScaleMatrix); + +// auto embeddedViewParams = +// std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); + +// flutterPlatformViewsController->BeginFrame(SkISize::Make(0, 0)); +// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); +// flutterPlatformViewsController->PushVisitedPlatformView(2); +// auto filter = std::make_shared(5, 2, flutter::DlTileMode::kClamp); +// flutterPlatformViewsController->PushFilterToVisitedPlatformViews( +// filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); +// flutterPlatformViewsController->CompositeEmbeddedView(2); +// XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]); +// ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; +// [mockFlutterView addSubview:childClippingView]; + +// [mockFlutterView setNeedsLayout]; +// [mockFlutterView layoutIfNeeded]; + +// // childClippingView has visual effect view with the correct configurations. +// NSUInteger numberOfExpectedVisualEffectView = 0; +// for (UIView* subview in childClippingView.subviews) { +// if (![subview isKindOfClass:[UIVisualEffectView class]]) { +// continue; +// } +// XCTAssertLessThan(numberOfExpectedVisualEffectView, 1u); +// if ([self validateOneVisualEffectView:subview +// expectedFrame:CGRectMake(0, 0, 10, 10) +// inputRadius:5]) { +// numberOfExpectedVisualEffectView++; +// } +// } +// XCTAssertEqual(numberOfExpectedVisualEffectView, 1u); + +// // New frame, with no filter pushed. +// auto embeddedViewParams2 = +// std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); +// flutterPlatformViewsController->BeginFrame(SkISize::Make(0, 0)); +// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams2)); +// flutterPlatformViewsController->CompositeEmbeddedView(2); +// XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]); + +// [mockFlutterView setNeedsLayout]; +// [mockFlutterView layoutIfNeeded]; + +// numberOfExpectedVisualEffectView = 0; +// for (UIView* subview in childClippingView.subviews) { +// if (![subview isKindOfClass:[UIVisualEffectView class]]) { +// continue; +// } +// numberOfExpectedVisualEffectView++; +// } +// XCTAssertEqual(numberOfExpectedVisualEffectView, 0u); +// } + +// - (void)testChildClippingViewShouldBeTheBoundingRectOfPlatformView { +// 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(); +// auto platform_view = std::make_unique( +// /*delegate=*/mock_delegate, +// /*rendering_api=*/mock_delegate.settings_.enable_impeller +// ? flutter::IOSRenderingAPI::kMetal +// : flutter::IOSRenderingAPI::kSoftware, +// /*platform_views_controller=*/flutterPlatformViewsController, +// /*task_runners=*/runners, +// /*worker_task_runner=*/nil, +// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + +// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = +// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; +// flutterPlatformViewsController->RegisterViewFactory( +// factory, @"MockFlutterPlatformView", +// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); +// FlutterResult result = ^(id result) { +// }; +// flutterPlatformViewsController->OnMethodCall( +// [FlutterMethodCall +// methodCallWithMethodName:@"create" +// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], +// result); + +// XCTAssertNotNil(gMockPlatformView); + +// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; +// flutterPlatformViewsController->SetFlutterView(mockFlutterView); +// // Create embedded view params +// flutter::MutatorsStack stack; +// // Layer tree always pushes a screen scale factor to the stack +// SkMatrix screenScaleMatrix = +// SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); +// stack.PushTransform(screenScaleMatrix); +// // Push a rotate matrix +// SkMatrix rotateMatrix; +// rotateMatrix.setRotate(10); +// stack.PushTransform(rotateMatrix); +// SkMatrix finalMatrix; +// finalMatrix.setConcat(screenScaleMatrix, rotateMatrix); + +// auto embeddedViewParams = +// std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); + +// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); +// flutterPlatformViewsController->CompositeEmbeddedView(2); +// CGRect platformViewRectInFlutterView = [gMockPlatformView convertRect:gMockPlatformView.bounds +// toView:mockFlutterView]; +// XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); +// ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; +// // The childclippingview's frame is set based on flow, but the platform view's frame is set based +// // on quartz. Although they should be the same, but we should tolerate small floating point +// // errors. +// XCTAssertLessThan(fabs(platformViewRectInFlutterView.origin.x - childClippingView.frame.origin.x), +// kFloatCompareEpsilon); +// XCTAssertLessThan(fabs(platformViewRectInFlutterView.origin.y - childClippingView.frame.origin.y), +// kFloatCompareEpsilon); +// XCTAssertLessThan( +// fabs(platformViewRectInFlutterView.size.width - childClippingView.frame.size.width), +// kFloatCompareEpsilon); +// XCTAssertLessThan( +// fabs(platformViewRectInFlutterView.size.height - childClippingView.frame.size.height), +// kFloatCompareEpsilon); +// } + +// - (void)testClipsDoNotInterceptWithPlatformViewShouldNotAddMaskView { +// 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(); +// auto platform_view = std::make_unique( +// /*delegate=*/mock_delegate, +// /*rendering_api=*/mock_delegate.settings_.enable_impeller +// ? flutter::IOSRenderingAPI::kMetal +// : flutter::IOSRenderingAPI::kSoftware, +// /*platform_views_controller=*/flutterPlatformViewsController, +// /*task_runners=*/runners, +// /*worker_task_runner=*/nil, +// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + +// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = +// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; +// flutterPlatformViewsController->RegisterViewFactory( +// factory, @"MockFlutterPlatformView", +// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); +// FlutterResult result = ^(id result) { +// }; +// flutterPlatformViewsController->OnMethodCall( +// [FlutterMethodCall +// methodCallWithMethodName:@"create" +// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], +// result); + +// XCTAssertNotNil(gMockPlatformView); + +// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 30, 30)]; +// flutterPlatformViewsController->SetFlutterView(mockFlutterView); +// // Create embedded view params. +// flutter::MutatorsStack stack; +// // Layer tree always pushes a screen scale factor to the stack. +// SkMatrix screenScaleMatrix = +// SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); +// stack.PushTransform(screenScaleMatrix); +// SkMatrix translateMatrix = SkMatrix::Translate(5, 5); +// // The platform view's rect for this test will be (5, 5, 10, 10). +// stack.PushTransform(translateMatrix); +// // Push a clip rect, big enough to contain the entire platform view bound. +// SkRect rect = SkRect::MakeXYWH(0, 0, 25, 25); +// stack.PushClipRect(rect); +// // Push a clip rrect, big enough to contain the entire platform view bound without clipping it. +// // Make the origin (-1, -1) so that the top left rounded corner isn't clipping the PlatformView. +// SkRect rect_for_rrect = SkRect::MakeXYWH(-1, -1, 25, 25); +// SkRRect rrect = SkRRect::MakeRectXY(rect_for_rrect, 1, 1); +// stack.PushClipRRect(rrect); + +// auto embeddedViewParams = std::make_unique( +// SkMatrix::Concat(screenScaleMatrix, translateMatrix), SkSize::Make(5, 5), stack); + +// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); +// flutterPlatformViewsController->CompositeEmbeddedView(2); +// gMockPlatformView.backgroundColor = UIColor.redColor; +// XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); +// ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; +// [mockFlutterView addSubview:childClippingView]; + +// [mockFlutterView setNeedsLayout]; +// [mockFlutterView layoutIfNeeded]; +// XCTAssertNil(childClippingView.maskView); +// } + +// - (void)testClipRRectOnlyHasCornersInterceptWithPlatformViewShouldAddMaskView { +// 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(); +// auto platform_view = std::make_unique( +// /*delegate=*/mock_delegate, +// /*rendering_api=*/mock_delegate.settings_.enable_impeller +// ? flutter::IOSRenderingAPI::kMetal +// : flutter::IOSRenderingAPI::kSoftware, +// /*platform_views_controller=*/flutterPlatformViewsController, +// /*task_runners=*/runners, +// /*worker_task_runner=*/nil, +// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + +// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = +// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; +// flutterPlatformViewsController->RegisterViewFactory( +// factory, @"MockFlutterPlatformView", +// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); +// FlutterResult result = ^(id result) { +// }; +// flutterPlatformViewsController->OnMethodCall( +// [FlutterMethodCall +// methodCallWithMethodName:@"create" +// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], +// result); + +// XCTAssertNotNil(gMockPlatformView); + +// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 30, 30)]; +// flutterPlatformViewsController->SetFlutterView(mockFlutterView); +// // Create embedded view params +// flutter::MutatorsStack stack; +// // Layer tree always pushes a screen scale factor to the stack. +// SkMatrix screenScaleMatrix = +// SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); +// stack.PushTransform(screenScaleMatrix); +// SkMatrix translateMatrix = SkMatrix::Translate(5, 5); +// // The platform view's rect for this test will be (5, 5, 10, 10). +// stack.PushTransform(translateMatrix); + +// // Push a clip rrect, the rect of the rrect is the same as the PlatformView of the corner should. +// // clip the PlatformView. +// SkRect rect_for_rrect = SkRect::MakeXYWH(0, 0, 10, 10); +// SkRRect rrect = SkRRect::MakeRectXY(rect_for_rrect, 1, 1); +// stack.PushClipRRect(rrect); + +// auto embeddedViewParams = std::make_unique( +// SkMatrix::Concat(screenScaleMatrix, translateMatrix), SkSize::Make(5, 5), stack); + +// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); +// flutterPlatformViewsController->CompositeEmbeddedView(2); +// gMockPlatformView.backgroundColor = UIColor.redColor; +// XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); +// ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; +// [mockFlutterView addSubview:childClippingView]; + +// [mockFlutterView setNeedsLayout]; +// [mockFlutterView layoutIfNeeded]; + +// XCTAssertNotNil(childClippingView.maskView); +// } + +// - (void)testClipRect { +// 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(); +// auto platform_view = std::make_unique( +// /*delegate=*/mock_delegate, +// /*rendering_api=*/mock_delegate.settings_.enable_impeller +// ? flutter::IOSRenderingAPI::kMetal +// : flutter::IOSRenderingAPI::kSoftware, +// /*platform_views_controller=*/flutterPlatformViewsController, +// /*task_runners=*/runners, +// /*worker_task_runner=*/nil, +// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + +// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = +// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; +// flutterPlatformViewsController->RegisterViewFactory( +// factory, @"MockFlutterPlatformView", +// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); +// FlutterResult result = ^(id result) { +// }; +// flutterPlatformViewsController->OnMethodCall( +// [FlutterMethodCall +// methodCallWithMethodName:@"create" +// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], +// result); + +// XCTAssertNotNil(gMockPlatformView); + +// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; +// flutterPlatformViewsController->SetFlutterView(mockFlutterView); +// // Create embedded view params +// flutter::MutatorsStack stack; +// // Layer tree always pushes a screen scale factor to the stack +// SkMatrix screenScaleMatrix = +// SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); +// stack.PushTransform(screenScaleMatrix); +// // Push a clip rect +// SkRect rect = SkRect::MakeXYWH(2, 2, 3, 3); +// stack.PushClipRect(rect); + +// auto embeddedViewParams = +// std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); + +// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); +// flutterPlatformViewsController->CompositeEmbeddedView(2); +// gMockPlatformView.backgroundColor = UIColor.redColor; +// XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); +// ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; +// [mockFlutterView addSubview:childClippingView]; + +// [mockFlutterView setNeedsLayout]; +// [mockFlutterView layoutIfNeeded]; + +// CGRect insideClipping = CGRectMake(2, 2, 3, 3); +// for (int i = 0; i < 10; i++) { +// for (int j = 0; j < 10; j++) { +// CGPoint point = CGPointMake(i, j); +// int alpha = [self alphaOfPoint:CGPointMake(i, j) onView:mockFlutterView]; +// if (CGRectContainsPoint(insideClipping, point)) { +// XCTAssertEqual(alpha, 255); +// } else { +// XCTAssertEqual(alpha, 0); +// } +// } +// } +// } + +// - (void)testClipRRect { +// 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(); +// auto platform_view = std::make_unique( +// /*delegate=*/mock_delegate, +// /*rendering_api=*/mock_delegate.settings_.enable_impeller +// ? flutter::IOSRenderingAPI::kMetal +// : flutter::IOSRenderingAPI::kSoftware, +// /*platform_views_controller=*/flutterPlatformViewsController, +// /*task_runners=*/runners, +// /*worker_task_runner=*/nil, +// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + +// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = +// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; +// flutterPlatformViewsController->RegisterViewFactory( +// factory, @"MockFlutterPlatformView", +// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); +// FlutterResult result = ^(id result) { +// }; +// flutterPlatformViewsController->OnMethodCall( +// [FlutterMethodCall +// methodCallWithMethodName:@"create" +// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], +// result); + +// XCTAssertNotNil(gMockPlatformView); + +// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; +// flutterPlatformViewsController->SetFlutterView(mockFlutterView); +// // Create embedded view params +// flutter::MutatorsStack stack; +// // Layer tree always pushes a screen scale factor to the stack +// SkMatrix screenScaleMatrix = +// SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); +// stack.PushTransform(screenScaleMatrix); +// // Push a clip rrect +// SkRRect rrect = SkRRect::MakeRectXY(SkRect::MakeXYWH(2, 2, 6, 6), 1, 1); +// stack.PushClipRRect(rrect); + +// auto embeddedViewParams = +// std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); + +// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); +// flutterPlatformViewsController->CompositeEmbeddedView(2); +// gMockPlatformView.backgroundColor = UIColor.redColor; +// XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); +// ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; +// [mockFlutterView addSubview:childClippingView]; + +// [mockFlutterView setNeedsLayout]; +// [mockFlutterView layoutIfNeeded]; + +// /* +// ClippingMask outterClipping +// 2 3 4 5 6 7 2 3 4 5 6 7 +// 2 / - - - - \ 2 + - - - - + +// 3 | | 3 | | +// 4 | | 4 | | +// 5 | | 5 | | +// 6 | | 6 | | +// 7 \ - - - - / 7 + - - - - + + +// innerClipping1 innerClipping2 +// 2 3 4 5 6 7 2 3 4 5 6 7 +// 2 + - - + 2 +// 3 | | 3 + - - - - + +// 4 | | 4 | | +// 5 | | 5 | | +// 6 | | 6 + - - - - + +// 7 + - - + 7 +// */ +// CGRect innerClipping1 = CGRectMake(3, 2, 4, 6); +// CGRect innerClipping2 = CGRectMake(2, 3, 6, 4); +// CGRect outterClipping = CGRectMake(2, 2, 6, 6); +// for (int i = 0; i < 10; i++) { +// for (int j = 0; j < 10; j++) { +// CGPoint point = CGPointMake(i, j); +// int alpha = [self alphaOfPoint:CGPointMake(i, j) onView:mockFlutterView]; +// if (CGRectContainsPoint(innerClipping1, point) || +// CGRectContainsPoint(innerClipping2, point)) { +// // Pixels inside either of the 2 inner clippings should be fully opaque. +// XCTAssertEqual(alpha, 255); +// } else if (CGRectContainsPoint(outterClipping, point)) { +// // Corner pixels (i.e. (2, 2), (2, 7), (7, 2) and (7, 7)) should be partially transparent. +// XCTAssert(0 < alpha && alpha < 255); +// } else { +// // Pixels outside outterClipping should be fully transparent. +// XCTAssertEqual(alpha, 0); +// } +// } +// } +// } + +// - (void)testClipPath { +// 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(); +// auto platform_view = std::make_unique( +// /*delegate=*/mock_delegate, +// /*rendering_api=*/mock_delegate.settings_.enable_impeller +// ? flutter::IOSRenderingAPI::kMetal +// : flutter::IOSRenderingAPI::kSoftware, +// /*platform_views_controller=*/flutterPlatformViewsController, +// /*task_runners=*/runners, +// /*worker_task_runner=*/nil, +// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + +// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = +// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; +// flutterPlatformViewsController->RegisterViewFactory( +// factory, @"MockFlutterPlatformView", +// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); +// FlutterResult result = ^(id result) { +// }; +// flutterPlatformViewsController->OnMethodCall( +// [FlutterMethodCall +// methodCallWithMethodName:@"create" +// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], +// result); + +// XCTAssertNotNil(gMockPlatformView); + +// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; +// flutterPlatformViewsController->SetFlutterView(mockFlutterView); +// // Create embedded view params +// flutter::MutatorsStack stack; +// // Layer tree always pushes a screen scale factor to the stack +// SkMatrix screenScaleMatrix = +// SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); +// stack.PushTransform(screenScaleMatrix); +// // Push a clip path +// SkPath path; +// path.addRoundRect(SkRect::MakeXYWH(2, 2, 6, 6), 1, 1); +// stack.PushClipPath(path); + +// auto embeddedViewParams = +// std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); + +// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); +// flutterPlatformViewsController->CompositeEmbeddedView(2); +// gMockPlatformView.backgroundColor = UIColor.redColor; +// XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); +// ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; +// [mockFlutterView addSubview:childClippingView]; + +// [mockFlutterView setNeedsLayout]; +// [mockFlutterView layoutIfNeeded]; + +// /* +// ClippingMask outterClipping +// 2 3 4 5 6 7 2 3 4 5 6 7 +// 2 / - - - - \ 2 + - - - - + +// 3 | | 3 | | +// 4 | | 4 | | +// 5 | | 5 | | +// 6 | | 6 | | +// 7 \ - - - - / 7 + - - - - + + +// innerClipping1 innerClipping2 +// 2 3 4 5 6 7 2 3 4 5 6 7 +// 2 + - - + 2 +// 3 | | 3 + - - - - + +// 4 | | 4 | | +// 5 | | 5 | | +// 6 | | 6 + - - - - + +// 7 + - - + 7 +// */ +// CGRect innerClipping1 = CGRectMake(3, 2, 4, 6); +// CGRect innerClipping2 = CGRectMake(2, 3, 6, 4); +// CGRect outterClipping = CGRectMake(2, 2, 6, 6); +// for (int i = 0; i < 10; i++) { +// for (int j = 0; j < 10; j++) { +// CGPoint point = CGPointMake(i, j); +// int alpha = [self alphaOfPoint:CGPointMake(i, j) onView:mockFlutterView]; +// if (CGRectContainsPoint(innerClipping1, point) || +// CGRectContainsPoint(innerClipping2, point)) { +// // Pixels inside either of the 2 inner clippings should be fully opaque. +// XCTAssertEqual(alpha, 255); +// } else if (CGRectContainsPoint(outterClipping, point)) { +// // Corner pixels (i.e. (2, 2), (2, 7), (7, 2) and (7, 7)) should be partially transparent. +// XCTAssert(0 < alpha && alpha < 255); +// } else { +// // Pixels outside outterClipping should be fully transparent. +// XCTAssertEqual(alpha, 0); +// } +// } +// } +// } + +// - (void)testSetFlutterViewControllerAfterCreateCanStillDispatchTouchEvents { +// 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(); +// auto platform_view = std::make_unique( +// /*delegate=*/mock_delegate, +// /*rendering_api=*/mock_delegate.settings_.enable_impeller +// ? flutter::IOSRenderingAPI::kMetal +// : flutter::IOSRenderingAPI::kSoftware, +// /*platform_views_controller=*/flutterPlatformViewsController, +// /*task_runners=*/runners, +// /*worker_task_runner=*/nil, +// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + +// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = +// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; +// flutterPlatformViewsController->RegisterViewFactory( +// factory, @"MockFlutterPlatformView", +// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); +// FlutterResult result = ^(id result) { +// }; +// flutterPlatformViewsController->OnMethodCall( +// [FlutterMethodCall +// methodCallWithMethodName:@"create" +// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], +// result); + +// XCTAssertNotNil(gMockPlatformView); + +// // Find touch inteceptor view +// UIView* touchInteceptorView = gMockPlatformView; +// while (touchInteceptorView != nil && +// ![touchInteceptorView isKindOfClass:[FlutterTouchInterceptingView class]]) { +// touchInteceptorView = touchInteceptorView.superview; +// } +// XCTAssertNotNil(touchInteceptorView); + +// // Find ForwardGestureRecognizer +// UIGestureRecognizer* forwardGectureRecognizer = nil; +// for (UIGestureRecognizer* gestureRecognizer in touchInteceptorView.gestureRecognizers) { +// if ([gestureRecognizer isKindOfClass:NSClassFromString(@"ForwardingGestureRecognizer")]) { +// forwardGectureRecognizer = gestureRecognizer; +// break; +// } +// } + +// // Before setting flutter view controller, events are not dispatched. +// NSSet* touches1 = [[NSSet alloc] init]; +// id event1 = OCMClassMock([UIEvent class]); +// id mockFlutterViewContoller = OCMClassMock([FlutterViewController class]); +// [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1]; +// OCMReject([mockFlutterViewContoller touchesBegan:touches1 withEvent:event1]); + +// // Set flutter view controller allows events to be dispatched. +// NSSet* touches2 = [[NSSet alloc] init]; +// id event2 = OCMClassMock([UIEvent class]); +// flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller); +// [forwardGectureRecognizer touchesBegan:touches2 withEvent:event2]; +// OCMVerify([mockFlutterViewContoller touchesBegan:touches2 withEvent:event2]); +// } + +// - (void)testSetFlutterViewControllerInTheMiddleOfTouchEventShouldStillAllowGesturesToBeHandled { +// 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(); +// auto platform_view = std::make_unique( +// /*delegate=*/mock_delegate, +// /*rendering_api=*/mock_delegate.settings_.enable_impeller +// ? flutter::IOSRenderingAPI::kMetal +// : flutter::IOSRenderingAPI::kSoftware, +// /*platform_views_controller=*/flutterPlatformViewsController, +// /*task_runners=*/runners, +// /*worker_task_runner=*/nil, +// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + +// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = +// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; +// flutterPlatformViewsController->RegisterViewFactory( +// factory, @"MockFlutterPlatformView", +// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); +// FlutterResult result = ^(id result) { +// }; +// flutterPlatformViewsController->OnMethodCall( +// [FlutterMethodCall +// methodCallWithMethodName:@"create" +// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], +// result); + +// XCTAssertNotNil(gMockPlatformView); + +// // Find touch inteceptor view +// UIView* touchInteceptorView = gMockPlatformView; +// while (touchInteceptorView != nil && +// ![touchInteceptorView isKindOfClass:[FlutterTouchInterceptingView class]]) { +// touchInteceptorView = touchInteceptorView.superview; +// } +// XCTAssertNotNil(touchInteceptorView); + +// // Find ForwardGestureRecognizer +// UIGestureRecognizer* forwardGectureRecognizer = nil; +// for (UIGestureRecognizer* gestureRecognizer in touchInteceptorView.gestureRecognizers) { +// if ([gestureRecognizer isKindOfClass:NSClassFromString(@"ForwardingGestureRecognizer")]) { +// forwardGectureRecognizer = gestureRecognizer; +// break; +// } +// } +// id mockFlutterViewContoller = OCMClassMock([FlutterViewController class]); +// { +// // ***** Sequence 1, finishing touch event with touchEnded ***** // +// flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller); + +// NSSet* touches1 = [[NSSet alloc] init]; +// id event1 = OCMClassMock([UIEvent class]); +// [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1]; +// OCMVerify([mockFlutterViewContoller touchesBegan:touches1 withEvent:event1]); + +// flutterPlatformViewsController->SetFlutterViewController(nil); + +// // Allow the touch events to finish +// NSSet* touches2 = [[NSSet alloc] init]; +// id event2 = OCMClassMock([UIEvent class]); +// [forwardGectureRecognizer touchesMoved:touches2 withEvent:event2]; +// OCMVerify([mockFlutterViewContoller touchesMoved:touches2 withEvent:event2]); + +// NSSet* touches3 = [[NSSet alloc] init]; +// id event3 = OCMClassMock([UIEvent class]); +// [forwardGectureRecognizer touchesEnded:touches3 withEvent:event3]; +// OCMVerify([mockFlutterViewContoller touchesEnded:touches3 withEvent:event3]); + +// // Now the 2nd touch sequence should not be allowed. +// NSSet* touches4 = [[NSSet alloc] init]; +// id event4 = OCMClassMock([UIEvent class]); +// [forwardGectureRecognizer touchesBegan:touches4 withEvent:event4]; +// OCMReject([mockFlutterViewContoller touchesBegan:touches4 withEvent:event4]); + +// NSSet* touches5 = [[NSSet alloc] init]; +// id event5 = OCMClassMock([UIEvent class]); +// [forwardGectureRecognizer touchesEnded:touches5 withEvent:event5]; +// OCMReject([mockFlutterViewContoller touchesEnded:touches5 withEvent:event5]); +// } + +// { +// // ***** Sequence 2, finishing touch event with touchCancelled ***** // +// flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller); + +// NSSet* touches1 = [[NSSet alloc] init]; +// id event1 = OCMClassMock([UIEvent class]); +// [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1]; +// OCMVerify([mockFlutterViewContoller touchesBegan:touches1 withEvent:event1]); + +// flutterPlatformViewsController->SetFlutterViewController(nil); + +// // Allow the touch events to finish +// NSSet* touches2 = [[NSSet alloc] init]; +// id event2 = OCMClassMock([UIEvent class]); +// [forwardGectureRecognizer touchesMoved:touches2 withEvent:event2]; +// OCMVerify([mockFlutterViewContoller touchesMoved:touches2 withEvent:event2]); + +// NSSet* touches3 = [[NSSet alloc] init]; +// id event3 = OCMClassMock([UIEvent class]); +// [forwardGectureRecognizer touchesCancelled:touches3 withEvent:event3]; +// OCMVerify([mockFlutterViewContoller forceTouchesCancelled:touches3]); + +// // Now the 2nd touch sequence should not be allowed. +// NSSet* touches4 = [[NSSet alloc] init]; +// id event4 = OCMClassMock([UIEvent class]); +// [forwardGectureRecognizer touchesBegan:touches4 withEvent:event4]; +// OCMReject([mockFlutterViewContoller touchesBegan:touches4 withEvent:event4]); + +// NSSet* touches5 = [[NSSet alloc] init]; +// id event5 = OCMClassMock([UIEvent class]); +// [forwardGectureRecognizer touchesEnded:touches5 withEvent:event5]; +// OCMReject([mockFlutterViewContoller touchesEnded:touches5 withEvent:event5]); +// } + +// flutterPlatformViewsController->Reset(); +// } + +// - (void) +// testSetFlutterViewControllerInTheMiddleOfTouchEventAllowsTheNewControllerToHandleSecondTouchSequence { +// 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(); +// auto platform_view = std::make_unique( +// /*delegate=*/mock_delegate, +// /*rendering_api=*/mock_delegate.settings_.enable_impeller +// ? flutter::IOSRenderingAPI::kMetal +// : flutter::IOSRenderingAPI::kSoftware, +// /*platform_views_controller=*/flutterPlatformViewsController, +// /*task_runners=*/runners, +// /*worker_task_runner=*/nil, +// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + +// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = +// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; +// flutterPlatformViewsController->RegisterViewFactory( +// factory, @"MockFlutterPlatformView", +// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); +// FlutterResult result = ^(id result) { +// }; +// flutterPlatformViewsController->OnMethodCall( +// [FlutterMethodCall +// methodCallWithMethodName:@"create" +// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], +// result); + +// XCTAssertNotNil(gMockPlatformView); + +// // Find touch inteceptor view +// UIView* touchInteceptorView = gMockPlatformView; +// while (touchInteceptorView != nil && +// ![touchInteceptorView isKindOfClass:[FlutterTouchInterceptingView class]]) { +// touchInteceptorView = touchInteceptorView.superview; +// } +// XCTAssertNotNil(touchInteceptorView); + +// // Find ForwardGestureRecognizer +// UIGestureRecognizer* forwardGectureRecognizer = nil; +// for (UIGestureRecognizer* gestureRecognizer in touchInteceptorView.gestureRecognizers) { +// if ([gestureRecognizer isKindOfClass:NSClassFromString(@"ForwardingGestureRecognizer")]) { +// forwardGectureRecognizer = gestureRecognizer; +// break; +// } +// } +// id mockFlutterViewContoller = OCMClassMock([FlutterViewController class]); + +// flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller); + +// // The touches in this sequence requires 1 touch object, we always create the NSSet with one item. +// NSSet* touches1 = [NSSet setWithObject:@1]; +// id event1 = OCMClassMock([UIEvent class]); +// [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1]; +// OCMVerify([mockFlutterViewContoller touchesBegan:touches1 withEvent:event1]); + +// FlutterViewController* mockFlutterViewContoller2 = OCMClassMock([FlutterViewController class]); +// flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller2); + +// // Touch events should still send to the old FlutterViewController if FlutterViewController +// // is updated in between. +// NSSet* touches2 = [NSSet setWithObject:@1]; +// id event2 = OCMClassMock([UIEvent class]); +// [forwardGectureRecognizer touchesBegan:touches2 withEvent:event2]; +// OCMVerify([mockFlutterViewContoller touchesBegan:touches2 withEvent:event2]); +// OCMReject([mockFlutterViewContoller2 touchesBegan:touches2 withEvent:event2]); + +// NSSet* touches3 = [NSSet setWithObject:@1]; +// id event3 = OCMClassMock([UIEvent class]); +// [forwardGectureRecognizer touchesMoved:touches3 withEvent:event3]; +// OCMVerify([mockFlutterViewContoller touchesMoved:touches3 withEvent:event3]); +// OCMReject([mockFlutterViewContoller2 touchesMoved:touches3 withEvent:event3]); + +// NSSet* touches4 = [NSSet setWithObject:@1]; +// id event4 = OCMClassMock([UIEvent class]); +// [forwardGectureRecognizer touchesEnded:touches4 withEvent:event4]; +// OCMVerify([mockFlutterViewContoller touchesEnded:touches4 withEvent:event4]); +// OCMReject([mockFlutterViewContoller2 touchesEnded:touches4 withEvent:event4]); + +// NSSet* touches5 = [NSSet setWithObject:@1]; +// id event5 = OCMClassMock([UIEvent class]); +// [forwardGectureRecognizer touchesEnded:touches5 withEvent:event5]; +// OCMVerify([mockFlutterViewContoller touchesEnded:touches5 withEvent:event5]); +// OCMReject([mockFlutterViewContoller2 touchesEnded:touches5 withEvent:event5]); + +// // Now the 2nd touch sequence should go to the new FlutterViewController + +// NSSet* touches6 = [NSSet setWithObject:@1]; +// id event6 = OCMClassMock([UIEvent class]); +// [forwardGectureRecognizer touchesBegan:touches6 withEvent:event6]; +// OCMVerify([mockFlutterViewContoller2 touchesBegan:touches6 withEvent:event6]); +// OCMReject([mockFlutterViewContoller touchesBegan:touches6 withEvent:event6]); + +// // Allow the touch events to finish +// NSSet* touches7 = [NSSet setWithObject:@1]; +// id event7 = OCMClassMock([UIEvent class]); +// [forwardGectureRecognizer touchesMoved:touches7 withEvent:event7]; +// OCMVerify([mockFlutterViewContoller2 touchesMoved:touches7 withEvent:event7]); +// OCMReject([mockFlutterViewContoller touchesMoved:touches7 withEvent:event7]); + +// NSSet* touches8 = [NSSet setWithObject:@1]; +// id event8 = OCMClassMock([UIEvent class]); +// [forwardGectureRecognizer touchesEnded:touches8 withEvent:event8]; +// OCMVerify([mockFlutterViewContoller2 touchesEnded:touches8 withEvent:event8]); +// OCMReject([mockFlutterViewContoller touchesEnded:touches8 withEvent:event8]); + +// flutterPlatformViewsController->Reset(); +// } + +// - (void)testFlutterPlatformViewTouchesCancelledEventAreForcedToBeCancelled { +// 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(); +// auto platform_view = std::make_unique( +// /*delegate=*/mock_delegate, +// /*rendering_api=*/mock_delegate.settings_.enable_impeller +// ? flutter::IOSRenderingAPI::kMetal +// : flutter::IOSRenderingAPI::kSoftware, +// /*platform_views_controller=*/flutterPlatformViewsController, +// /*task_runners=*/runners, +// /*worker_task_runner=*/nil, +// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + +// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = +// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; +// flutterPlatformViewsController->RegisterViewFactory( +// factory, @"MockFlutterPlatformView", +// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); +// FlutterResult result = ^(id result) { +// }; +// flutterPlatformViewsController->OnMethodCall( +// [FlutterMethodCall +// methodCallWithMethodName:@"create" +// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], +// result); + +// XCTAssertNotNil(gMockPlatformView); + +// // Find touch inteceptor view +// UIView* touchInteceptorView = gMockPlatformView; +// while (touchInteceptorView != nil && +// ![touchInteceptorView isKindOfClass:[FlutterTouchInterceptingView class]]) { +// touchInteceptorView = touchInteceptorView.superview; +// } +// XCTAssertNotNil(touchInteceptorView); + +// // Find ForwardGestureRecognizer +// UIGestureRecognizer* forwardGectureRecognizer = nil; +// for (UIGestureRecognizer* gestureRecognizer in touchInteceptorView.gestureRecognizers) { +// if ([gestureRecognizer isKindOfClass:NSClassFromString(@"ForwardingGestureRecognizer")]) { +// forwardGectureRecognizer = gestureRecognizer; +// break; +// } +// } +// id mockFlutterViewContoller = OCMClassMock([FlutterViewController class]); + +// flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller); + +// NSSet* touches1 = [NSSet setWithObject:@1]; +// id event1 = OCMClassMock([UIEvent class]); +// [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1]; + +// [forwardGectureRecognizer touchesCancelled:touches1 withEvent:event1]; +// OCMVerify([mockFlutterViewContoller forceTouchesCancelled:touches1]); + +// flutterPlatformViewsController->Reset(); +// } + +// - (void)testFlutterPlatformViewControllerSubmitFrameWithoutFlutterViewNotCrashing { +// 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(); +// auto platform_view = std::make_unique( +// /*delegate=*/mock_delegate, +// /*rendering_api=*/mock_delegate.settings_.enable_impeller +// ? flutter::IOSRenderingAPI::kMetal +// : flutter::IOSRenderingAPI::kSoftware, +// /*platform_views_controller=*/flutterPlatformViewsController, +// /*task_runners=*/runners, +// /*worker_task_runner=*/nil, +// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + +// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = +// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; +// flutterPlatformViewsController->RegisterViewFactory( +// factory, @"MockFlutterPlatformView", +// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); +// FlutterResult result = ^(id result) { +// }; +// flutterPlatformViewsController->OnMethodCall( +// [FlutterMethodCall +// methodCallWithMethodName:@"create" +// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], +// result); + +// XCTAssertNotNil(gMockPlatformView); + +// // Create embedded view params +// flutter::MutatorsStack stack; +// SkMatrix finalMatrix; + +// auto embeddedViewParams_1 = +// std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); + +// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams_1)); +// flutterPlatformViewsController->CompositeEmbeddedView(2); +// flutter::SurfaceFrame::FramebufferInfo framebuffer_info; +// auto mock_surface = std::make_unique( +// nullptr, framebuffer_info, +// [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return false; }, +// /*frame_size=*/SkISize::Make(800, 600)); +// XCTAssertFalse( +// flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface))); + +// auto embeddedViewParams_2 = +// std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); +// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams_2)); +// flutterPlatformViewsController->CompositeEmbeddedView(2); +// auto mock_surface_submit_true = std::make_unique( +// nullptr, 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_submit_true))); +// } + +// - (void) +// testFlutterPlatformViewControllerResetDeallocsPlatformViewWhenRootViewsNotBindedToFlutterView { +// 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(); +// auto platform_view = std::make_unique( +// /*delegate=*/mock_delegate, +// /*rendering_api=*/mock_delegate.settings_.enable_impeller +// ? flutter::IOSRenderingAPI::kMetal +// : flutter::IOSRenderingAPI::kSoftware, +// /*platform_views_controller=*/flutterPlatformViewsController, +// /*task_runners=*/runners, +// /*worker_task_runner=*/nil, +// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + +// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; +// flutterPlatformViewsController->SetFlutterView(mockFlutterView); + +// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = +// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; +// flutterPlatformViewsController->RegisterViewFactory( +// factory, @"MockFlutterPlatformView", +// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); +// FlutterResult result = ^(id result) { +// }; +// // autorelease pool to trigger an autorelease for all the root_views_ and touch_interceptors_. +// @autoreleasepool { +// flutterPlatformViewsController->OnMethodCall( +// [FlutterMethodCall +// methodCallWithMethodName:@"create" +// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], +// result); + +// flutter::MutatorsStack stack; +// SkMatrix finalMatrix; +// auto embeddedViewParams = +// std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); +// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); +// flutterPlatformViewsController->CompositeEmbeddedView(2); +// // Not calling |flutterPlatformViewsController::SubmitFrame| so that the platform views are not +// // added to flutter_view_. + +// XCTAssertNotNil(gMockPlatformView); +// flutterPlatformViewsController->Reset(); +// } +// XCTAssertNil(gMockPlatformView); +// } + +// - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { +// 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(); +// auto platform_view = std::make_unique( +// /*delegate=*/mock_delegate, +// /*rendering_api=*/mock_delegate.settings_.enable_impeller +// ? flutter::IOSRenderingAPI::kMetal +// : flutter::IOSRenderingAPI::kSoftware, +// /*platform_views_controller=*/flutterPlatformViewsController, +// /*task_runners=*/runners, +// /*worker_task_runner=*/nil, +// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + +// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; +// flutterPlatformViewsController->SetFlutterView(mockFlutterView); + +// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = +// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; +// flutterPlatformViewsController->RegisterViewFactory( +// factory, @"MockFlutterPlatformView", +// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); +// FlutterResult result = ^(id result) { +// }; + +// flutterPlatformViewsController->OnMethodCall( +// [FlutterMethodCall +// methodCallWithMethodName:@"create" +// arguments:@{@"id" : @0, @"viewType" : @"MockFlutterPlatformView"}], +// result); + +// // First frame, |EmbeddedViewCount| is not empty after composite. +// flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300)); +// flutter::MutatorsStack stack; +// SkMatrix finalMatrix; +// auto embeddedViewParams1 = +// std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); +// flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1)); +// flutterPlatformViewsController->CompositeEmbeddedView(0); +// XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 1UL); + +// // Second frame, |EmbeddedViewCount| should be empty at the start +// flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300)); +// XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 0UL); + +// auto embeddedViewParams2 = +// std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); +// flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams2)); +// flutterPlatformViewsController->CompositeEmbeddedView(0); +// XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 1UL); +// } + +// - (void) +// testFlutterPlatformViewControllerSubmitFrameShouldOrderSubviewsCorrectlyWithDifferentViewHierarchy { +// 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(); +// auto platform_view = std::make_unique( +// /*delegate=*/mock_delegate, +// /*rendering_api=*/mock_delegate.settings_.enable_impeller +// ? flutter::IOSRenderingAPI::kMetal +// : flutter::IOSRenderingAPI::kSoftware, +// /*platform_views_controller=*/flutterPlatformViewsController, +// /*task_runners=*/runners, +// /*worker_task_runner=*/nil, +// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + +// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; +// flutterPlatformViewsController->SetFlutterView(mockFlutterView); + +// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = +// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; +// 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(finalMatrix, SkSize::Make(300, 300), stack); +// flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1)); +// flutterPlatformViewsController->CompositeEmbeddedView(0); +// auto embeddedViewParams2 = +// std::make_unique(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 mock_sk_surface = SkSurfaces::Raster(image_info); +// flutter::SurfaceFrame::FramebufferInfo framebuffer_info; +// auto mock_surface = std::make_unique( +// 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 opposite order. +// embeddedViewParams2 = +// std::make_unique(finalMatrix, SkSize::Make(500, 500), stack); +// flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams2)); +// flutterPlatformViewsController->CompositeEmbeddedView(1); +// embeddedViewParams1 = +// std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); +// flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1)); +// flutterPlatformViewsController->CompositeEmbeddedView(0); + +// mock_sk_surface = SkSurfaces::Raster(image_info); +// mock_surface = std::make_unique( +// 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 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(); +// auto platform_view = std::make_unique( +// /*delegate=*/mock_delegate, +// /*rendering_api=*/mock_delegate.settings_.enable_impeller +// ? flutter::IOSRenderingAPI::kMetal +// : flutter::IOSRenderingAPI::kSoftware, +// /*platform_views_controller=*/flutterPlatformViewsController, +// /*task_runners=*/runners, +// /*worker_task_runner=*/nil, +// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + +// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; +// flutterPlatformViewsController->SetFlutterView(mockFlutterView); + +// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = +// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; +// 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(finalMatrix, SkSize::Make(300, 300), stack); +// flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1)); +// flutterPlatformViewsController->CompositeEmbeddedView(0); +// auto embeddedViewParams2 = +// std::make_unique(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 mock_sk_surface = SkSurfaces::Raster(image_info); +// flutter::SurfaceFrame::FramebufferInfo framebuffer_info; +// auto mock_surface = std::make_unique( +// 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(finalMatrix, SkSize::Make(300, 300), stack); +// flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1)); +// flutterPlatformViewsController->CompositeEmbeddedView(0); +// embeddedViewParams2 = +// std::make_unique(finalMatrix, SkSize::Make(500, 500), stack); +// flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams2)); +// flutterPlatformViewsController->CompositeEmbeddedView(1); + +// mock_sk_surface = SkSurfaces::Raster(image_info); +// mock_surface = std::make_unique( +// 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"); +// auto thread_task_runner_other = CreateNewThread("FlutterPlatformViewsTest2"); +// flutter::TaskRunners runners(/*label=*/self.name.UTF8String, +// /*platform=*/thread_task_runner_platform, +// /*raster=*/thread_task_runner_other, +// /*ui=*/thread_task_runner_other, +// /*io=*/thread_task_runner_other); +// auto flutterPlatformViewsController = std::make_shared(); +// auto platform_view = std::make_unique( +// /*delegate=*/mock_delegate, +// /*rendering_api=*/mock_delegate.settings_.enable_impeller +// ? flutter::IOSRenderingAPI::kMetal +// : flutter::IOSRenderingAPI::kSoftware, +// /*platform_views_controller=*/flutterPlatformViewsController, +// /*task_runners=*/runners, +// /*worker_task_runner=*/nil, +// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + +// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; +// flutterPlatformViewsController->SetFlutterView(mockFlutterView); + +// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = +// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; +// flutterPlatformViewsController->RegisterViewFactory( +// factory, @"MockFlutterPlatformView", +// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); +// XCTestExpectation* waitForPlatformView = +// [self expectationWithDescription:@"wait for platform view to be created"]; +// FlutterResult result = ^(id result) { +// [waitForPlatformView fulfill]; +// }; + +// flutterPlatformViewsController->OnMethodCall( +// [FlutterMethodCall +// methodCallWithMethodName:@"create" +// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], +// result); +// [self waitForExpectations:@[ waitForPlatformView ] timeout:30]; +// XCTAssertNotNil(gMockPlatformView); + +// flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300)); +// SkMatrix finalMatrix; +// flutter::MutatorsStack stack; +// auto embeddedViewParams = +// std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); +// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); + +// fml::RefPtr raster_thread_merger = +// fml::MakeRefCounted(thread_task_runner_platform->GetTaskQueueId(), +// thread_task_runner_other->GetTaskQueueId()); +// XCTAssertEqual(flutterPlatformViewsController->PostPrerollAction(raster_thread_merger), +// flutter::PostPrerollResult::kSkipAndRetryFrame); +// XCTAssertFalse(raster_thread_merger->IsMerged()); + +// flutterPlatformViewsController->EndFrame(true, raster_thread_merger); +// XCTAssertTrue(raster_thread_merger->IsMerged()); + +// // Unmerge threads before the end of the test +// // TaskRunners are required to be unmerged before destruction. +// while (raster_thread_merger->DecrementLease() != fml::RasterThreadStatus::kUnmergedNow) { +// } +// } + +// - (int)alphaOfPoint:(CGPoint)point onView:(UIView*)view { +// unsigned char pixel[4] = {0}; + +// CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + +// // Draw the pixel on `point` in the context. +// CGContextRef context = CGBitmapContextCreate( +// pixel, 1, 1, 8, 4, colorSpace, kCGBitmapAlphaInfoMask & kCGImageAlphaPremultipliedLast); +// CGContextTranslateCTM(context, -point.x, -point.y); +// [view.layer renderInContext:context]; + +// CGContextRelease(context); +// CGColorSpaceRelease(colorSpace); +// // Get the alpha from the pixel that we just rendered. +// return pixel[3]; +// } + +// - (void)testHasFirstResponderInViewHierarchySubtree_viewItselfBecomesFirstResponder { +// // For view to become the first responder, it must be a descendant of a UIWindow +// UIWindow* window = [[UIWindow alloc] init]; +// UITextField* textField = [[UITextField alloc] init]; +// [window addSubview:textField]; + +// [textField becomeFirstResponder]; +// XCTAssertTrue(textField.isFirstResponder); +// XCTAssertTrue(textField.flt_hasFirstResponderInViewHierarchySubtree); +// [textField resignFirstResponder]; +// XCTAssertFalse(textField.isFirstResponder); +// XCTAssertFalse(textField.flt_hasFirstResponderInViewHierarchySubtree); +// } + +// - (void)testHasFirstResponderInViewHierarchySubtree_descendantViewBecomesFirstResponder { +// // For view to become the first responder, it must be a descendant of a UIWindow +// UIWindow* window = [[UIWindow alloc] init]; +// UIView* view = [[UIView alloc] init]; +// UIView* childView = [[UIView alloc] init]; +// UITextField* textField = [[UITextField alloc] init]; +// [window addSubview:view]; +// [view addSubview:childView]; +// [childView addSubview:textField]; + +// [textField becomeFirstResponder]; +// XCTAssertTrue(textField.isFirstResponder); +// XCTAssertTrue(view.flt_hasFirstResponderInViewHierarchySubtree); +// [textField resignFirstResponder]; +// XCTAssertFalse(textField.isFirstResponder); +// XCTAssertFalse(view.flt_hasFirstResponderInViewHierarchySubtree); +// } + +// - (void)testFlutterClippingMaskViewPoolReuseViewsAfterRecycle { +// FlutterClippingMaskViewPool* pool = [[FlutterClippingMaskViewPool alloc] initWithCapacity:2]; +// FlutterClippingMaskView* view1 = [pool getMaskViewWithFrame:CGRectZero]; +// FlutterClippingMaskView* view2 = [pool getMaskViewWithFrame:CGRectZero]; +// [pool insertViewToPoolIfNeeded:view1]; +// [pool insertViewToPoolIfNeeded:view2]; +// CGRect newRect = CGRectMake(0, 0, 10, 10); +// FlutterClippingMaskView* view3 = [pool getMaskViewWithFrame:newRect]; +// FlutterClippingMaskView* view4 = [pool getMaskViewWithFrame:newRect]; +// // view3 and view4 should randomly get either of view1 and view2. +// NSSet* set1 = [NSSet setWithObjects:view1, view2, nil]; +// NSSet* set2 = [NSSet setWithObjects:view3, view4, nil]; +// XCTAssertEqualObjects(set1, set2); +// XCTAssertTrue(CGRectEqualToRect(view3.frame, newRect)); +// XCTAssertTrue(CGRectEqualToRect(view4.frame, newRect)); +// } + +// - (void)testFlutterClippingMaskViewPoolAllocsNewMaskViewsAfterReachingCapacity { +// FlutterClippingMaskViewPool* pool = [[FlutterClippingMaskViewPool alloc] initWithCapacity:2]; +// FlutterClippingMaskView* view1 = [pool getMaskViewWithFrame:CGRectZero]; +// FlutterClippingMaskView* view2 = [pool getMaskViewWithFrame:CGRectZero]; +// FlutterClippingMaskView* view3 = [pool getMaskViewWithFrame:CGRectZero]; +// XCTAssertNotEqual(view1, view3); +// XCTAssertNotEqual(view2, view3); +// } + +// - (void)testMaskViewsReleasedWhenPoolIsReleased { +// __weak UIView* weakView; +// @autoreleasepool { +// FlutterClippingMaskViewPool* pool = [[FlutterClippingMaskViewPool alloc] initWithCapacity:2]; +// FlutterClippingMaskView* view = [pool getMaskViewWithFrame:CGRectZero]; +// weakView = view; +// XCTAssertNotNil(weakView); +// } +// XCTAssertNil(weakView); +// } + +// - (void)testClipMaskViewIsReused { +// 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(); +// auto platform_view = std::make_unique( +// /*delegate=*/mock_delegate, +// /*rendering_api=*/mock_delegate.settings_.enable_impeller +// ? flutter::IOSRenderingAPI::kMetal +// : flutter::IOSRenderingAPI::kSoftware, +// /*platform_views_controller=*/flutterPlatformViewsController, +// /*task_runners=*/runners, +// /*worker_task_runner=*/nil, +// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + +// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = +// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; +// flutterPlatformViewsController->RegisterViewFactory( +// factory, @"MockFlutterPlatformView", +// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); +// FlutterResult result = ^(id result) { +// }; +// flutterPlatformViewsController->OnMethodCall( +// [FlutterMethodCall +// methodCallWithMethodName:@"create" +// arguments:@{@"id" : @1, @"viewType" : @"MockFlutterPlatformView"}], +// result); + +// XCTAssertNotNil(gMockPlatformView); +// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; +// flutterPlatformViewsController->SetFlutterView(mockFlutterView); +// // Create embedded view params +// flutter::MutatorsStack stack1; +// // Layer tree always pushes a screen scale factor to the stack +// SkMatrix screenScaleMatrix = +// SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); +// stack1.PushTransform(screenScaleMatrix); +// // Push a clip rect +// SkRect rect = SkRect::MakeXYWH(2, 2, 3, 3); +// stack1.PushClipRect(rect); + +// auto embeddedViewParams1 = std::make_unique( +// screenScaleMatrix, SkSize::Make(10, 10), stack1); + +// flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1)); +// flutterPlatformViewsController->CompositeEmbeddedView(1); +// UIView* childClippingView1 = gMockPlatformView.superview.superview; +// UIView* maskView1 = childClippingView1.maskView; +// XCTAssertNotNil(maskView1); + +// // Composite a new frame. +// flutterPlatformViewsController->BeginFrame(SkISize::Make(100, 100)); +// flutter::MutatorsStack stack2; +// auto embeddedViewParams2 = std::make_unique( +// screenScaleMatrix, SkSize::Make(10, 10), stack2); +// auto embeddedViewParams3 = std::make_unique( +// screenScaleMatrix, SkSize::Make(10, 10), stack2); +// flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams3)); +// flutterPlatformViewsController->CompositeEmbeddedView(1); +// childClippingView1 = gMockPlatformView.superview.superview; + +// // This overrides gMockPlatformView to point to the newly created platform view. +// flutterPlatformViewsController->OnMethodCall( +// [FlutterMethodCall +// methodCallWithMethodName:@"create" +// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], +// result); + +// auto embeddedViewParams4 = std::make_unique( +// screenScaleMatrix, SkSize::Make(10, 10), stack1); +// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams4)); +// flutterPlatformViewsController->CompositeEmbeddedView(2); +// UIView* childClippingView2 = gMockPlatformView.superview.superview; + +// UIView* maskView2 = childClippingView2.maskView; +// XCTAssertEqual(maskView1, maskView2); +// XCTAssertNotNil(childClippingView2.maskView); +// XCTAssertNil(childClippingView1.maskView); +// } + +// - (void)testDifferentClipMaskViewIsUsedForEachView { +// 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(); +// auto platform_view = std::make_unique( +// /*delegate=*/mock_delegate, +// /*rendering_api=*/mock_delegate.settings_.enable_impeller +// ? flutter::IOSRenderingAPI::kMetal +// : flutter::IOSRenderingAPI::kSoftware, +// /*platform_views_controller=*/flutterPlatformViewsController, +// /*task_runners=*/runners, +// /*worker_task_runner=*/nil, +// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + +// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = +// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; +// flutterPlatformViewsController->RegisterViewFactory( +// factory, @"MockFlutterPlatformView", +// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); +// FlutterResult result = ^(id result) { +// }; + +// flutterPlatformViewsController->OnMethodCall( +// [FlutterMethodCall +// methodCallWithMethodName:@"create" +// arguments:@{@"id" : @1, @"viewType" : @"MockFlutterPlatformView"}], +// result); +// UIView* view1 = gMockPlatformView; + +// // This overwrites `gMockPlatformView` to another view. +// flutterPlatformViewsController->OnMethodCall( +// [FlutterMethodCall +// methodCallWithMethodName:@"create" +// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], +// result); +// UIView* view2 = gMockPlatformView; + +// XCTAssertNotNil(gMockPlatformView); +// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; +// flutterPlatformViewsController->SetFlutterView(mockFlutterView); +// // Create embedded view params +// flutter::MutatorsStack stack1; +// // Layer tree always pushes a screen scale factor to the stack +// SkMatrix screenScaleMatrix = +// SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); +// stack1.PushTransform(screenScaleMatrix); +// // Push a clip rect +// SkRect rect = SkRect::MakeXYWH(2, 2, 3, 3); +// stack1.PushClipRect(rect); + +// auto embeddedViewParams1 = std::make_unique( +// screenScaleMatrix, SkSize::Make(10, 10), stack1); + +// flutter::MutatorsStack stack2; +// stack2.PushClipRect(rect); +// auto embeddedViewParams2 = std::make_unique( +// screenScaleMatrix, SkSize::Make(10, 10), stack2); + +// flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1)); +// flutterPlatformViewsController->CompositeEmbeddedView(1); +// UIView* childClippingView1 = view1.superview.superview; + +// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams2)); +// flutterPlatformViewsController->CompositeEmbeddedView(2); +// UIView* childClippingView2 = view2.superview.superview; +// UIView* maskView1 = childClippingView1.maskView; +// UIView* maskView2 = childClippingView2.maskView; +// XCTAssertNotEqual(maskView1, maskView2); +// } + +// - (void)testMaskViewUsesCAShapeLayerAsTheBackingLayer { +// 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(); +// auto platform_view = std::make_unique( +// /*delegate=*/mock_delegate, +// /*rendering_api=*/mock_delegate.settings_.enable_impeller +// ? flutter::IOSRenderingAPI::kMetal +// : flutter::IOSRenderingAPI::kSoftware, +// /*platform_views_controller=*/flutterPlatformViewsController, +// /*task_runners=*/runners, +// /*worker_task_runner=*/nil, +// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + +// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = +// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; +// flutterPlatformViewsController->RegisterViewFactory( +// factory, @"MockFlutterPlatformView", +// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); +// FlutterResult result = ^(id result) { +// }; + +// flutterPlatformViewsController->OnMethodCall( +// [FlutterMethodCall +// methodCallWithMethodName:@"create" +// arguments:@{@"id" : @1, @"viewType" : @"MockFlutterPlatformView"}], +// result); + +// XCTAssertNotNil(gMockPlatformView); +// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; +// flutterPlatformViewsController->SetFlutterView(mockFlutterView); +// // Create embedded view params +// flutter::MutatorsStack stack1; +// // Layer tree always pushes a screen scale factor to the stack +// SkMatrix screenScaleMatrix = +// SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); +// stack1.PushTransform(screenScaleMatrix); +// // Push a clip rect +// SkRect rect = SkRect::MakeXYWH(2, 2, 3, 3); +// stack1.PushClipRect(rect); + +// auto embeddedViewParams1 = std::make_unique( +// screenScaleMatrix, SkSize::Make(10, 10), stack1); + +// flutter::MutatorsStack stack2; +// stack2.PushClipRect(rect); +// auto embeddedViewParams2 = std::make_unique( +// screenScaleMatrix, SkSize::Make(10, 10), stack2); + +// flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1)); +// flutterPlatformViewsController->CompositeEmbeddedView(1); +// UIView* childClippingView = gMockPlatformView.superview.superview; + +// UIView* maskView = childClippingView.maskView; +// XCTAssert([maskView.layer isKindOfClass:[CAShapeLayer class]], +// @"Mask view must use CAShapeLayer as its backing layer."); +// } + +// // Return true if a correct visual effect view is found. It also implies all the validation in this +// // method passes. +// // +// // There are two fail states for this method. 1. One of the XCTAssert method failed; or 2. No +// // correct visual effect view found. +// - (BOOL)validateOneVisualEffectView:(UIView*)visualEffectView +// expectedFrame:(CGRect)frame +// inputRadius:(CGFloat)inputRadius { +// XCTAssertTrue(CGRectEqualToRect(visualEffectView.frame, frame)); +// for (UIView* view in visualEffectView.subviews) { +// if (![NSStringFromClass([view class]) hasSuffix:@"BackdropView"]) { +// continue; +// } +// XCTAssertEqual(view.layer.filters.count, 1u); +// NSObject* filter = view.layer.filters.firstObject; + +// XCTAssertEqualObjects([filter valueForKey:@"name"], @"gaussianBlur"); + +// NSObject* inputRadiusInFilter = [filter valueForKey:@"inputRadius"]; +// XCTAssertTrue([inputRadiusInFilter isKindOfClass:[NSNumber class]] && +// flutter::BlurRadiusEqualToBlurRadius(((NSNumber*)inputRadiusInFilter).floatValue, +// inputRadius)); +// return YES; +// } +// return NO; +// } + +// - (void)testDisposingViewInCompositionOrderDoNotCrash { +// 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(); +// auto platform_view = std::make_unique( +// /*delegate=*/mock_delegate, +// /*rendering_api=*/mock_delegate.settings_.enable_impeller +// ? flutter::IOSRenderingAPI::kMetal +// : flutter::IOSRenderingAPI::kSoftware, +// /*platform_views_controller=*/flutterPlatformViewsController, +// /*task_runners=*/runners, +// /*worker_task_runner=*/nil, +// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + +// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; +// flutterPlatformViewsController->SetFlutterView(mockFlutterView); + +// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = +// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; +// flutterPlatformViewsController->RegisterViewFactory( +// factory, @"MockFlutterPlatformView", +// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); +// FlutterResult result = ^(id result) { +// }; + +// flutterPlatformViewsController->OnMethodCall( +// [FlutterMethodCall +// methodCallWithMethodName:@"create" +// arguments:@{@"id" : @0, @"viewType" : @"MockFlutterPlatformView"}], +// result); +// flutterPlatformViewsController->OnMethodCall( +// [FlutterMethodCall +// methodCallWithMethodName:@"create" +// arguments:@{@"id" : @1, @"viewType" : @"MockFlutterPlatformView"}], +// result); + +// { +// // **** First frame, view id 0, 1 in the composition_order_, disposing view 0 is called. **** // +// // No view should be disposed, or removed from the composition order. +// flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300)); +// flutter::MutatorsStack stack; +// SkMatrix finalMatrix; +// auto embeddedViewParams0 = +// std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); +// flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams0)); +// flutterPlatformViewsController->CompositeEmbeddedView(0); + +// auto embeddedViewParams1 = +// std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); +// flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1)); +// flutterPlatformViewsController->CompositeEmbeddedView(1); +// XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 2UL); + +// XCTestExpectation* expectation = [self expectationWithDescription:@"dispose call ended."]; +// FlutterResult disposeResult = ^(id result) { +// [expectation fulfill]; +// }; + +// flutterPlatformViewsController->OnMethodCall( +// [FlutterMethodCall methodCallWithMethodName:@"dispose" arguments:@0], disposeResult); +// [self waitForExpectationsWithTimeout:30 handler:nil]; + +// const SkImageInfo image_info = SkImageInfo::MakeN32Premul(1000, 1000); +// sk_sp mock_sk_surface = SkSurfaces::Raster(image_info); +// flutter::SurfaceFrame::FramebufferInfo framebuffer_info; +// auto mock_surface = std::make_unique( +// 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))); + +// // Disposing won't remove embedded views until the view is removed from the composition_order_ +// XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 2UL); +// XCTAssertNotNil(flutterPlatformViewsController->GetPlatformViewByID(0)); +// XCTAssertNotNil(flutterPlatformViewsController->GetPlatformViewByID(1)); +// } + +// { +// // **** Second frame, view id 1 in the composition_order_, no disposing view is called, **** // +// // View 0 is removed from the composition order in this frame, hence also disposed. +// flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300)); +// flutter::MutatorsStack stack; +// SkMatrix finalMatrix; +// auto embeddedViewParams1 = +// std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); +// flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1)); +// flutterPlatformViewsController->CompositeEmbeddedView(1); + +// const SkImageInfo image_info = SkImageInfo::MakeN32Premul(1000, 1000); +// sk_sp mock_sk_surface = SkSurfaces::Raster(image_info); +// flutter::SurfaceFrame::FramebufferInfo framebuffer_info; +// auto mock_surface = std::make_unique( +// 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))); + +// // Disposing won't remove embedded views until the view is removed from the composition_order_ +// XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 1UL); +// XCTAssertNil(flutterPlatformViewsController->GetPlatformViewByID(0)); +// XCTAssertNotNil(flutterPlatformViewsController->GetPlatformViewByID(1)); +// } +// } +// - (void)testOnlyPlatformViewsAreRemovedWhenReset { +// 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(); +// auto platform_view = std::make_unique( +// /*delegate=*/mock_delegate, +// /*rendering_api=*/mock_delegate.settings_.enable_impeller +// ? flutter::IOSRenderingAPI::kMetal +// : flutter::IOSRenderingAPI::kSoftware, +// /*platform_views_controller=*/flutterPlatformViewsController, +// /*task_runners=*/runners, +// /*worker_task_runner=*/nil, +// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + +// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = +// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; +// flutterPlatformViewsController->RegisterViewFactory( +// factory, @"MockFlutterPlatformView", +// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); +// FlutterResult result = ^(id result) { +// }; +// flutterPlatformViewsController->OnMethodCall( +// [FlutterMethodCall +// methodCallWithMethodName:@"create" +// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], +// result); +// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; +// flutterPlatformViewsController->SetFlutterView(mockFlutterView); +// // Create embedded view params +// flutter::MutatorsStack stack; +// // Layer tree always pushes a screen scale factor to the stack +// SkMatrix screenScaleMatrix = +// SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); +// stack.PushTransform(screenScaleMatrix); +// // Push a translate matrix +// SkMatrix translateMatrix = SkMatrix::Translate(100, 100); +// stack.PushTransform(translateMatrix); +// SkMatrix finalMatrix; +// finalMatrix.setConcat(screenScaleMatrix, translateMatrix); + +// auto embeddedViewParams = +// std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); + +// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); +// flutterPlatformViewsController->CompositeEmbeddedView(2); + +// // SKSurface is required if the root FlutterView is present. +// const SkImageInfo image_info = SkImageInfo::MakeN32Premul(1000, 1000); +// sk_sp mock_sk_surface = SkSurfaces::Raster(image_info); +// flutter::SurfaceFrame::FramebufferInfo framebuffer_info; +// auto mock_surface = std::make_unique( +// std::move(mock_sk_surface), framebuffer_info, +// [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; }, +// /*frame_size=*/SkISize::Make(800, 600)); + +// flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface)); + +// UIView* someView = [[UIView alloc] init]; +// [mockFlutterView addSubview:someView]; + +// flutterPlatformViewsController->Reset(); +// XCTAssertEqual(mockFlutterView.subviews.count, 1u); +// XCTAssertEqual(mockFlutterView.subviews.firstObject, someView); +// } + +// - (void)testFlutterTouchInterceptingViewLinksToAccessibilityContainer { +// FlutterTouchInterceptingView* touchInteceptorView = [[FlutterTouchInterceptingView alloc] init]; +// NSObject* container = [[NSObject alloc] init]; +// [touchInteceptorView setFlutterAccessibilityContainer:container]; +// XCTAssertEqualObjects([touchInteceptorView accessibilityContainer], container); +// } + +// @end diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index 3f84587d515b1..2ac37e25756c6 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -278,7 +278,8 @@ class FlutterPlatformViewsController { bool SubmitFrame(GrDirectContext* gr_context, const std::shared_ptr& ios_context, - std::unique_ptr frame); + std::unique_ptr frame, + fml::RefPtr platform_task_runner); void OnMethodCall(FlutterMethodCall* call, FlutterResult result) __attribute__((cf_audited_transfer)); diff --git a/shell/platform/darwin/ios/ios_external_view_embedder.h b/shell/platform/darwin/ios/ios_external_view_embedder.h index 953966378b15a..9118c5d277900 100644 --- a/shell/platform/darwin/ios/ios_external_view_embedder.h +++ b/shell/platform/darwin/ios/ios_external_view_embedder.h @@ -57,7 +57,8 @@ class IOSExternalViewEmbedder : public ExternalViewEmbedder { int64_t flutter_view_id, GrDirectContext* context, const std::shared_ptr& aiks_context, - std::unique_ptr frame) override; + std::unique_ptr frame, + fml::RefPtr platform_task_runner) override; // |ExternalViewEmbedder| void EndFrame(bool should_resubmit_frame, diff --git a/shell/platform/darwin/ios/ios_external_view_embedder.mm b/shell/platform/darwin/ios/ios_external_view_embedder.mm index 0b03e8dd1664a..a71e789b70c1d 100644 --- a/shell/platform/darwin/ios/ios_external_view_embedder.mm +++ b/shell/platform/darwin/ios/ios_external_view_embedder.mm @@ -74,13 +74,14 @@ int64_t flutter_view_id, GrDirectContext* context, const std::shared_ptr& aiks_context, - std::unique_ptr frame) { + std::unique_ptr frame, + fml::RefPtr platform_task_runner) { TRACE_EVENT0("flutter", "IOSExternalViewEmbedder::SubmitFlutterView"); // TODO(dkwingsmt): This class only supports rendering into the implicit view. // Properly support multi-view in the future. FML_DCHECK(flutter_view_id == kFlutterImplicitViewId); FML_CHECK(platform_views_controller_); - platform_views_controller_->SubmitFrame(context, ios_context_, std::move(frame)); + platform_views_controller_->SubmitFrame(context, ios_context_, std::move(frame), platform_task_runner); TRACE_EVENT0("flutter", "IOSExternalViewEmbedder::DidSubmitFrame"); } diff --git a/shell/platform/embedder/embedder_external_view_embedder.cc b/shell/platform/embedder/embedder_external_view_embedder.cc index 01c813c8028c0..0f95b44dfb07a 100644 --- a/shell/platform/embedder/embedder_external_view_embedder.cc +++ b/shell/platform/embedder/embedder_external_view_embedder.cc @@ -422,7 +422,8 @@ void EmbedderExternalViewEmbedder::SubmitFlutterView( int64_t flutter_view_id, GrDirectContext* context, const std::shared_ptr& aiks_context, - std::unique_ptr frame) { + std::unique_ptr frame, + fml::RefPtr platform_runner) { // The unordered_map render_target_cache creates a new entry if the view ID is // unrecognized. EmbedderRenderTargetCache& render_target_cache = diff --git a/shell/platform/embedder/embedder_external_view_embedder.h b/shell/platform/embedder/embedder_external_view_embedder.h index 63f383f8bb4fd..f46b29292db36 100644 --- a/shell/platform/embedder/embedder_external_view_embedder.h +++ b/shell/platform/embedder/embedder_external_view_embedder.h @@ -14,6 +14,7 @@ #include "flutter/fml/macros.h" #include "flutter/shell/platform/embedder/embedder_external_view.h" #include "flutter/shell/platform/embedder/embedder_render_target_cache.h" +#include "fml/task_runner.h" namespace flutter { @@ -106,7 +107,8 @@ class EmbedderExternalViewEmbedder final : public ExternalViewEmbedder { int64_t flutter_view_id, GrDirectContext* context, const std::shared_ptr& aiks_context, - std::unique_ptr frame) override; + std::unique_ptr frame, + fml::RefPtr platform_runner) override; // |ExternalViewEmbedder| DlCanvas* GetRootCanvas() override; From 75f931aec97e5996b24edafc0af512e2ba9cc2a8 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Thu, 11 Jul 2024 19:12:42 -0700 Subject: [PATCH 04/49] hey it almost works. --- flow/surface_frame.h | 6 + impeller/renderer/backend/metal/surface_mtl.h | 11 ++ .../renderer/backend/metal/surface_mtl.mm | 8 + shell/gpu/gpu_surface_metal_impeller.mm | 3 + .../framework/Source/FlutterPlatformViews.mm | 185 ++++++++++-------- .../Source/FlutterPlatformViews_Internal.h | 34 ++-- .../darwin/ios/ios_surface_metal_impeller.mm | 2 +- 7 files changed, 143 insertions(+), 106 deletions(-) diff --git a/flow/surface_frame.h b/flow/surface_frame.h index da5516feaacab..307abc0865ead 100644 --- a/flow/surface_frame.h +++ b/flow/surface_frame.h @@ -14,6 +14,7 @@ #include "flutter/fml/macros.h" #include "flutter/fml/time/time_point.h" +#include "fml/synchronization/count_down_latch.h" #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkSurface.h" @@ -26,6 +27,9 @@ class SurfaceFrame { using SubmitCallback = std::function; + using DeferredSubmit = std::function; + using SubmitReciever = std::function; + // Information about the underlying framebuffer struct FramebufferInfo { // Indicates whether or not the surface supports pixel readback as used in @@ -87,6 +91,8 @@ class SurfaceFrame { // // Defaults to true, which is generally a safe value. bool frame_boundary = true; + + SubmitReciever submit_receiver; }; bool Submit(); diff --git a/impeller/renderer/backend/metal/surface_mtl.h b/impeller/renderer/backend/metal/surface_mtl.h index 5ddc6973d06dd..27be92a505152 100644 --- a/impeller/renderer/backend/metal/surface_mtl.h +++ b/impeller/renderer/backend/metal/surface_mtl.h @@ -8,6 +8,7 @@ #include #include +#include "fml/synchronization/count_down_latch.h" #include "impeller/geometry/rect.h" #include "impeller/renderer/context.h" #include "impeller/renderer/surface.h" @@ -58,6 +59,14 @@ class SurfaceMTL final : public Surface { // Returns a Rect defining the area of the surface in device pixels IRect coverage() const; + using DeferredSubmit = std::function; + using SubmitReciever = std::function; + + void SetSubmitInfo( + const SubmitReciever& submit_reciever) { + submit_reciever_ = submit_reciever; + } + // |Surface| bool Present() const override; @@ -70,6 +79,8 @@ class SurfaceMTL final : public Surface { bool requires_blit_ = false; std::optional clip_rect_; + SubmitReciever submit_reciever_; + static bool ShouldPerformPartialRepaint(std::optional damage_rect); SurfaceMTL(const std::weak_ptr& context, diff --git a/impeller/renderer/backend/metal/surface_mtl.mm b/impeller/renderer/backend/metal/surface_mtl.mm index 9219aca941278..841717171aaf9 100644 --- a/impeller/renderer/backend/metal/surface_mtl.mm +++ b/impeller/renderer/backend/metal/surface_mtl.mm @@ -294,6 +294,14 @@ - (void)flutterPrepareForPresent:(nonnull id)commandBuffer; [command_buffer waitUntilScheduled]; #endif // defined(FML_OS_IOS_SIMULATOR) && defined(FML_ARCH_CPU_X86_64) [drawable_ present]; + } else if (submit_reciever_) { + auto drawable = drawable_; + [command_buffer commit]; + [command_buffer waitUntilScheduled]; + submit_reciever_([drawable]() -> bool { + [drawable present]; + return true; + }); } else { // The drawable may come from a FlutterMetalLayer, so it can't be // presented through the command buffer. diff --git a/shell/gpu/gpu_surface_metal_impeller.mm b/shell/gpu/gpu_surface_metal_impeller.mm index bc9bfd78d2ffb..6e89734d5ba1b 100644 --- a/shell/gpu/gpu_surface_metal_impeller.mm +++ b/shell/gpu/gpu_surface_metal_impeller.mm @@ -155,6 +155,7 @@ if (!surface) { return false; } + surface->SetSubmitInfo(surface_frame.submit_info().submit_receiver); if (clip_rect && clip_rect->IsEmpty()) { return surface->Present(); @@ -277,6 +278,8 @@ auto surface = impeller::SurfaceMTL::MakeFromTexture(renderer->GetContext(), mtl_texture, clip_rect); + surface->SetSubmitInfo(surface_frame.submit_info().submit_receiver); + if (clip_rect && clip_rect->IsEmpty()) { return surface->Present(); } diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index a1a931939d4f1..68bdf17be6fba 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "flow/surface_frame.h" #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h" #include "fml/logging.h" @@ -81,34 +82,32 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, // Becomes NO if Apple's API changes and blurred backdrop filters cannot be applied. BOOL canApplyBlurBackdrop = YES; -std::shared_ptr FlutterPlatformViewLayerPool::GetLayer( - GrDirectContext* gr_context, - const std::shared_ptr& ios_context, - MTLPixelFormat pixel_format, - bool create_if_missing) { +std::shared_ptr FlutterPlatformViewLayerPool::GetNextLayer() { if (available_layer_index_ < layers_.size()) { - // TODO: Skia + // // TODO: Skia std::shared_ptr layer = layers_[available_layer_index_]; - // This condition can only happen with the Skia backend, which is due to be removed from - // iOS in short order. - if (gr_context != layer->gr_context) { - layer->gr_context = gr_context; - // The overlay already exists, but the GrContext was changed so we need to recreate - // the rendering surface with the new GrContext. - IOSSurface* ios_surface = layer->ios_surface.get(); - std::unique_ptr surface = ios_surface->CreateGPUSurface(gr_context); - layer->surface = std::move(surface); - } + // // This condition can only happen with the Skia backend, which is due to be removed from + // // iOS in short order. + // if (gr_context != layer->gr_context) { + // layer->gr_context = gr_context; + // // The overlay already exists, but the GrContext was changed so we need to recreate + // // the rendering surface with the new GrContext. + // IOSSurface* ios_surface = layer->ios_surface.get(); + // std::unique_ptr surface = ios_surface->CreateGPUSurface(gr_context); + // layer->surface = std::move(surface); + // } available_layer_index_++; return layer; } - if (!create_if_missing) { - return nullptr; - } + return nullptr; +} +void FlutterPlatformViewLayerPool::CreateLayer(GrDirectContext* gr_context, + const std::shared_ptr& ios_context, + MTLPixelFormat pixel_format) { std::shared_ptr layer; fml::scoped_nsobject overlay_view; fml::scoped_nsobject overlay_view_wrapper; @@ -158,8 +157,6 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, layer->overlay_view_wrapper.get().clipsToBounds = YES; [layer->overlay_view_wrapper.get() addSubview:layer->overlay_view]; layers_.push_back(layer); - - return layer; } void FlutterPlatformViewLayerPool::RecycleLayers() { @@ -169,9 +166,12 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, std::vector> FlutterPlatformViewLayerPool::GetUnusedLayers() { std::vector> results; - for (size_t i = available_layer_index_; i < layers_.size(); i++) { - results.push_back(layers_[i]); - } + // for (size_t i = available_layer_index_; i < layers_.size(); i++) { + // results.push_back(layers_[i]); + // } + // if (!results.empty()) { + // FML_LOG(ERROR) << "Removing Layers"; + // } return results; } @@ -339,7 +339,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, // Make this method check if there are pending view operations instead. // Also rename it to `HasPendingViewOperations`. bool FlutterPlatformViewsController::HasPlatformViewThisOrNextFrame() { - return !composition_order_.empty() || !active_composition_order_.empty(); + return true; } const int FlutterPlatformViewsController::kDefaultMergedLeaseDuration; @@ -424,7 +424,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, } void FlutterPlatformViewsController::ClipViewSetMaskView(UIView* clipView) { - FML_CHECK([[NSThread currentThread] isMainThread]); + FML_DCHECK([[NSThread currentThread] isMainThread]); if (clipView.maskView) { return; } @@ -440,7 +440,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, void FlutterPlatformViewsController::ApplyMutators(const MutatorsStack& mutators_stack, UIView* embedded_view, const SkRect& bounding_rect) { - FML_CHECK([[NSThread currentThread] isMainThread]); + FML_DCHECK([[NSThread currentThread] isMainThread]); if (flutter_view_ == nullptr) { return; } @@ -568,7 +568,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, // included in the `views_to_recomposite_`. void FlutterPlatformViewsController::CompositeWithParams(int64_t view_id, const EmbeddedViewParams& params) { - FML_CHECK([[NSThread currentThread] isMainThread]); + FML_DCHECK([[NSThread currentThread] isMainThread]); CGRect frame = CGRectMake(0, 0, params.sizePoints().width(), params.sizePoints().height()); FlutterTouchInterceptingView* touchInterceptor = touch_interceptors_[view_id].get(); #if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG @@ -611,16 +611,15 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, } void FlutterPlatformViewsController::Reset() { - FML_CHECK([[NSThread currentThread] isMainThread]); - for (int64_t view_id : active_composition_order_) { - UIView* sub_view = root_views_[view_id].get(); - [sub_view removeFromSuperview]; - } + FML_DCHECK([[NSThread currentThread] isMainThread]); + // for (int64_t view_id : active_composition_order_) { + // UIView* sub_view = root_views_[view_id].get(); + // [sub_view removeFromSuperview]; + // } root_views_.clear(); touch_interceptors_.clear(); views_.clear(); composition_order_.clear(); - active_composition_order_.clear(); slices_.clear(); current_composition_params_.clear(); clip_count_.clear(); @@ -630,7 +629,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, } SkRect FlutterPlatformViewsController::GetPlatformViewRect(int64_t view_id) { - FML_CHECK([[NSThread currentThread] isMainThread]); + FML_DCHECK([[NSThread currentThread] isMainThread]); UIView* platform_view = GetPlatformViewByID(view_id); UIScreen* screen = [UIScreen mainScreen]; CGRect platform_view_cgrect = [platform_view convertRect:platform_view.bounds @@ -670,6 +669,9 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, size_t missing_layer_count = 0; + // Pending Submit Callbacks + std::vector callbacks; + for (size_t i = 0; i < num_platform_views; i++) { int64_t platform_view_id = composition_order_[i]; EmbedderViewSlice* slice = slices_[platform_view_id].get(); @@ -729,11 +731,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, // on the overlay layer. background_canvas->ClipRect(SkRect::Make(joined_rect), DlCanvas::ClipOp::kDifference); // Get a new host layer. - std::shared_ptr layer = - GetExistingLayer(gr_context, // - ios_context, // - MTLPixelFormatBGRA10_XR // - ); + std::shared_ptr layer = GetExistingLayer(); if (!layer) { missing_layer_count++; continue; @@ -759,7 +757,10 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, // This flutter view is never the last in a frame, since we always submit the // underlay view last. - frame->set_submit_info({.frame_boundary = false}); + frame->set_submit_info({.frame_boundary = false, + .submit_receiver = [&callbacks](SurfaceFrame::DeferredSubmit cb) { + callbacks.push_back(cb); + }}); layer->did_submit_last_frame = frame->Submit(); } @@ -775,14 +776,29 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, // Manually trigger the SkAutoCanvasRestore before we submit the frame save.Restore(); - // TODO + frame->set_submit_info({.submit_receiver = [&callbacks](SurfaceFrame::DeferredSubmit cb) { + callbacks.push_back(cb); + }}); did_submit &= frame->Submit(); - platform_task_runner->PostTask( - fml::MakeCopyable([&, platform_view_layers = std::move(platform_view_layers), - missing_layer_count, frame = std::move(frame), - current_composition_params = current_composition_params_, - views_to_recomposite = views_to_recomposite_]() mutable { + // Mark all layers as available, so they can be used in the next frame. + layer_pool_->RecycleLayers(); + + auto task = fml::MakeCopyable( + [&, platform_view_layers = std::move(platform_view_layers), missing_layer_count, + frame = std::move(frame), current_composition_params = current_composition_params_, + views_to_recomposite = views_to_recomposite_, callbacks = callbacks, + composition_order = composition_order_]() mutable { + TRACE_EVENT0("flutter", "FlutterPlatformViewsController::SubmitFrame::CATransaction"); + + [CATransaction begin]; + // Configure Flutter overlay views. + for (const auto& [key, layers] : platform_view_layers) { + for (const auto& layer : layers) { + layer->UpdateViewState(flutter_view_); + } + } + // Dispose unused Flutter Views. DisposeViews(); @@ -791,53 +807,54 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, CompositeWithParams(view_id, current_composition_params[view_id]); } - // Configure Flutter overlay views. - for (const auto& [key, layers] : platform_view_layers) { - for (const auto& layer : layers) { - layer->UpdateViewState(flutter_view_); - } + for (const auto& cb : callbacks) { + cb(); } // Create Missing Layers for (auto i = 0u; i < missing_layer_count; i++) { - auto layer = GetOrCreateLayer(gr_context, // - ios_context, // - MTLPixelFormatBGRA10_XR // + CreateLayer(gr_context, // + ios_context, // + MTLPixelFormatBGRA10_XR // ); - layer->did_submit_last_frame = true; } + // Organize the layers by their z indexes. + auto active_composition_order = + BringLayersIntoView(platform_view_layers, composition_order); + // If a layer was allocated in the previous frame, but it's not used in the current frame, // then it can be removed from the scene. - RemoveUnusedLayers(); - // Organize the layers by their z indexes. - BringLayersIntoView(platform_view_layers); - // Mark all layers as available, so they can be used in the next frame. - layer_pool_->RecycleLayers(); + RemoveUnusedLayers(composition_order, active_composition_order); // If the frame is submitted with embedded platform views, // there should be a |[CATransaction begin]| call in this frame prior to all the drawing. // If that case, we need to commit the transaction. CommitCATransactionIfNeeded(); - })); + [CATransaction commit]; + }); + platform_task_runner->PostTask(task); return true; } -void FlutterPlatformViewsController::BringLayersIntoView(LayersMap layer_map) { +std::vector FlutterPlatformViewsController::BringLayersIntoView( + LayersMap layer_map, + const std::vector& composition_order) { FML_DCHECK(flutter_view_); UIView* flutter_view = flutter_view_.get(); - // Clear the `active_composition_order_`, which will be populated down below. - active_composition_order_.clear(); + + std::vector active_composition_order; + NSMutableArray* desired_platform_subviews = [NSMutableArray array]; - for (size_t i = 0; i < composition_order_.size(); i++) { - int64_t platform_view_id = composition_order_[i]; + for (size_t i = 0; i < composition_order.size(); i++) { + int64_t platform_view_id = composition_order[i]; std::vector> layers = layer_map[platform_view_id]; UIView* platform_view_root = root_views_[platform_view_id].get(); [desired_platform_subviews addObject:platform_view_root]; for (const std::shared_ptr& layer : layers) { [desired_platform_subviews addObject:layer->overlay_view_wrapper]; } - active_composition_order_.push_back(platform_view_id); + active_composition_order.push_back(platform_view_id); } NSSet* desired_platform_subviews_set = [NSSet setWithArray:desired_platform_subviews]; @@ -856,29 +873,24 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, [flutter_view addSubview:subview]; } } + return active_composition_order; } bool FlutterPlatformViewsController::HasPlatformViewLayerAlready( GrDirectContext* gr_context, const std::shared_ptr& ios_context, MTLPixelFormat pixel_format) { - std::shared_ptr layer = - layer_pool_->GetLayer(gr_context, ios_context, pixel_format, /*create_if_missing=*/false); - return !!layer; + return true; } -std::shared_ptr FlutterPlatformViewsController::GetExistingLayer( - GrDirectContext* gr_context, - const std::shared_ptr& ios_context, - MTLPixelFormat pixel_format) { - return layer_pool_->GetLayer(gr_context, ios_context, pixel_format, /*create_if_missing=*/false); +std::shared_ptr FlutterPlatformViewsController::GetExistingLayer() { + return layer_pool_->GetNextLayer(); } -std::shared_ptr FlutterPlatformViewsController::GetOrCreateLayer( - GrDirectContext* gr_context, - const std::shared_ptr& ios_context, - MTLPixelFormat pixel_format) { - return layer_pool_->GetLayer(gr_context, ios_context, pixel_format, /*create_if_missing=*/true); +void FlutterPlatformViewsController::CreateLayer(GrDirectContext* gr_context, + const std::shared_ptr& ios_context, + MTLPixelFormat pixel_format) { + layer_pool_->CreateLayer(gr_context, ios_context, pixel_format); } void FlutterPlatformViewLayer::UpdateViewState(UIView* flutter_view) { @@ -901,18 +913,21 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, [NSString stringWithFormat:@"platform_view[%lld].overlay_view[%lld]", view_id, overlay_id]; } -void FlutterPlatformViewsController::RemoveUnusedLayers() { +void FlutterPlatformViewsController::RemoveUnusedLayers( + const std::vector& composition_order, + const std::vector& active_composition_order) { + // TODO std::vector> layers = layer_pool_->GetUnusedLayers(); for (const std::shared_ptr& layer : layers) { [layer->overlay_view_wrapper removeFromSuperview]; } std::unordered_set composition_order_set; - for (int64_t view_id : composition_order_) { + for (int64_t view_id : composition_order) { composition_order_set.insert(view_id); } // Remove unused platform views. - for (int64_t view_id : active_composition_order_) { + for (int64_t view_id : active_composition_order) { if (composition_order_set.find(view_id) == composition_order_set.end()) { UIView* platform_view_root = root_views_[view_id].get(); [platform_view_root removeFromSuperview]; @@ -925,7 +940,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, return; } - FML_CHECK([[NSThread currentThread] isMainThread]); + FML_DCHECK([[NSThread currentThread] isMainThread]); std::unordered_set views_to_composite(composition_order_.begin(), composition_order_.end()); diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index 2ac37e25756c6..7d827c27b8423 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -179,13 +179,11 @@ class FlutterPlatformViewLayerPool { /// Gets a layer from the pool if available, or allocates a new one. /// Finally, it marks the layer as used. That is, it increments `available_layer_index_`. - /// - /// If `create_if_missing` is false, nullptr will be returned if there is no existing - /// pooled layer. - std::shared_ptr GetLayer(GrDirectContext* gr_context, - const std::shared_ptr& ios_context, - MTLPixelFormat pixel_format, - bool create_if_missing); + std::shared_ptr GetNextLayer(); + + void CreateLayer(GrDirectContext* gr_context, + const std::shared_ptr& ios_context, + MTLPixelFormat pixel_format); // Gets the layers in the pool that aren't currently used. // This method doesn't mark the layers as unused. @@ -341,22 +339,21 @@ class FlutterPlatformViewsController { const std::shared_ptr& ios_context, MTLPixelFormat pixel_format); - std::shared_ptr GetExistingLayer( - GrDirectContext* gr_context, - const std::shared_ptr& ios_context, - MTLPixelFormat pixel_format); + std::shared_ptr GetExistingLayer(); - std::shared_ptr GetOrCreateLayer( - GrDirectContext* gr_context, - const std::shared_ptr& ios_context, - MTLPixelFormat pixel_format); + void CreateLayer(GrDirectContext* gr_context, + const std::shared_ptr& ios_context, + MTLPixelFormat pixel_format); // Removes overlay views and platform views that aren't needed in the current frame. // Must run on the platform thread. - void RemoveUnusedLayers(); + void RemoveUnusedLayers(const std::vector& composition_order, + const std::vector& active_composition_order); + // Appends the overlay views and platform view and sets their z index based on the composition // order. - void BringLayersIntoView(LayersMap layer_map); + std::vector BringLayersIntoView(LayersMap layer_map, + const std::vector& composition_order); // Begin a CATransaction. // This transaction needs to be balanced with |CommitCATransactionIfNeeded|. @@ -415,9 +412,6 @@ class FlutterPlatformViewsController { // A vector of visited platform view IDs. std::vector visited_platform_views_; - // The latest composition order that was presented in Present(). - std::vector active_composition_order_; - // Only compoiste platform views in this set. std::unordered_set views_to_recomposite_; diff --git a/shell/platform/darwin/ios/ios_surface_metal_impeller.mm b/shell/platform/darwin/ios/ios_surface_metal_impeller.mm index edf0e6c886192..850c3429e2364 100644 --- a/shell/platform/darwin/ios/ios_surface_metal_impeller.mm +++ b/shell/platform/darwin/ios/ios_surface_metal_impeller.mm @@ -64,7 +64,7 @@ // presented. If there is a non-Flutter UIView active, such as in add2app or a // presentViewController page transition, then this will cause CoreAnimation assertion errors and // exit the app. - layer.presentsWithTransaction = [[NSThread currentThread] isMainThread]; + layer.presentsWithTransaction = YES;// [[NSThread currentThread] isMainThread]; return (__bridge GPUCAMetalLayerHandle)layer; } From 4525bd89296788a455ff16897c3755096c9b1dba Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Fri, 12 Jul 2024 09:52:48 -0700 Subject: [PATCH 05/49] disable partial repaint. --- .clangd | 2 +- flow/compositor_context.cc | 6 +- shell/gpu/gpu_surface_metal_impeller.mm | 2 +- .../external_view_embedder.cc | 4 +- .../external_view_embedder.h | 7 - .../framework/Source/FlutterPlatformViews.mm | 199 ++++++++---------- .../Source/FlutterPlatformViews_Internal.h | 51 ++--- 7 files changed, 110 insertions(+), 161 deletions(-) diff --git a/.clangd b/.clangd index c36b4792d750c..5e9a80ab7c29f 100644 --- a/.clangd +++ b/.clangd @@ -7,4 +7,4 @@ # - https://github.com/clangd/clangd/issues/662 CompileFlags: Add: -Wno-unknown-warning-option - Remove: [-m*, -f*] + Remove: [-m*] diff --git a/flow/compositor_context.cc b/flow/compositor_context.cc index 0a94e0f530a20..bf20fd2cf7479 100644 --- a/flow/compositor_context.cc +++ b/flow/compositor_context.cc @@ -187,9 +187,9 @@ void CompositorContext::ScopedFrame::PaintLayerTreeImpeller( flutter::LayerTree& layer_tree, std::optional clip_rect, bool ignore_raster_cache) { - if (canvas() && clip_rect) { - canvas()->Translate(-clip_rect->x(), -clip_rect->y()); - } + // if (canvas() && clip_rect) { + // canvas()->Translate(-clip_rect->x(), -clip_rect->y()); + // } layer_tree.Paint(*this, ignore_raster_cache); } diff --git a/shell/gpu/gpu_surface_metal_impeller.mm b/shell/gpu/gpu_surface_metal_impeller.mm index 6e89734d5ba1b..a3090cfa4edc4 100644 --- a/shell/gpu/gpu_surface_metal_impeller.mm +++ b/shell/gpu/gpu_surface_metal_impeller.mm @@ -43,7 +43,7 @@ NSNumber* disablePartialRepaint = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"FLTDisablePartialRepaint"]; if (disablePartialRepaint != nil) { - disable_partial_repaint_ = disablePartialRepaint.boolValue; + disable_partial_repaint_ = true; } } diff --git a/shell/platform/android/external_view_embedder/external_view_embedder.cc b/shell/platform/android/external_view_embedder/external_view_embedder.cc index f6ce9eb161273..4af54c3705a46 100644 --- a/shell/platform/android/external_view_embedder/external_view_embedder.cc +++ b/shell/platform/android/external_view_embedder/external_view_embedder.cc @@ -136,8 +136,8 @@ void AndroidExternalViewEmbedder::SubmitFlutterView( overlay_layers.insert({view_id, full_joined_rect}); // Clip the background canvas, so it doesn't contain any of the pixels // drawn on the overlay layer. - background_canvas->ClipRect(full_joined_rect, - DlCanvas::ClipOp::kDifference); + // background_canvas->ClipRect(full_joined_rect, + // DlCanvas::ClipOp::kDifference); } slice->render_into(background_canvas); } diff --git a/shell/platform/android/external_view_embedder/external_view_embedder.h b/shell/platform/android/external_view_embedder/external_view_embedder.h index b8c585450fca2..868779b752f1d 100644 --- a/shell/platform/android/external_view_embedder/external_view_embedder.h +++ b/shell/platform/android/external_view_embedder/external_view_embedder.h @@ -85,13 +85,6 @@ class AndroidExternalViewEmbedder final : public ExternalViewEmbedder { SkRect GetViewRect(int64_t view_id) const; private: - // The number of frames the rasterizer task runner will continue - // to run on the platform thread after no platform view is rendered. - // - // Note: this is an arbitrary number that attempts to account for cases - // where the platform view might be momentarily off the screen. - static const int kDefaultMergedLeaseDuration = 10; - // Provides metadata to the Android surfaces. const AndroidContext& android_context_; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 68bdf17be6fba..807d1159c8d03 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include #include "flow/surface_frame.h" #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h" #include "fml/logging.h" @@ -83,26 +84,17 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, BOOL canApplyBlurBackdrop = YES; std::shared_ptr FlutterPlatformViewLayerPool::GetNextLayer() { - if (available_layer_index_ < layers_.size()) { - // // TODO: Skia - std::shared_ptr layer = layers_[available_layer_index_]; - - // // This condition can only happen with the Skia backend, which is due to be removed from - // // iOS in short order. - // if (gr_context != layer->gr_context) { - // layer->gr_context = gr_context; - // // The overlay already exists, but the GrContext was changed so we need to recreate - // // the rendering surface with the new GrContext. - // IOSSurface* ios_surface = layer->ios_surface.get(); - // std::unique_ptr surface = ios_surface->CreateGPUSurface(gr_context); - // layer->surface = std::move(surface); - // } + layers_mutex_.lock(); + std::shared_ptr result; + if (available_layer_index_ < layers_.size()) { + result = layers_[available_layer_index_]; available_layer_index_++; - return layer; } - return nullptr; + layers_mutex_.unlock(); + + return result; } void FlutterPlatformViewLayerPool::CreateLayer(GrDirectContext* gr_context, @@ -156,7 +148,10 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, // +------------------------+ layer->overlay_view_wrapper.get().clipsToBounds = YES; [layer->overlay_view_wrapper.get() addSubview:layer->overlay_view]; + + layers_mutex_.lock(); layers_.push_back(layer); + layers_mutex_.unlock(); } void FlutterPlatformViewLayerPool::RecycleLayers() { @@ -165,13 +160,12 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, std::vector> FlutterPlatformViewLayerPool::GetUnusedLayers() { + layers_mutex_.lock(); std::vector> results; - // for (size_t i = available_layer_index_; i < layers_.size(); i++) { - // results.push_back(layers_[i]); - // } - // if (!results.empty()) { - // FML_LOG(ERROR) << "Removing Layers"; - // } + for (size_t i = available_layer_index_; i < layers_.size(); i++) { + results.push_back(layers_[i]); + } + layers_mutex_.unlock(); return results; } @@ -335,15 +329,6 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, ResetFrameState(); } -// TODO(cyanglaz): https://github.com/flutter/flutter/issues/56474 -// Make this method check if there are pending view operations instead. -// Also rename it to `HasPendingViewOperations`. -bool FlutterPlatformViewsController::HasPlatformViewThisOrNextFrame() { - return true; -} - -const int FlutterPlatformViewsController::kDefaultMergedLeaseDuration; - PostPrerollResult FlutterPlatformViewsController::PostPrerollAction( const fml::RefPtr& raster_thread_merger) { return PostPrerollResult::kSuccess; @@ -366,9 +351,6 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, void FlutterPlatformViewsController::PrerollCompositeEmbeddedView( int64_t view_id, std::unique_ptr params) { - // All the CATransactions should be committed by the end of the last frame, - // so catransaction_added_ must be false. - FML_DCHECK(!catransaction_added_); SkRect view_bounds = SkRect::Make(frame_size_); std::unique_ptr view; @@ -444,6 +426,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, if (flutter_view_ == nullptr) { return; } + FML_DCHECK(CATransform3DEqualToTransform(embedded_view.layer.transform, CATransform3DIdentity)); ResetAnchor(embedded_view.layer); ChildClippingView* clipView = (ChildClippingView*)embedded_view.superview; @@ -628,33 +611,21 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, visited_platform_views_.clear(); } -SkRect FlutterPlatformViewsController::GetPlatformViewRect(int64_t view_id) { - FML_DCHECK([[NSThread currentThread] isMainThread]); - UIView* platform_view = GetPlatformViewByID(view_id); - UIScreen* screen = [UIScreen mainScreen]; - CGRect platform_view_cgrect = [platform_view convertRect:platform_view.bounds - toView:flutter_view_]; - return SkRect::MakeXYWH(platform_view_cgrect.origin.x * screen.scale, // - platform_view_cgrect.origin.y * screen.scale, // - platform_view_cgrect.size.width * screen.scale, // - platform_view_cgrect.size.height * screen.scale // - ); -} - bool FlutterPlatformViewsController::SubmitFrame( GrDirectContext* gr_context, const std::shared_ptr& ios_context, - std::unique_ptr frame, + std::unique_ptr background_frame, fml::RefPtr platform_task_runner) { TRACE_EVENT0("flutter", "FlutterPlatformViewsController::SubmitFrame"); if (flutter_view_ == nullptr) { - return frame->Submit(); + return background_frame->Submit(); } - DlCanvas* background_canvas = frame->Canvas(); + DlCanvas* background_canvas = background_frame->Canvas(); // Resolve all pending GPU operations before allocating a new surface. + // This does nothing on Impeller. background_canvas->Flush(); // Clipping the background canvas before drawing the picture recorders requires @@ -683,6 +654,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, int64_t current_platform_view_id = composition_order_[j - 1]; SkRect platform_view_rect = current_composition_params_[current_platform_view_id].finalBoundingRect(); + std::vector intersection_rects = slice->region(platform_view_rect).getRects(); const SkIRect rounded_in_platform_view_rect = platform_view_rect.roundIn(); // Ignore intersections of single width/height on the edge of the platform view. @@ -737,10 +709,6 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, continue; } - layer->view_id = current_platform_view_id; - layer->overlay_id = overlay_id; - layer->rect = joined_rect; - { std::unique_ptr frame = layer->surface->AcquireFrame(frame_size_); // If frame is null, AcquireFrame already printed out an error message. @@ -766,7 +734,11 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, } did_submit &= layer->did_submit_last_frame; - platform_view_layers[current_platform_view_id].push_back(layer); + platform_view_layers[current_platform_view_id].push_back( + {.rect = joined_rect, + .view_id = current_platform_view_id, + .overlay_id = static_cast(overlay_id), + .layer = layer}); overlay_id++; } } @@ -776,65 +748,71 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, // Manually trigger the SkAutoCanvasRestore before we submit the frame save.Restore(); - frame->set_submit_info({.submit_receiver = [&callbacks](SurfaceFrame::DeferredSubmit cb) { + background_frame->set_submit_info({.submit_receiver = [&callbacks](SurfaceFrame::DeferredSubmit cb) { callbacks.push_back(cb); }}); - did_submit &= frame->Submit(); + did_submit &= background_frame->Submit(); // Mark all layers as available, so they can be used in the next frame. + std::vector> unused_layers = + layer_pool_->GetUnusedLayers(); + FML_DCHECK(unused_layers.empty() || missing_layer_count == 0); layer_pool_->RecycleLayers(); - auto task = fml::MakeCopyable( - [&, platform_view_layers = std::move(platform_view_layers), missing_layer_count, - frame = std::move(frame), current_composition_params = current_composition_params_, - views_to_recomposite = views_to_recomposite_, callbacks = callbacks, - composition_order = composition_order_]() mutable { - TRACE_EVENT0("flutter", "FlutterPlatformViewsController::SubmitFrame::CATransaction"); - - [CATransaction begin]; - // Configure Flutter overlay views. - for (const auto& [key, layers] : platform_view_layers) { - for (const auto& layer : layers) { - layer->UpdateViewState(flutter_view_); - } - } + auto task = fml::MakeCopyable([&, platform_view_layers = std::move(platform_view_layers), + missing_layer_count, // + current_composition_params = current_composition_params_, // + views_to_recomposite = views_to_recomposite_, // + callbacks = callbacks, // + composition_order = composition_order_, // + unused_layers = unused_layers]() mutable { + TRACE_EVENT0("flutter", "FlutterPlatformViewsController::SubmitFrame::CATransaction"); + + [CATransaction begin]; + + // Configure Flutter overlay views. + for (const auto& [key, layers] : platform_view_layers) { + for (const auto& layer_data : layers) { + layer_data.layer->UpdateViewState(flutter_view_, layer_data.rect, layer_data.view_id, + layer_data.overlay_id); + } + } - // Dispose unused Flutter Views. - DisposeViews(); + // Dispose unused Flutter Views. + DisposeViews(); - // Composite Platform Views. - for (auto view_id : views_to_recomposite) { - CompositeWithParams(view_id, current_composition_params[view_id]); - } + // Composite Platform Views. + for (auto view_id : views_to_recomposite) { + CompositeWithParams(view_id, current_composition_params[view_id]); + } - for (const auto& cb : callbacks) { - cb(); - } + for (const auto& cb : callbacks) { + cb(); + } - // Create Missing Layers - for (auto i = 0u; i < missing_layer_count; i++) { - CreateLayer(gr_context, // - ios_context, // - MTLPixelFormatBGRA10_XR // - ); - } + // Create Missing Layers + for (auto i = 0u; i < missing_layer_count; i++) { + CreateLayer(gr_context, // + ios_context, // + MTLPixelFormatBGRA10_XR // + ); + } + + // Organize the layers by their z indexes. + auto active_composition_order = BringLayersIntoView(platform_view_layers, composition_order); - // Organize the layers by their z indexes. - auto active_composition_order = - BringLayersIntoView(platform_view_layers, composition_order); + // If a layer was allocated in the previous frame, but it's not used in the current frame, + // then it can be removed from the scene. + RemoveUnusedLayers(unused_layers, composition_order, active_composition_order); - // If a layer was allocated in the previous frame, but it's not used in the current frame, - // then it can be removed from the scene. - RemoveUnusedLayers(composition_order, active_composition_order); + // If the frame is submitted with embedded platform views, + // there should be a |[CATransaction begin]| call in this frame prior to all the drawing. + // If that case, we need to commit the transaction. + [CATransaction commit]; + }); - // If the frame is submitted with embedded platform views, - // there should be a |[CATransaction begin]| call in this frame prior to all the drawing. - // If that case, we need to commit the transaction. - CommitCATransactionIfNeeded(); - [CATransaction commit]; - }); platform_task_runner->PostTask(task); - return true; + return did_submit; } std::vector FlutterPlatformViewsController::BringLayersIntoView( @@ -848,11 +826,11 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, 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> layers = layer_map[platform_view_id]; + std::vector layers = layer_map[platform_view_id]; UIView* platform_view_root = root_views_[platform_view_id].get(); [desired_platform_subviews addObject:platform_view_root]; - for (const std::shared_ptr& layer : layers) { - [desired_platform_subviews addObject:layer->overlay_view_wrapper]; + for (const auto& layer_data : layers) { + [desired_platform_subviews addObject:layer_data.layer->overlay_view_wrapper]; } active_composition_order.push_back(platform_view_id); } @@ -893,7 +871,10 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, layer_pool_->CreateLayer(gr_context, ios_context, pixel_format); } -void FlutterPlatformViewLayer::UpdateViewState(UIView* flutter_view) { +void FlutterPlatformViewLayer::UpdateViewState(UIView* flutter_view, + SkIRect rect, + int64_t view_id, + int64_t overlay_id) { UIView* overlay_view_wrapper = this->overlay_view_wrapper.get(); auto screenScale = [UIScreen mainScreen].scale; // Set the size of the overlay view wrapper. @@ -914,11 +895,10 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, } void FlutterPlatformViewsController::RemoveUnusedLayers( + const std::vector>& unused_layers, const std::vector& composition_order, const std::vector& active_composition_order) { - // TODO - std::vector> layers = layer_pool_->GetUnusedLayers(); - for (const std::shared_ptr& layer : layers) { + for (const std::shared_ptr& layer : unused_layers) { [layer->overlay_view_wrapper removeFromSuperview]; } @@ -963,12 +943,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, views_to_dispose_ = std::move(views_to_delay_dispose); } -void FlutterPlatformViewsController::BeginCATransaction() {} - -void FlutterPlatformViewsController::CommitCATransactionIfNeeded() {} - void FlutterPlatformViewsController::ResetFrameState() { - // TODO: move this state when posting task to platform loop slices_.clear(); composition_order_.clear(); visited_platform_views_.clear(); diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index 7d827c27b8423..9f9eec98726ae 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -6,9 +6,11 @@ #define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERPLATFORMVIEWS_INTERNAL_H_ #import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlatformViews.h" +#include "impeller/base/thread_safety.h" #include "third_party/skia/include/core/SkRect.h" #include +#include #include "flutter/flow/surface.h" #include "flutter/fml/memory/weak_ptr.h" @@ -163,11 +165,7 @@ struct FlutterPlatformViewLayer { // so we can update the overlay with the new context. GrDirectContext* gr_context; - SkIRect rect; - int64_t view_id; - int64_t overlay_id; - - void UpdateViewState(UIView* flutter_view); + void UpdateViewState(UIView* flutter_view, SkIRect rect, int64_t view_id, int64_t overlay_id); }; // This class isn't thread safe. @@ -206,6 +204,7 @@ class FlutterPlatformViewLayerPool { /// This indicates that entries starting from 1 can be reused meanwhile the entry at position 0 /// cannot be reused. size_t available_layer_index_ = 0; + mutable std::mutex layers_mutex_; std::vector> layers_; FML_DISALLOW_COPY_AND_ASSIGN(FlutterPlatformViewLayerPool); @@ -267,10 +266,6 @@ class FlutterPlatformViewsController { DlCanvas* CompositeEmbeddedView(int64_t view_id); - // The rect of the platform view at index view_id. This rect has been translated into the - // host view coordinate system. Units are device screen pixels. - SkRect GetPlatformViewRect(int64_t view_id); - // Discards all platform views instances and auxiliary resources. void Reset(); @@ -296,7 +291,14 @@ class FlutterPlatformViewsController { private: static const size_t kMaxLayerAllocations = 2; - using LayersMap = std::map>>; + struct LayerData { + SkIRect rect; + int64_t view_id; + int64_t overlay_id; + std::shared_ptr layer; + }; + + using LayersMap = std::map>; void OnCreate(FlutterMethodCall* call, FlutterResult result) __attribute__((cf_audited_transfer)); void OnDispose(FlutterMethodCall* call, FlutterResult result) @@ -308,13 +310,6 @@ class FlutterPlatformViewsController { // Dispose the views in `views_to_dispose_`. void DisposeViews(); - // Returns true if there are embedded views in the scene at current frame - // Or there will be embedded views in the next frame. - // TODO(cyanglaz): https://github.com/flutter/flutter/issues/56474 - // Make this method check if there are pending view operations instead. - // Also rename it to `HasPendingViewOperations`. - bool HasPlatformViewThisOrNextFrame(); - // Traverse the `mutators_stack` and return the number of clip operations. int CountClips(const MutatorsStack& mutators_stack); @@ -347,21 +342,16 @@ class FlutterPlatformViewsController { // Removes overlay views and platform views that aren't needed in the current frame. // Must run on the platform thread. - void RemoveUnusedLayers(const std::vector& composition_order, - const std::vector& active_composition_order); + void RemoveUnusedLayers( + const std::vector>& unused_layers, + const std::vector& composition_order, + const std::vector& active_composition_order); // Appends the overlay views and platform view and sets their z index based on the composition // order. std::vector BringLayersIntoView(LayersMap layer_map, const std::vector& composition_order); - // Begin a CATransaction. - // This transaction needs to be balanced with |CommitCATransactionIfNeeded|. - void BeginCATransaction(); - - // Commit a CATransaction if |BeginCATransaction| has been called during the frame. - void CommitCATransactionIfNeeded(); - // Resets the state of the frame. void ResetFrameState(); @@ -394,13 +384,6 @@ class FlutterPlatformViewsController { std::map clip_count_; SkISize frame_size_; - // The number of frames the rasterizer task runner will continue - // to run on the platform thread after no platform view is rendered. - // - // Note: this is an arbitrary number that attempts to account for cases - // where the platform view might be momentarily off the screen. - static const int kDefaultMergedLeaseDuration = 10; - // Method channel `OnDispose` calls adds the views to be disposed to this set to be disposed on // the next frame. std::unordered_set views_to_dispose_; @@ -419,8 +402,6 @@ class FlutterPlatformViewsController { std::map gesture_recognizers_blocking_policies_; - bool catransaction_added_ = false; - // WeakPtrFactory must be the last member. std::unique_ptr> weak_factory_; From 6ce3aa0ccf704e165dabc54335fbf3eb451b0571 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Fri, 12 Jul 2024 10:23:39 -0700 Subject: [PATCH 06/49] cleanups. --- flow/compositor_context.cc | 6 +++--- impeller/renderer/backend/metal/surface_mtl.h | 1 - shell/common/rasterizer.cc | 3 ++- shell/gpu/gpu_surface_metal_impeller.mm | 2 +- .../external_view_embedder/external_view_embedder.cc | 4 ++-- shell/platform/darwin/ios/ios_external_view_embedder.mm | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/flow/compositor_context.cc b/flow/compositor_context.cc index bf20fd2cf7479..0a94e0f530a20 100644 --- a/flow/compositor_context.cc +++ b/flow/compositor_context.cc @@ -187,9 +187,9 @@ void CompositorContext::ScopedFrame::PaintLayerTreeImpeller( flutter::LayerTree& layer_tree, std::optional clip_rect, bool ignore_raster_cache) { - // if (canvas() && clip_rect) { - // canvas()->Translate(-clip_rect->x(), -clip_rect->y()); - // } + if (canvas() && clip_rect) { + canvas()->Translate(-clip_rect->x(), -clip_rect->y()); + } layer_tree.Paint(*this, ignore_raster_cache); } diff --git a/impeller/renderer/backend/metal/surface_mtl.h b/impeller/renderer/backend/metal/surface_mtl.h index 27be92a505152..20c7b5a6d7db3 100644 --- a/impeller/renderer/backend/metal/surface_mtl.h +++ b/impeller/renderer/backend/metal/surface_mtl.h @@ -8,7 +8,6 @@ #include #include -#include "fml/synchronization/count_down_latch.h" #include "impeller/geometry/rect.h" #include "impeller/renderer/context.h" #include "impeller/renderer/surface.h" diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index dbf904f61794c..20ef6cc7505d0 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -787,7 +787,8 @@ DrawSurfaceStatus Rasterizer::DrawToSurfaceUnsafe( frame->set_submit_info(submit_info); - if (external_view_embedder_) { + if (external_view_embedder_ && + (!raster_thread_merger_ || raster_thread_merger_->IsMerged())) { FML_DCHECK(!frame->IsSubmitted()); external_view_embedder_->SubmitFlutterView( view_id, surface_->GetContext(), surface_->GetAiksContext(), diff --git a/shell/gpu/gpu_surface_metal_impeller.mm b/shell/gpu/gpu_surface_metal_impeller.mm index a3090cfa4edc4..c2dd91ae39537 100644 --- a/shell/gpu/gpu_surface_metal_impeller.mm +++ b/shell/gpu/gpu_surface_metal_impeller.mm @@ -43,7 +43,7 @@ NSNumber* disablePartialRepaint = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"FLTDisablePartialRepaint"]; if (disablePartialRepaint != nil) { - disable_partial_repaint_ = true; + disable_partial_repaint_ = disablePartialRepaint.boolValue;; } } diff --git a/shell/platform/android/external_view_embedder/external_view_embedder.cc b/shell/platform/android/external_view_embedder/external_view_embedder.cc index 4af54c3705a46..f6ce9eb161273 100644 --- a/shell/platform/android/external_view_embedder/external_view_embedder.cc +++ b/shell/platform/android/external_view_embedder/external_view_embedder.cc @@ -136,8 +136,8 @@ void AndroidExternalViewEmbedder::SubmitFlutterView( overlay_layers.insert({view_id, full_joined_rect}); // Clip the background canvas, so it doesn't contain any of the pixels // drawn on the overlay layer. - // background_canvas->ClipRect(full_joined_rect, - // DlCanvas::ClipOp::kDifference); + background_canvas->ClipRect(full_joined_rect, + DlCanvas::ClipOp::kDifference); } slice->render_into(background_canvas); } diff --git a/shell/platform/darwin/ios/ios_external_view_embedder.mm b/shell/platform/darwin/ios/ios_external_view_embedder.mm index a71e789b70c1d..30d13b14dae26 100644 --- a/shell/platform/darwin/ios/ios_external_view_embedder.mm +++ b/shell/platform/darwin/ios/ios_external_view_embedder.mm @@ -95,7 +95,7 @@ // |ExternalViewEmbedder| bool IOSExternalViewEmbedder::SupportsDynamicThreadMerging() { - return true; + return false; } // |ExternalViewEmbedder| From be03cf85a4fdcd8050568ce251e9d0ac13f0a790 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Fri, 12 Jul 2024 10:40:03 -0700 Subject: [PATCH 07/49] set presentsWithTransaction. --- flow/surface_frame.h | 2 ++ shell/gpu/gpu_surface_metal_impeller.mm | 15 +++++++++++++-- .../ios/framework/Source/FlutterPlatformViews.mm | 10 ++++++---- .../darwin/ios/ios_surface_metal_impeller.mm | 8 -------- 4 files changed, 21 insertions(+), 14 deletions(-) diff --git a/flow/surface_frame.h b/flow/surface_frame.h index 307abc0865ead..8a50fa00154de 100644 --- a/flow/surface_frame.h +++ b/flow/surface_frame.h @@ -92,6 +92,8 @@ class SurfaceFrame { // Defaults to true, which is generally a safe value. bool frame_boundary = true; + bool present_with_transaction = false; + SubmitReciever submit_receiver; }; diff --git a/shell/gpu/gpu_surface_metal_impeller.mm b/shell/gpu/gpu_surface_metal_impeller.mm index c2dd91ae39537..6e77e81286889 100644 --- a/shell/gpu/gpu_surface_metal_impeller.mm +++ b/shell/gpu/gpu_surface_metal_impeller.mm @@ -43,7 +43,8 @@ NSNumber* disablePartialRepaint = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"FLTDisablePartialRepaint"]; if (disablePartialRepaint != nil) { - disable_partial_repaint_ = disablePartialRepaint.boolValue;; + disable_partial_repaint_ = disablePartialRepaint.boolValue; + ; } } @@ -113,8 +114,18 @@ renderer = impeller_renderer_, // aiks_context = aiks_context_, // drawable, // - last_texture // + last_texture, // + mtl_layer // ](SurfaceFrame& surface_frame, DlCanvas* canvas) mutable -> bool { + // When there are platform views in the scene, the drawable needs to be presented in the + // same transaction as the one created for platform views. When the drawable are being + // presented from the raster thread, we may not be able to use a transaction as it will + // dirty the UIViews being presented. If there is a non-Flutter UIView active, such as in + // add2app or a presentViewController page transition, then this will cause CoreAnimation + // assertion errors and + // exit the app. + mtl_layer.presentsWithTransaction = surface_frame.submit_info().present_with_transaction; + if (!aiks_context) { return false; } diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 807d1159c8d03..78c0da185a3a4 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -351,7 +351,6 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, void FlutterPlatformViewsController::PrerollCompositeEmbeddedView( int64_t view_id, std::unique_ptr params) { - SkRect view_bounds = SkRect::Make(frame_size_); std::unique_ptr view; view = std::make_unique(view_bounds); @@ -726,6 +725,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, // This flutter view is never the last in a frame, since we always submit the // underlay view last. frame->set_submit_info({.frame_boundary = false, + .present_with_transaction = true, .submit_receiver = [&callbacks](SurfaceFrame::DeferredSubmit cb) { callbacks.push_back(cb); }}); @@ -748,9 +748,11 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, // Manually trigger the SkAutoCanvasRestore before we submit the frame save.Restore(); - background_frame->set_submit_info({.submit_receiver = [&callbacks](SurfaceFrame::DeferredSubmit cb) { - callbacks.push_back(cb); - }}); + background_frame->set_submit_info( + {.present_with_transaction = true, + .submit_receiver = [&callbacks](SurfaceFrame::DeferredSubmit cb) { + callbacks.push_back(cb); + }}); did_submit &= background_frame->Submit(); // Mark all layers as available, so they can be used in the next frame. diff --git a/shell/platform/darwin/ios/ios_surface_metal_impeller.mm b/shell/platform/darwin/ios/ios_surface_metal_impeller.mm index 850c3429e2364..8d1a75d6f1893 100644 --- a/shell/platform/darwin/ios/ios_surface_metal_impeller.mm +++ b/shell/platform/darwin/ios/ios_surface_metal_impeller.mm @@ -58,14 +58,6 @@ // backdrop filters. Flutter plugins that create platform views may also read from the layer. layer.framebufferOnly = NO; - // When there are platform views in the scene, the drawable needs to be presented in the same - // transaction as the one created for platform views. When the drawable are being presented from - // the raster thread, we may not be able to use a transaction as it will dirty the UIViews being - // presented. If there is a non-Flutter UIView active, such as in add2app or a - // presentViewController page transition, then this will cause CoreAnimation assertion errors and - // exit the app. - layer.presentsWithTransaction = YES;// [[NSThread currentThread] isMainThread]; - return (__bridge GPUCAMetalLayerHandle)layer; } From 677037668ce0f18bc01cc73668938f5c44adccd0 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Fri, 12 Jul 2024 12:00:52 -0700 Subject: [PATCH 08/49] sanity refactor. --- flow/embedded_views.cc | 3 +- flow/embedded_views.h | 3 +- impeller/renderer/backend/metal/surface_mtl.h | 3 +- shell/common/rasterizer.cc | 2 +- .../shell_test_external_view_embedder.h | 2 + .../external_view_embedder.h | 10 +- .../ios/framework/Source/FlutterEngine.mm | 9 +- .../framework/Source/FlutterPlatformViews.mm | 10 +- .../Source/FlutterPlatformViewsTest.mm | 6664 +++++++++-------- .../Source/FlutterPlatformViews_Internal.h | 14 +- .../Source/FlutterPlatformViews_Internal.mm | 4 +- .../Source/accessibility_bridge_test.mm | 15 +- .../darwin/ios/ios_external_view_embedder.h | 3 +- .../darwin/ios/ios_external_view_embedder.mm | 6 +- .../embedder_external_view_embedder.cc | 3 +- .../embedder_external_view_embedder.h | 3 +- 16 files changed, 3395 insertions(+), 3359 deletions(-) diff --git a/flow/embedded_views.cc b/flow/embedded_views.cc index 80738886af18c..b0d17870a707b 100644 --- a/flow/embedded_views.cc +++ b/flow/embedded_views.cc @@ -48,8 +48,7 @@ void ExternalViewEmbedder::SubmitFlutterView( int64_t flutter_view_id, GrDirectContext* context, const std::shared_ptr& aiks_context, - std::unique_ptr frame, - fml::RefPtr platform_task_runner) { + std::unique_ptr frame) { frame->Submit(); } diff --git a/flow/embedded_views.h b/flow/embedded_views.h index d3de61c278d7a..be05535dd4322 100644 --- a/flow/embedded_views.h +++ b/flow/embedded_views.h @@ -460,8 +460,7 @@ class ExternalViewEmbedder { int64_t flutter_view_id, GrDirectContext* context, const std::shared_ptr& aiks_context, - std::unique_ptr frame, - fml::RefPtr platform_task_runner); + std::unique_ptr frame); // This method provides the embedder a way to do additional tasks after // |SubmitFrame|. For example, merge task runners if `should_resubmit_frame` diff --git a/impeller/renderer/backend/metal/surface_mtl.h b/impeller/renderer/backend/metal/surface_mtl.h index 20c7b5a6d7db3..2941163442087 100644 --- a/impeller/renderer/backend/metal/surface_mtl.h +++ b/impeller/renderer/backend/metal/surface_mtl.h @@ -61,8 +61,7 @@ class SurfaceMTL final : public Surface { using DeferredSubmit = std::function; using SubmitReciever = std::function; - void SetSubmitInfo( - const SubmitReciever& submit_reciever) { + void SetSubmitInfo(const SubmitReciever& submit_reciever) { submit_reciever_ = submit_reciever; } diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index 20ef6cc7505d0..00964a4ac18b7 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -792,7 +792,7 @@ DrawSurfaceStatus Rasterizer::DrawToSurfaceUnsafe( FML_DCHECK(!frame->IsSubmitted()); external_view_embedder_->SubmitFlutterView( view_id, surface_->GetContext(), surface_->GetAiksContext(), - std::move(frame), delegate_.GetTaskRunners().GetPlatformTaskRunner()); + std::move(frame)); } else { frame->Submit(); } diff --git a/shell/common/shell_test_external_view_embedder.h b/shell/common/shell_test_external_view_embedder.h index b000c239435fb..a13044ce158c8 100644 --- a/shell/common/shell_test_external_view_embedder.h +++ b/shell/common/shell_test_external_view_embedder.h @@ -7,6 +7,8 @@ #include "flutter/flow/embedded_views.h" #include "flutter/fml/raster_thread_merger.h" +#include "fml/memory/ref_ptr.h" +#include "fml/task_runner.h" namespace flutter { diff --git a/shell/platform/android/external_view_embedder/external_view_embedder.h b/shell/platform/android/external_view_embedder/external_view_embedder.h index 868779b752f1d..9bac37572cedd 100644 --- a/shell/platform/android/external_view_embedder/external_view_embedder.h +++ b/shell/platform/android/external_view_embedder/external_view_embedder.h @@ -48,8 +48,7 @@ class AndroidExternalViewEmbedder final : public ExternalViewEmbedder { int64_t flutter_view_id, GrDirectContext* context, const std::shared_ptr& aiks_context, - std::unique_ptr frame, - fml::RefPtr platform_task_runner) override; + std::unique_ptr frame) override; // |ExternalViewEmbedder| PostPrerollResult PostPrerollAction( @@ -85,6 +84,13 @@ class AndroidExternalViewEmbedder final : public ExternalViewEmbedder { SkRect GetViewRect(int64_t view_id) const; private: + // The number of frames the rasterizer task runner will continue + // to run on the platform thread after no platform view is rendered. + // + // Note: this is an arbitrary number that attempts to account for cases + // where the platform view might be momentarily off the screen. + static const int kDefaultMergedLeaseDuration = 10; + // Provides metadata to the Android surfaces. const AndroidContext& android_context_; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index 5f2a1fa74ff82..40c6d888dcbca 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -218,7 +218,6 @@ - (instancetype)initWithName:(NSString*)labelPrefix _pluginPublications = [[NSMutableDictionary alloc] init]; _registrars = [[NSMutableDictionary alloc] init]; - [self recreatePlatformViewController]; _binaryMessenger = [[FlutterBinaryMessengerRelay alloc] initWithParent:self]; _textureRegistry = [[FlutterTextureRegistryRelay alloc] initWithParent:self]; @@ -270,9 +269,9 @@ - (void)setUpApplicationLifecycleNotifications:(NSNotificationCenter*)center { object:nil]; } -- (void)recreatePlatformViewController { +- (void)recreatePlatformViewController:(fml::RefPtr)platform_task_runner { _renderingApi = flutter::GetRenderingAPIForProcess(FlutterView.forceSoftwareRendering); - _platformViewsController.reset(new flutter::FlutterPlatformViewsController()); + _platformViewsController.reset(new flutter::FlutterPlatformViewsController(platform_task_runner)); } - (flutter::IOSRenderingAPI)platformViewsRenderingAPI { @@ -868,7 +867,7 @@ - (BOOL)createShell:(NSString*)entrypoint // create call is synchronous. flutter::Shell::CreateCallback on_create_platform_view = [self](flutter::Shell& shell) { - [self recreatePlatformViewController]; + [self recreatePlatformViewController:shell.GetTaskRunners().GetPlatformTaskRunner()]; return std::make_unique( shell, self->_renderingApi, self->_platformViewsController, shell.GetTaskRunners(), shell.GetConcurrentWorkerTaskRunner(), shell.GetIsGpuDisabledSyncSwitch()); @@ -1467,7 +1466,7 @@ - (FlutterEngine*)spawnWithEntrypoint:(/*nullable*/ NSString*)entrypoint // create call is synchronous. flutter::Shell::CreateCallback on_create_platform_view = [result, context](flutter::Shell& shell) { - [result recreatePlatformViewController]; + [result recreatePlatformViewController:shell.GetTaskRunners().GetPlatformTaskRunner()]; return std::make_unique( shell, context, result->_platformViewsController, shell.GetTaskRunners()); }; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 78c0da185a3a4..9f001b3160297 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -610,11 +610,9 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, visited_platform_views_.clear(); } -bool FlutterPlatformViewsController::SubmitFrame( - GrDirectContext* gr_context, - const std::shared_ptr& ios_context, - std::unique_ptr background_frame, - fml::RefPtr platform_task_runner) { +bool FlutterPlatformViewsController::SubmitFrame(GrDirectContext* gr_context, + const std::shared_ptr& ios_context, + std::unique_ptr background_frame) { TRACE_EVENT0("flutter", "FlutterPlatformViewsController::SubmitFrame"); if (flutter_view_ == nullptr) { @@ -813,7 +811,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, [CATransaction commit]; }); - platform_task_runner->PostTask(task); + platform_task_runner_->PostTask(task); return did_submit; } diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm index da17bdc881a40..2a273974cafc8 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm @@ -1,3317 +1,3347 @@ -// // Copyright 2013 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 -// #import -// #import - -// #import "flutter/fml/thread.h" -// #import "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h" -// #import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlatformViews.h" -// #import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h" -// #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h" -// #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterTouchInterceptingView_Test.h" -// #import "flutter/shell/platform/darwin/ios/platform_view_ios.h" - -// FLUTTER_ASSERT_ARC - -// @class FlutterPlatformViewsTestMockPlatformView; -// __weak static FlutterPlatformViewsTestMockPlatformView* gMockPlatformView = nil; -// const float kFloatCompareEpsilon = 0.001; - -// @interface FlutterPlatformViewsTestMockPlatformView : UIView -// @end -// @implementation FlutterPlatformViewsTestMockPlatformView - -// - (instancetype)init { -// self = [super init]; -// if (self) { -// gMockPlatformView = self; -// } -// return self; -// } - -// - (void)dealloc { -// gMockPlatformView = nil; -// } - -// @end - -// @interface FlutterPlatformViewsTestMockFlutterPlatformView : NSObject -// @property(nonatomic, strong) UIView* view; -// @property(nonatomic, assign) BOOL viewCreated; -// @end - -// @implementation FlutterPlatformViewsTestMockFlutterPlatformView - -// - (instancetype)init { -// if (self = [super init]) { -// _view = [[FlutterPlatformViewsTestMockPlatformView alloc] init]; -// _viewCreated = NO; -// } -// return self; -// } - -// - (UIView*)view { -// [self checkViewCreatedOnce]; -// return _view; -// } - -// - (void)checkViewCreatedOnce { -// if (self.viewCreated) { -// abort(); -// } -// self.viewCreated = YES; -// } - -// @end - -// @interface FlutterPlatformViewsTestMockFlutterPlatformFactory -// : NSObject -// @end - -// @implementation FlutterPlatformViewsTestMockFlutterPlatformFactory -// - (NSObject*)createWithFrame:(CGRect)frame -// viewIdentifier:(int64_t)viewId -// arguments:(id _Nullable)args { -// return [[FlutterPlatformViewsTestMockFlutterPlatformView alloc] init]; -// } - -// @end - -// namespace flutter { -// namespace { -// class FlutterPlatformViewsTestMockPlatformViewDelegate : public PlatformView::Delegate { -// public: -// void OnPlatformViewCreated(std::unique_ptr surface) override {} -// void OnPlatformViewDestroyed() override {} -// void OnPlatformViewScheduleFrame() override {} -// void OnPlatformViewAddView(int64_t view_id, -// const ViewportMetrics& viewport_metrics, -// AddViewCallback callback) override {} -// void OnPlatformViewRemoveView(int64_t view_id, RemoveViewCallback callback) override {} -// void OnPlatformViewSetNextFrameCallback(const fml::closure& closure) override {} -// void OnPlatformViewSetViewportMetrics(int64_t view_id, const ViewportMetrics& metrics) override {} -// const flutter::Settings& OnPlatformViewGetSettings() const override { return settings_; } -// void OnPlatformViewDispatchPlatformMessage(std::unique_ptr message) override {} -// void OnPlatformViewDispatchPointerDataPacket(std::unique_ptr packet) override { -// } -// void OnPlatformViewDispatchSemanticsAction(int32_t id, -// SemanticsAction action, -// fml::MallocMapping args) override {} -// void OnPlatformViewSetSemanticsEnabled(bool enabled) override {} -// void OnPlatformViewSetAccessibilityFeatures(int32_t flags) override {} -// void OnPlatformViewRegisterTexture(std::shared_ptr texture) override {} -// void OnPlatformViewUnregisterTexture(int64_t texture_id) override {} -// void OnPlatformViewMarkTextureFrameAvailable(int64_t texture_id) override {} - -// void LoadDartDeferredLibrary(intptr_t loading_unit_id, -// std::unique_ptr snapshot_data, -// std::unique_ptr snapshot_instructions) override { -// } -// void LoadDartDeferredLibraryError(intptr_t loading_unit_id, -// const std::string error_message, -// bool transient) override {} -// void UpdateAssetResolverByType(std::unique_ptr updated_asset_resolver, -// flutter::AssetResolver::AssetResolverType type) override {} - -// flutter::Settings settings_; -// }; - -// } // namespace -// } // namespace flutter - -// namespace { -// fml::RefPtr CreateNewThread(const std::string& name) { -// auto thread = std::make_unique(name); -// auto runner = thread->GetTaskRunner(); -// return runner; -// } -// } // namespace - -// @interface FlutterPlatformViewsTest : XCTestCase -// @end - -// @implementation FlutterPlatformViewsTest - -// - (void)testFlutterViewOnlyCreateOnceInOneFrame { -// 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(); -// auto platform_view = std::make_unique( -// /*delegate=*/mock_delegate, -// /*rendering_api=*/mock_delegate.settings_.enable_impeller -// ? flutter::IOSRenderingAPI::kMetal -// : flutter::IOSRenderingAPI::kSoftware, -// /*platform_views_controller=*/flutterPlatformViewsController, -// /*task_runners=*/runners, -// /*worker_task_runner=*/nil, -// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - -// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = -// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; -// flutterPlatformViewsController->RegisterViewFactory( -// factory, @"MockFlutterPlatformView", -// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); -// FlutterResult result = ^(id result) { -// }; -// flutterPlatformViewsController->OnMethodCall( -// [FlutterMethodCall -// methodCallWithMethodName:@"create" -// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], -// result); -// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; -// flutterPlatformViewsController->SetFlutterView(mockFlutterView); -// // Create embedded view params -// flutter::MutatorsStack stack; -// // Layer tree always pushes a screen scale factor to the stack -// SkMatrix screenScaleMatrix = -// SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); -// stack.PushTransform(screenScaleMatrix); -// // Push a translate matrix -// SkMatrix translateMatrix = SkMatrix::Translate(100, 100); -// stack.PushTransform(translateMatrix); -// SkMatrix finalMatrix; -// finalMatrix.setConcat(screenScaleMatrix, translateMatrix); - -// auto embeddedViewParams = -// std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); - -// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); -// flutterPlatformViewsController->CompositeEmbeddedView(2); - -// flutterPlatformViewsController->GetPlatformViewRect(2); - -// XCTAssertNotNil(gMockPlatformView); - -// flutterPlatformViewsController->Reset(); -// } - -// - (void)testCanCreatePlatformViewWithoutFlutterView { -// 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(); -// auto platform_view = std::make_unique( -// /*delegate=*/mock_delegate, -// /*rendering_api=*/mock_delegate.settings_.enable_impeller -// ? flutter::IOSRenderingAPI::kMetal -// : flutter::IOSRenderingAPI::kSoftware, -// /*platform_views_controller=*/flutterPlatformViewsController, -// /*task_runners=*/runners, -// /*worker_task_runner=*/nil, -// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - -// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = -// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; -// flutterPlatformViewsController->RegisterViewFactory( -// factory, @"MockFlutterPlatformView", -// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); -// FlutterResult result = ^(id result) { -// }; -// flutterPlatformViewsController->OnMethodCall( -// [FlutterMethodCall -// methodCallWithMethodName:@"create" -// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], -// result); - -// XCTAssertNotNil(gMockPlatformView); -// } - -// - (void)testChildClippingViewHitTests { -// ChildClippingView* childClippingView = -// [[ChildClippingView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; -// UIView* childView = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)]; -// [childClippingView addSubview:childView]; - -// XCTAssertFalse([childClippingView pointInside:CGPointMake(50, 50) withEvent:nil]); -// XCTAssertFalse([childClippingView pointInside:CGPointMake(99, 100) withEvent:nil]); -// XCTAssertFalse([childClippingView pointInside:CGPointMake(100, 99) withEvent:nil]); -// XCTAssertFalse([childClippingView pointInside:CGPointMake(201, 200) withEvent:nil]); -// XCTAssertFalse([childClippingView pointInside:CGPointMake(200, 201) withEvent:nil]); -// XCTAssertFalse([childClippingView pointInside:CGPointMake(99, 200) withEvent:nil]); -// XCTAssertFalse([childClippingView pointInside:CGPointMake(200, 299) withEvent:nil]); - -// XCTAssertTrue([childClippingView pointInside:CGPointMake(150, 150) withEvent:nil]); -// XCTAssertTrue([childClippingView pointInside:CGPointMake(100, 100) withEvent:nil]); -// XCTAssertTrue([childClippingView pointInside:CGPointMake(199, 100) withEvent:nil]); -// XCTAssertTrue([childClippingView pointInside:CGPointMake(100, 199) withEvent:nil]); -// XCTAssertTrue([childClippingView pointInside:CGPointMake(199, 199) withEvent:nil]); -// } - -// - (void)testReleasesBackdropFilterSubviewsOnChildClippingViewDealloc { -// __weak NSMutableArray* weakBackdropFilterSubviews = nil; -// __weak UIVisualEffectView* weakVisualEffectView1 = nil; -// __weak UIVisualEffectView* weakVisualEffectView2 = nil; - -// @autoreleasepool { -// ChildClippingView* clippingView = [[ChildClippingView alloc] initWithFrame:CGRectZero]; -// UIVisualEffectView* visualEffectView1 = [[UIVisualEffectView alloc] -// initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]]; -// weakVisualEffectView1 = visualEffectView1; -// PlatformViewFilter* platformViewFilter1 = -// [[PlatformViewFilter alloc] initWithFrame:CGRectMake(0, 0, 10, 10) -// blurRadius:5 -// visualEffectView:visualEffectView1]; - -// [clippingView applyBlurBackdropFilters:@[ platformViewFilter1 ]]; - -// // Replace the blur filter to validate the original and new UIVisualEffectView are released. -// UIVisualEffectView* visualEffectView2 = [[UIVisualEffectView alloc] -// initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleDark]]; -// weakVisualEffectView2 = visualEffectView2; -// PlatformViewFilter* platformViewFilter2 = -// [[PlatformViewFilter alloc] initWithFrame:CGRectMake(0, 0, 10, 10) -// blurRadius:5 -// visualEffectView:visualEffectView2]; -// [clippingView applyBlurBackdropFilters:@[ platformViewFilter2 ]]; - -// weakBackdropFilterSubviews = clippingView.backdropFilterSubviews; -// XCTAssertNotNil(weakBackdropFilterSubviews); -// clippingView = nil; -// } -// XCTAssertNil(weakBackdropFilterSubviews); -// XCTAssertNil(weakVisualEffectView1); -// XCTAssertNil(weakVisualEffectView2); -// } - -// - (void)testApplyBackdropFilter { -// 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(); -// auto platform_view = std::make_unique( -// /*delegate=*/mock_delegate, -// /*rendering_api=*/mock_delegate.settings_.enable_impeller -// ? flutter::IOSRenderingAPI::kMetal -// : flutter::IOSRenderingAPI::kSoftware, -// /*platform_views_controller=*/flutterPlatformViewsController, -// /*task_runners=*/runners, -// /*worker_task_runner=*/nil, -// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - -// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = -// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; -// flutterPlatformViewsController->RegisterViewFactory( -// factory, @"MockFlutterPlatformView", -// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); -// FlutterResult result = ^(id result) { -// }; -// flutterPlatformViewsController->OnMethodCall( -// [FlutterMethodCall -// methodCallWithMethodName:@"create" -// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], -// result); - -// XCTAssertNotNil(gMockPlatformView); - -// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; -// flutterPlatformViewsController->SetFlutterView(mockFlutterView); -// // Create embedded view params -// flutter::MutatorsStack stack; -// // Layer tree always pushes a screen scale factor to the stack -// CGFloat screenScale = [UIScreen mainScreen].scale; -// SkMatrix screenScaleMatrix = SkMatrix::Scale(screenScale, screenScale); -// stack.PushTransform(screenScaleMatrix); -// // Push a backdrop filter -// auto filter = std::make_shared(5, 2, flutter::DlTileMode::kClamp); -// stack.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); - -// auto embeddedViewParams = -// std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); - -// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); -// flutterPlatformViewsController->CompositeEmbeddedView(2); -// XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]); -// ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; -// [mockFlutterView addSubview:childClippingView]; - -// [mockFlutterView setNeedsLayout]; -// [mockFlutterView layoutIfNeeded]; - -// // childClippingView has visual effect view with the correct configurations. -// NSUInteger numberOfExpectedVisualEffectView = 0; -// for (UIView* subview in childClippingView.subviews) { -// if (![subview isKindOfClass:[UIVisualEffectView class]]) { -// continue; -// } -// XCTAssertLessThan(numberOfExpectedVisualEffectView, 1u); -// if ([self validateOneVisualEffectView:subview -// expectedFrame:CGRectMake(0, 0, 10, 10) -// inputRadius:5]) { -// numberOfExpectedVisualEffectView++; -// } -// } -// XCTAssertEqual(numberOfExpectedVisualEffectView, 1u); -// } - -// - (void)testApplyBackdropFilterWithCorrectFrame { -// 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(); -// auto platform_view = std::make_unique( -// /*delegate=*/mock_delegate, -// /*rendering_api=*/mock_delegate.settings_.enable_impeller -// ? flutter::IOSRenderingAPI::kMetal -// : flutter::IOSRenderingAPI::kSoftware, -// /*platform_views_controller=*/flutterPlatformViewsController, -// /*task_runners=*/runners, -// /*worker_task_runner=*/nil, -// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - -// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = -// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; -// flutterPlatformViewsController->RegisterViewFactory( -// factory, @"MockFlutterPlatformView", -// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); -// FlutterResult result = ^(id result) { -// }; -// flutterPlatformViewsController->OnMethodCall( -// [FlutterMethodCall -// methodCallWithMethodName:@"create" -// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], -// result); - -// XCTAssertNotNil(gMockPlatformView); - -// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; -// flutterPlatformViewsController->SetFlutterView(mockFlutterView); -// // Create embedded view params -// flutter::MutatorsStack stack; -// // Layer tree always pushes a screen scale factor to the stack -// CGFloat screenScale = [UIScreen mainScreen].scale; -// SkMatrix screenScaleMatrix = SkMatrix::Scale(screenScale, screenScale); -// stack.PushTransform(screenScaleMatrix); -// // Push a backdrop filter -// auto filter = std::make_shared(5, 2, flutter::DlTileMode::kClamp); -// stack.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 8, screenScale * 8)); - -// auto embeddedViewParams = -// std::make_unique(screenScaleMatrix, SkSize::Make(5, 10), stack); - -// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); -// flutterPlatformViewsController->CompositeEmbeddedView(2); -// XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]); -// ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; -// [mockFlutterView addSubview:childClippingView]; - -// [mockFlutterView setNeedsLayout]; -// [mockFlutterView layoutIfNeeded]; - -// // childClippingView has visual effect view with the correct configurations. -// NSUInteger numberOfExpectedVisualEffectView = 0; -// for (UIView* subview in childClippingView.subviews) { -// if (![subview isKindOfClass:[UIVisualEffectView class]]) { -// continue; -// } -// XCTAssertLessThan(numberOfExpectedVisualEffectView, 1u); -// if ([self validateOneVisualEffectView:subview -// expectedFrame:CGRectMake(0, 0, 5, 8) -// inputRadius:5]) { -// numberOfExpectedVisualEffectView++; -// } -// } -// XCTAssertEqual(numberOfExpectedVisualEffectView, 1u); -// } - -// - (void)testApplyMultipleBackdropFilters { -// 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(); -// auto platform_view = std::make_unique( -// /*delegate=*/mock_delegate, -// /*rendering_api=*/mock_delegate.settings_.enable_impeller -// ? flutter::IOSRenderingAPI::kMetal -// : flutter::IOSRenderingAPI::kSoftware, -// /*platform_views_controller=*/flutterPlatformViewsController, -// /*task_runners=*/runners, -// /*worker_task_runner=*/nil, -// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - -// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = -// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; -// flutterPlatformViewsController->RegisterViewFactory( -// factory, @"MockFlutterPlatformView", -// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); -// FlutterResult result = ^(id result) { -// }; -// flutterPlatformViewsController->OnMethodCall( -// [FlutterMethodCall -// methodCallWithMethodName:@"create" -// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], -// result); - -// XCTAssertNotNil(gMockPlatformView); - -// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; -// flutterPlatformViewsController->SetFlutterView(mockFlutterView); -// // Create embedded view params -// flutter::MutatorsStack stack; -// // Layer tree always pushes a screen scale factor to the stack -// CGFloat screenScale = [UIScreen mainScreen].scale; -// SkMatrix screenScaleMatrix = SkMatrix::Scale(screenScale, screenScale); -// stack.PushTransform(screenScaleMatrix); -// // Push backdrop filters -// for (int i = 0; i < 50; i++) { -// auto filter = std::make_shared(i, 2, flutter::DlTileMode::kClamp); -// stack.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); -// } - -// auto embeddedViewParams = -// std::make_unique(screenScaleMatrix, SkSize::Make(20, 20), stack); - -// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); -// flutterPlatformViewsController->CompositeEmbeddedView(2); -// XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); -// ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; -// [mockFlutterView addSubview:childClippingView]; - -// [mockFlutterView setNeedsLayout]; -// [mockFlutterView layoutIfNeeded]; - -// NSUInteger numberOfExpectedVisualEffectView = 0; -// for (UIView* subview in childClippingView.subviews) { -// if (![subview isKindOfClass:[UIVisualEffectView class]]) { -// continue; -// } -// XCTAssertLessThan(numberOfExpectedVisualEffectView, 50u); -// if ([self validateOneVisualEffectView:subview -// expectedFrame:CGRectMake(0, 0, 10, 10) -// inputRadius:(CGFloat)numberOfExpectedVisualEffectView]) { -// numberOfExpectedVisualEffectView++; -// } -// } -// XCTAssertEqual(numberOfExpectedVisualEffectView, (NSUInteger)numberOfExpectedVisualEffectView); -// } - -// - (void)testAddBackdropFilters { -// 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(); -// auto platform_view = std::make_unique( -// /*delegate=*/mock_delegate, -// /*rendering_api=*/mock_delegate.settings_.enable_impeller -// ? flutter::IOSRenderingAPI::kMetal -// : flutter::IOSRenderingAPI::kSoftware, -// /*platform_views_controller=*/flutterPlatformViewsController, -// /*task_runners=*/runners, -// /*worker_task_runner=*/nil, -// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - -// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = -// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; -// flutterPlatformViewsController->RegisterViewFactory( -// factory, @"MockFlutterPlatformView", -// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); -// FlutterResult result = ^(id result) { -// }; -// flutterPlatformViewsController->OnMethodCall( -// [FlutterMethodCall -// methodCallWithMethodName:@"create" -// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], -// result); - -// XCTAssertNotNil(gMockPlatformView); - -// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; -// flutterPlatformViewsController->SetFlutterView(mockFlutterView); -// // Create embedded view params -// flutter::MutatorsStack stack; -// // Layer tree always pushes a screen scale factor to the stack -// CGFloat screenScale = [UIScreen mainScreen].scale; -// SkMatrix screenScaleMatrix = SkMatrix::Scale(screenScale, screenScale); -// stack.PushTransform(screenScaleMatrix); -// // Push a backdrop filter -// auto filter = std::make_shared(5, 2, flutter::DlTileMode::kClamp); -// stack.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); - -// auto embeddedViewParams = -// std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); - -// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); -// flutterPlatformViewsController->CompositeEmbeddedView(2); -// XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]); -// ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; -// [mockFlutterView addSubview:childClippingView]; - -// [mockFlutterView setNeedsLayout]; -// [mockFlutterView layoutIfNeeded]; - -// NSMutableArray* originalVisualEffectViews = [[NSMutableArray alloc] init]; -// for (UIView* subview in childClippingView.subviews) { -// if (![subview isKindOfClass:[UIVisualEffectView class]]) { -// continue; -// } -// XCTAssertLessThan(originalVisualEffectViews.count, 1u); -// if ([self validateOneVisualEffectView:subview -// expectedFrame:CGRectMake(0, 0, 10, 10) -// inputRadius:(CGFloat)5]) { -// [originalVisualEffectViews addObject:subview]; -// } -// } -// XCTAssertEqual(originalVisualEffectViews.count, 1u); - -// // -// // Simulate adding 1 backdrop filter (create a new mutators stack) -// // Create embedded view params -// flutter::MutatorsStack stack2; -// // Layer tree always pushes a screen scale factor to the stack -// stack2.PushTransform(screenScaleMatrix); -// // Push backdrop filters -// for (int i = 0; i < 2; i++) { -// stack2.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); -// } - -// embeddedViewParams = std::make_unique(screenScaleMatrix, -// SkSize::Make(10, 10), stack2); - -// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); -// flutterPlatformViewsController->CompositeEmbeddedView(2); -// [mockFlutterView setNeedsLayout]; -// [mockFlutterView layoutIfNeeded]; - -// NSMutableArray* newVisualEffectViews = [[NSMutableArray alloc] init]; -// for (UIView* subview in childClippingView.subviews) { -// if (![subview isKindOfClass:[UIVisualEffectView class]]) { -// continue; -// } -// XCTAssertLessThan(newVisualEffectViews.count, 2u); - -// if ([self validateOneVisualEffectView:subview -// expectedFrame:CGRectMake(0, 0, 10, 10) -// inputRadius:(CGFloat)5]) { -// [newVisualEffectViews addObject:subview]; -// } -// } -// XCTAssertEqual(newVisualEffectViews.count, 2u); -// for (NSUInteger i = 0; i < originalVisualEffectViews.count; i++) { -// UIView* originalView = originalVisualEffectViews[i]; -// UIView* newView = newVisualEffectViews[i]; -// // Compare reference. -// XCTAssertEqual(originalView, newView); -// id mockOrignalView = OCMPartialMock(originalView); -// OCMReject([mockOrignalView removeFromSuperview]); -// [mockOrignalView stopMocking]; -// } -// } - -// - (void)testRemoveBackdropFilters { -// 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(); -// auto platform_view = std::make_unique( -// /*delegate=*/mock_delegate, -// /*rendering_api=*/mock_delegate.settings_.enable_impeller -// ? flutter::IOSRenderingAPI::kMetal -// : flutter::IOSRenderingAPI::kSoftware, -// /*platform_views_controller=*/flutterPlatformViewsController, -// /*task_runners=*/runners, -// /*worker_task_runner=*/nil, -// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - -// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = -// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; -// flutterPlatformViewsController->RegisterViewFactory( -// factory, @"MockFlutterPlatformView", -// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); -// FlutterResult result = ^(id result) { -// }; -// flutterPlatformViewsController->OnMethodCall( -// [FlutterMethodCall -// methodCallWithMethodName:@"create" -// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], -// result); - -// XCTAssertNotNil(gMockPlatformView); - -// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; -// flutterPlatformViewsController->SetFlutterView(mockFlutterView); -// // Create embedded view params -// flutter::MutatorsStack stack; -// // Layer tree always pushes a screen scale factor to the stack -// CGFloat screenScale = [UIScreen mainScreen].scale; -// SkMatrix screenScaleMatrix = SkMatrix::Scale(screenScale, screenScale); -// stack.PushTransform(screenScaleMatrix); -// // Push backdrop filters -// auto filter = std::make_shared(5, 2, flutter::DlTileMode::kClamp); -// for (int i = 0; i < 5; i++) { -// stack.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); -// } - -// auto embeddedViewParams = -// std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); - -// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); -// flutterPlatformViewsController->CompositeEmbeddedView(2); -// XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); -// ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; -// [mockFlutterView addSubview:childClippingView]; - -// [mockFlutterView setNeedsLayout]; -// [mockFlutterView layoutIfNeeded]; - -// NSMutableArray* originalVisualEffectViews = [[NSMutableArray alloc] init]; -// for (UIView* subview in childClippingView.subviews) { -// if (![subview isKindOfClass:[UIVisualEffectView class]]) { -// continue; -// } -// XCTAssertLessThan(originalVisualEffectViews.count, 5u); -// if ([self validateOneVisualEffectView:subview -// expectedFrame:CGRectMake(0, 0, 10, 10) -// inputRadius:(CGFloat)5]) { -// [originalVisualEffectViews addObject:subview]; -// } -// } - -// // Simulate removing 1 backdrop filter (create a new mutators stack) -// // Create embedded view params -// flutter::MutatorsStack stack2; -// // Layer tree always pushes a screen scale factor to the stack -// stack2.PushTransform(screenScaleMatrix); -// // Push backdrop filters -// for (int i = 0; i < 4; i++) { -// stack2.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); -// } - -// embeddedViewParams = std::make_unique(screenScaleMatrix, -// SkSize::Make(10, 10), stack2); - -// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); -// flutterPlatformViewsController->CompositeEmbeddedView(2); -// [mockFlutterView setNeedsLayout]; -// [mockFlutterView layoutIfNeeded]; - -// NSMutableArray* newVisualEffectViews = [[NSMutableArray alloc] init]; -// for (UIView* subview in childClippingView.subviews) { -// if (![subview isKindOfClass:[UIVisualEffectView class]]) { -// continue; -// } -// XCTAssertLessThan(newVisualEffectViews.count, 4u); -// if ([self validateOneVisualEffectView:subview -// expectedFrame:CGRectMake(0, 0, 10, 10) -// inputRadius:(CGFloat)5]) { -// [newVisualEffectViews addObject:subview]; -// } -// } -// XCTAssertEqual(newVisualEffectViews.count, 4u); - -// for (NSUInteger i = 0; i < newVisualEffectViews.count; i++) { -// UIView* newView = newVisualEffectViews[i]; -// id mockNewView = OCMPartialMock(newView); -// UIView* originalView = originalVisualEffectViews[i]; -// // Compare reference. -// XCTAssertEqual(originalView, newView); -// OCMReject([mockNewView removeFromSuperview]); -// [mockNewView stopMocking]; -// } - -// // Simulate removing all backdrop filters (replace the mutators stack) -// // Update embedded view params, delete except screenScaleMatrix -// for (int i = 0; i < 5; i++) { -// stack2.Pop(); -// } -// // No backdrop filters in the stack, so no nothing to push - -// embeddedViewParams = std::make_unique(screenScaleMatrix, -// SkSize::Make(10, 10), stack2); - -// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); -// flutterPlatformViewsController->CompositeEmbeddedView(2); -// [mockFlutterView setNeedsLayout]; -// [mockFlutterView layoutIfNeeded]; - -// NSUInteger numberOfExpectedVisualEffectView = 0u; -// for (UIView* subview in childClippingView.subviews) { -// if ([subview isKindOfClass:[UIVisualEffectView class]]) { -// numberOfExpectedVisualEffectView++; -// } -// } -// XCTAssertEqual(numberOfExpectedVisualEffectView, 0u); -// } - -// - (void)testEditBackdropFilters { -// 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(); -// auto platform_view = std::make_unique( -// /*delegate=*/mock_delegate, -// /*rendering_api=*/mock_delegate.settings_.enable_impeller -// ? flutter::IOSRenderingAPI::kMetal -// : flutter::IOSRenderingAPI::kSoftware, -// /*platform_views_controller=*/flutterPlatformViewsController, -// /*task_runners=*/runners, -// /*worker_task_runner=*/nil, -// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - -// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = -// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; -// flutterPlatformViewsController->RegisterViewFactory( -// factory, @"MockFlutterPlatformView", -// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); -// FlutterResult result = ^(id result) { -// }; -// flutterPlatformViewsController->OnMethodCall( -// [FlutterMethodCall -// methodCallWithMethodName:@"create" -// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], -// result); - -// XCTAssertNotNil(gMockPlatformView); - -// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; -// flutterPlatformViewsController->SetFlutterView(mockFlutterView); -// // Create embedded view params -// flutter::MutatorsStack stack; -// // Layer tree always pushes a screen scale factor to the stack -// CGFloat screenScale = [UIScreen mainScreen].scale; -// SkMatrix screenScaleMatrix = SkMatrix::Scale(screenScale, screenScale); -// stack.PushTransform(screenScaleMatrix); -// // Push backdrop filters -// auto filter = std::make_shared(5, 2, flutter::DlTileMode::kClamp); -// for (int i = 0; i < 5; i++) { -// stack.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); -// } - -// auto embeddedViewParams = -// std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); - -// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); -// flutterPlatformViewsController->CompositeEmbeddedView(2); -// XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); -// ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; -// [mockFlutterView addSubview:childClippingView]; - -// [mockFlutterView setNeedsLayout]; -// [mockFlutterView layoutIfNeeded]; - -// NSMutableArray* originalVisualEffectViews = [[NSMutableArray alloc] init]; -// for (UIView* subview in childClippingView.subviews) { -// if (![subview isKindOfClass:[UIVisualEffectView class]]) { -// continue; -// } -// XCTAssertLessThan(originalVisualEffectViews.count, 5u); -// if ([self validateOneVisualEffectView:subview -// expectedFrame:CGRectMake(0, 0, 10, 10) -// inputRadius:(CGFloat)5]) { -// [originalVisualEffectViews addObject:subview]; -// } -// } - -// // Simulate editing 1 backdrop filter in the middle of the stack (create a new mutators stack) -// // Create embedded view params -// flutter::MutatorsStack stack2; -// // Layer tree always pushes a screen scale factor to the stack -// stack2.PushTransform(screenScaleMatrix); -// // Push backdrop filters -// for (int i = 0; i < 5; i++) { -// if (i == 3) { -// auto filter2 = -// std::make_shared(2, 5, flutter::DlTileMode::kClamp); - -// stack2.PushBackdropFilter(filter2, -// SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); -// continue; -// } - -// stack2.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); -// } - -// embeddedViewParams = std::make_unique(screenScaleMatrix, -// SkSize::Make(10, 10), stack2); - -// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); -// flutterPlatformViewsController->CompositeEmbeddedView(2); -// [mockFlutterView setNeedsLayout]; -// [mockFlutterView layoutIfNeeded]; - -// NSMutableArray* newVisualEffectViews = [[NSMutableArray alloc] init]; -// for (UIView* subview in childClippingView.subviews) { -// if (![subview isKindOfClass:[UIVisualEffectView class]]) { -// continue; -// } -// XCTAssertLessThan(newVisualEffectViews.count, 5u); -// CGFloat expectInputRadius = 5; -// if (newVisualEffectViews.count == 3) { -// expectInputRadius = 2; -// } -// if ([self validateOneVisualEffectView:subview -// expectedFrame:CGRectMake(0, 0, 10, 10) -// inputRadius:(CGFloat)expectInputRadius]) { -// [newVisualEffectViews addObject:subview]; -// } -// } -// XCTAssertEqual(newVisualEffectViews.count, 5u); -// for (NSUInteger i = 0; i < newVisualEffectViews.count; i++) { -// UIView* newView = newVisualEffectViews[i]; -// id mockNewView = OCMPartialMock(newView); -// UIView* originalView = originalVisualEffectViews[i]; -// // Compare reference. -// XCTAssertEqual(originalView, newView); -// OCMReject([mockNewView removeFromSuperview]); -// [mockNewView stopMocking]; -// } -// [newVisualEffectViews removeAllObjects]; - -// // Simulate editing 1 backdrop filter in the beginning of the stack (replace the mutators stack) -// // Update embedded view params, delete except screenScaleMatrix -// for (int i = 0; i < 5; i++) { -// stack2.Pop(); -// } -// // Push backdrop filters -// for (int i = 0; i < 5; i++) { -// if (i == 0) { -// auto filter2 = -// std::make_shared(2, 5, flutter::DlTileMode::kClamp); -// stack2.PushBackdropFilter(filter2, -// SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); -// continue; -// } - -// stack2.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); -// } - -// embeddedViewParams = std::make_unique(screenScaleMatrix, -// SkSize::Make(10, 10), stack2); - -// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); -// flutterPlatformViewsController->CompositeEmbeddedView(2); -// [mockFlutterView setNeedsLayout]; -// [mockFlutterView layoutIfNeeded]; - -// for (UIView* subview in childClippingView.subviews) { -// if (![subview isKindOfClass:[UIVisualEffectView class]]) { -// continue; -// } -// XCTAssertLessThan(newVisualEffectViews.count, 5u); -// CGFloat expectInputRadius = 5; -// if (newVisualEffectViews.count == 0) { -// expectInputRadius = 2; -// } -// if ([self validateOneVisualEffectView:subview -// expectedFrame:CGRectMake(0, 0, 10, 10) -// inputRadius:(CGFloat)expectInputRadius]) { -// [newVisualEffectViews addObject:subview]; -// } -// } -// for (NSUInteger i = 0; i < newVisualEffectViews.count; i++) { -// UIView* newView = newVisualEffectViews[i]; -// id mockNewView = OCMPartialMock(newView); -// UIView* originalView = originalVisualEffectViews[i]; -// // Compare reference. -// XCTAssertEqual(originalView, newView); -// OCMReject([mockNewView removeFromSuperview]); -// [mockNewView stopMocking]; -// } -// [newVisualEffectViews removeAllObjects]; - -// // Simulate editing 1 backdrop filter in the end of the stack (replace the mutators stack) -// // Update embedded view params, delete except screenScaleMatrix -// for (int i = 0; i < 5; i++) { -// stack2.Pop(); -// } -// // Push backdrop filters -// for (int i = 0; i < 5; i++) { -// if (i == 4) { -// auto filter2 = -// std::make_shared(2, 5, flutter::DlTileMode::kClamp); -// stack2.PushBackdropFilter(filter2, -// SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); -// continue; -// } - -// stack2.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); -// } - -// embeddedViewParams = std::make_unique(screenScaleMatrix, -// SkSize::Make(10, 10), stack2); - -// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); -// flutterPlatformViewsController->CompositeEmbeddedView(2); -// [mockFlutterView setNeedsLayout]; -// [mockFlutterView layoutIfNeeded]; - -// for (UIView* subview in childClippingView.subviews) { -// if (![subview isKindOfClass:[UIVisualEffectView class]]) { -// continue; -// } -// XCTAssertLessThan(newVisualEffectViews.count, 5u); -// CGFloat expectInputRadius = 5; -// if (newVisualEffectViews.count == 4) { -// expectInputRadius = 2; -// } -// if ([self validateOneVisualEffectView:subview -// expectedFrame:CGRectMake(0, 0, 10, 10) -// inputRadius:(CGFloat)expectInputRadius]) { -// [newVisualEffectViews addObject:subview]; -// } -// } -// XCTAssertEqual(newVisualEffectViews.count, 5u); - -// for (NSUInteger i = 0; i < newVisualEffectViews.count; i++) { -// UIView* newView = newVisualEffectViews[i]; -// id mockNewView = OCMPartialMock(newView); -// UIView* originalView = originalVisualEffectViews[i]; -// // Compare reference. -// XCTAssertEqual(originalView, newView); -// OCMReject([mockNewView removeFromSuperview]); -// [mockNewView stopMocking]; -// } -// [newVisualEffectViews removeAllObjects]; - -// // Simulate editing all backdrop filters in the stack (replace the mutators stack) -// // Update embedded view params, delete except screenScaleMatrix -// for (int i = 0; i < 5; i++) { -// stack2.Pop(); -// } -// // Push backdrop filters -// for (int i = 0; i < 5; i++) { -// auto filter2 = std::make_shared(i, 2, flutter::DlTileMode::kClamp); - -// stack2.PushBackdropFilter(filter2, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); -// } - -// embeddedViewParams = std::make_unique(screenScaleMatrix, -// SkSize::Make(10, 10), stack2); - -// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); -// flutterPlatformViewsController->CompositeEmbeddedView(2); -// [mockFlutterView setNeedsLayout]; -// [mockFlutterView layoutIfNeeded]; - -// for (UIView* subview in childClippingView.subviews) { -// if (![subview isKindOfClass:[UIVisualEffectView class]]) { -// continue; -// } -// XCTAssertLessThan(newVisualEffectViews.count, 5u); -// if ([self validateOneVisualEffectView:subview -// expectedFrame:CGRectMake(0, 0, 10, 10) -// inputRadius:(CGFloat)newVisualEffectViews.count]) { -// [newVisualEffectViews addObject:subview]; -// } -// } -// XCTAssertEqual(newVisualEffectViews.count, 5u); - -// for (NSUInteger i = 0; i < newVisualEffectViews.count; i++) { -// UIView* newView = newVisualEffectViews[i]; -// id mockNewView = OCMPartialMock(newView); -// UIView* originalView = originalVisualEffectViews[i]; -// // Compare reference. -// XCTAssertEqual(originalView, newView); -// OCMReject([mockNewView removeFromSuperview]); -// [mockNewView stopMocking]; -// } -// [newVisualEffectViews removeAllObjects]; -// } - -// - (void)testApplyBackdropFilterNotDlBlurImageFilter { -// 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(); -// auto platform_view = std::make_unique( -// /*delegate=*/mock_delegate, -// /*rendering_api=*/mock_delegate.settings_.enable_impeller -// ? flutter::IOSRenderingAPI::kMetal -// : flutter::IOSRenderingAPI::kSoftware, -// /*platform_views_controller=*/flutterPlatformViewsController, -// /*task_runners=*/runners, -// /*worker_task_runner=*/nil, -// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - -// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = -// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; -// flutterPlatformViewsController->RegisterViewFactory( -// factory, @"MockFlutterPlatformView", -// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); -// FlutterResult result = ^(id result) { -// }; -// flutterPlatformViewsController->OnMethodCall( -// [FlutterMethodCall -// methodCallWithMethodName:@"create" -// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], -// result); - -// XCTAssertNotNil(gMockPlatformView); - -// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; -// flutterPlatformViewsController->SetFlutterView(mockFlutterView); -// // Create embedded view params -// flutter::MutatorsStack stack; -// // Layer tree always pushes a screen scale factor to the stack -// CGFloat screenScale = [UIScreen mainScreen].scale; -// SkMatrix screenScaleMatrix = SkMatrix::Scale(screenScale, screenScale); -// stack.PushTransform(screenScaleMatrix); -// // Push a dilate backdrop filter -// auto dilateFilter = std::make_shared(5, 2); -// stack.PushBackdropFilter(dilateFilter, SkRect::MakeEmpty()); - -// auto embeddedViewParams = -// std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); - -// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); -// flutterPlatformViewsController->CompositeEmbeddedView(2); -// XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]); -// ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; - -// [mockFlutterView addSubview:childClippingView]; - -// [mockFlutterView setNeedsLayout]; -// [mockFlutterView layoutIfNeeded]; - -// NSUInteger numberOfExpectedVisualEffectView = 0; -// for (UIView* subview in childClippingView.subviews) { -// if ([subview isKindOfClass:[UIVisualEffectView class]]) { -// numberOfExpectedVisualEffectView++; -// } -// } -// XCTAssertEqual(numberOfExpectedVisualEffectView, 0u); - -// // Simulate adding a non-DlBlurImageFilter in the middle of the stack (create a new mutators -// // stack) Create embedded view params -// flutter::MutatorsStack stack2; -// // Layer tree always pushes a screen scale factor to the stack -// stack2.PushTransform(screenScaleMatrix); -// // Push backdrop filters and dilate filter -// auto blurFilter = std::make_shared(5, 2, flutter::DlTileMode::kClamp); - -// for (int i = 0; i < 5; i++) { -// if (i == 2) { -// stack2.PushBackdropFilter(dilateFilter, -// SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); -// continue; -// } - -// stack2.PushBackdropFilter(blurFilter, -// SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); -// } - -// embeddedViewParams = std::make_unique(screenScaleMatrix, -// SkSize::Make(10, 10), stack2); - -// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); -// flutterPlatformViewsController->CompositeEmbeddedView(2); -// [mockFlutterView setNeedsLayout]; -// [mockFlutterView layoutIfNeeded]; - -// numberOfExpectedVisualEffectView = 0; -// for (UIView* subview in childClippingView.subviews) { -// if (![subview isKindOfClass:[UIVisualEffectView class]]) { -// continue; -// } -// XCTAssertLessThan(numberOfExpectedVisualEffectView, 4u); -// if ([self validateOneVisualEffectView:subview -// expectedFrame:CGRectMake(0, 0, 10, 10) -// inputRadius:(CGFloat)5]) { -// numberOfExpectedVisualEffectView++; -// } -// } -// XCTAssertEqual(numberOfExpectedVisualEffectView, 4u); - -// // Simulate adding a non-DlBlurImageFilter to the beginning of the stack (replace the mutators -// // stack) Update embedded view params, delete except screenScaleMatrix -// for (int i = 0; i < 5; i++) { -// stack2.Pop(); -// } -// // Push backdrop filters and dilate filter -// for (int i = 0; i < 5; i++) { -// if (i == 0) { -// stack2.PushBackdropFilter(dilateFilter, -// SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); -// continue; -// } - -// stack2.PushBackdropFilter(blurFilter, -// SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); -// } - -// embeddedViewParams = std::make_unique(screenScaleMatrix, -// SkSize::Make(10, 10), stack2); - -// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); -// flutterPlatformViewsController->CompositeEmbeddedView(2); -// [mockFlutterView setNeedsLayout]; -// [mockFlutterView layoutIfNeeded]; - -// numberOfExpectedVisualEffectView = 0; -// for (UIView* subview in childClippingView.subviews) { -// if (![subview isKindOfClass:[UIVisualEffectView class]]) { -// continue; -// } -// XCTAssertLessThan(numberOfExpectedVisualEffectView, 4u); -// if ([self validateOneVisualEffectView:subview -// expectedFrame:CGRectMake(0, 0, 10, 10) -// inputRadius:(CGFloat)5]) { -// numberOfExpectedVisualEffectView++; -// } -// } -// XCTAssertEqual(numberOfExpectedVisualEffectView, 4u); - -// // Simulate adding a non-DlBlurImageFilter to the end of the stack (replace the mutators stack) -// // Update embedded view params, delete except screenScaleMatrix -// for (int i = 0; i < 5; i++) { -// stack2.Pop(); -// } -// // Push backdrop filters and dilate filter -// for (int i = 0; i < 5; i++) { -// if (i == 4) { -// stack2.PushBackdropFilter(dilateFilter, -// SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); -// continue; -// } - -// stack2.PushBackdropFilter(blurFilter, -// SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); -// } - -// embeddedViewParams = std::make_unique(screenScaleMatrix, -// SkSize::Make(10, 10), stack2); - -// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); -// flutterPlatformViewsController->CompositeEmbeddedView(2); -// [mockFlutterView setNeedsLayout]; -// [mockFlutterView layoutIfNeeded]; - -// numberOfExpectedVisualEffectView = 0; -// for (UIView* subview in childClippingView.subviews) { -// if (![subview isKindOfClass:[UIVisualEffectView class]]) { -// continue; -// } -// XCTAssertLessThan(numberOfExpectedVisualEffectView, 4u); -// if ([self validateOneVisualEffectView:subview -// expectedFrame:CGRectMake(0, 0, 10, 10) -// inputRadius:(CGFloat)5]) { -// numberOfExpectedVisualEffectView++; -// } -// } -// XCTAssertEqual(numberOfExpectedVisualEffectView, 4u); - -// // Simulate adding only non-DlBlurImageFilter to the stack (replace the mutators stack) -// // Update embedded view params, delete except screenScaleMatrix -// for (int i = 0; i < 5; i++) { -// stack2.Pop(); -// } -// // Push dilate filters -// for (int i = 0; i < 5; i++) { -// stack2.PushBackdropFilter(dilateFilter, -// SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); -// } - -// embeddedViewParams = std::make_unique(screenScaleMatrix, -// SkSize::Make(10, 10), stack2); - -// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); -// flutterPlatformViewsController->CompositeEmbeddedView(2); -// [mockFlutterView setNeedsLayout]; -// [mockFlutterView layoutIfNeeded]; - -// numberOfExpectedVisualEffectView = 0; -// for (UIView* subview in childClippingView.subviews) { -// if ([subview isKindOfClass:[UIVisualEffectView class]]) { -// numberOfExpectedVisualEffectView++; -// } -// } -// XCTAssertEqual(numberOfExpectedVisualEffectView, 0u); -// } - -// - (void)testApplyBackdropFilterCorrectAPI { -// [PlatformViewFilter resetPreparation]; -// // The gaussianBlur filter is extracted from UIVisualEffectView. -// // Each test requires a new PlatformViewFilter -// // Valid UIVisualEffectView API -// UIVisualEffectView* visualEffectView = [[UIVisualEffectView alloc] -// initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]]; -// PlatformViewFilter* platformViewFilter = -// [[PlatformViewFilter alloc] initWithFrame:CGRectMake(0, 0, 10, 10) -// blurRadius:5 -// visualEffectView:visualEffectView]; -// XCTAssertNotNil(platformViewFilter); -// } - -// - (void)testApplyBackdropFilterAPIChangedInvalidUIVisualEffectView { -// [PlatformViewFilter resetPreparation]; -// UIVisualEffectView* visualEffectView = [[UIVisualEffectView alloc] init]; -// PlatformViewFilter* platformViewFilter = -// [[PlatformViewFilter alloc] initWithFrame:CGRectMake(0, 0, 10, 10) -// blurRadius:5 -// visualEffectView:visualEffectView]; -// XCTAssertNil(platformViewFilter); -// } - -// - (void)testApplyBackdropFilterAPIChangedNoGaussianBlurFilter { -// [PlatformViewFilter resetPreparation]; -// UIVisualEffectView* editedUIVisualEffectView = [[UIVisualEffectView alloc] -// initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]]; -// NSArray* subviews = editedUIVisualEffectView.subviews; -// for (UIView* view in subviews) { -// if ([NSStringFromClass([view class]) hasSuffix:@"BackdropView"]) { -// for (CIFilter* filter in view.layer.filters) { -// if ([[filter valueForKey:@"name"] isEqual:@"gaussianBlur"]) { -// [filter setValue:@"notGaussianBlur" forKey:@"name"]; -// break; -// } -// } -// break; -// } -// } -// PlatformViewFilter* platformViewFilter = -// [[PlatformViewFilter alloc] initWithFrame:CGRectMake(0, 0, 10, 10) -// blurRadius:5 -// visualEffectView:editedUIVisualEffectView]; -// XCTAssertNil(platformViewFilter); -// } - -// - (void)testApplyBackdropFilterAPIChangedInvalidInputRadius { -// [PlatformViewFilter resetPreparation]; -// UIVisualEffectView* editedUIVisualEffectView = [[UIVisualEffectView alloc] -// initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]]; -// NSArray* subviews = editedUIVisualEffectView.subviews; -// for (UIView* view in subviews) { -// if ([NSStringFromClass([view class]) hasSuffix:@"BackdropView"]) { -// for (CIFilter* filter in view.layer.filters) { -// if ([[filter valueForKey:@"name"] isEqual:@"gaussianBlur"]) { -// [filter setValue:@"invalidInputRadius" forKey:@"inputRadius"]; -// break; -// } -// } -// break; -// } -// } - -// PlatformViewFilter* platformViewFilter = -// [[PlatformViewFilter alloc] initWithFrame:CGRectMake(0, 0, 10, 10) -// blurRadius:5 -// visualEffectView:editedUIVisualEffectView]; -// XCTAssertNil(platformViewFilter); -// } - -// - (void)testBackdropFilterVisualEffectSubviewBackgroundColor { -// __weak UIVisualEffectView* weakVisualEffectView; - -// @autoreleasepool { -// UIVisualEffectView* visualEffectView = [[UIVisualEffectView alloc] -// initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]]; -// weakVisualEffectView = visualEffectView; -// PlatformViewFilter* platformViewFilter = -// [[PlatformViewFilter alloc] initWithFrame:CGRectMake(0, 0, 10, 10) -// blurRadius:5 -// visualEffectView:visualEffectView]; -// CGColorRef visualEffectSubviewBackgroundColor = nil; -// for (UIView* view in [platformViewFilter backdropFilterView].subviews) { -// if ([NSStringFromClass([view class]) hasSuffix:@"VisualEffectSubview"]) { -// visualEffectSubviewBackgroundColor = view.layer.backgroundColor; -// } -// } -// XCTAssertTrue( -// CGColorEqualToColor(visualEffectSubviewBackgroundColor, UIColor.clearColor.CGColor)); -// } -// XCTAssertNil(weakVisualEffectView); -// } - -// - (void)testCompositePlatformView { -// 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(); -// auto platform_view = std::make_unique( -// /*delegate=*/mock_delegate, -// /*rendering_api=*/mock_delegate.settings_.enable_impeller -// ? flutter::IOSRenderingAPI::kMetal -// : flutter::IOSRenderingAPI::kSoftware, -// /*platform_views_controller=*/flutterPlatformViewsController, -// /*task_runners=*/runners, -// /*worker_task_runner=*/nil, -// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - -// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = -// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; -// flutterPlatformViewsController->RegisterViewFactory( -// factory, @"MockFlutterPlatformView", -// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); -// FlutterResult result = ^(id result) { -// }; -// flutterPlatformViewsController->OnMethodCall( -// [FlutterMethodCall -// methodCallWithMethodName:@"create" -// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], -// result); - -// XCTAssertNotNil(gMockPlatformView); - -// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; -// flutterPlatformViewsController->SetFlutterView(mockFlutterView); -// // Create embedded view params -// flutter::MutatorsStack stack; -// // Layer tree always pushes a screen scale factor to the stack -// SkMatrix screenScaleMatrix = -// SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); -// stack.PushTransform(screenScaleMatrix); -// // Push a translate matrix -// SkMatrix translateMatrix = SkMatrix::Translate(100, 100); -// stack.PushTransform(translateMatrix); -// SkMatrix finalMatrix; -// finalMatrix.setConcat(screenScaleMatrix, translateMatrix); - -// auto embeddedViewParams = -// std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); - -// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); -// flutterPlatformViewsController->CompositeEmbeddedView(2); -// CGRect platformViewRectInFlutterView = [gMockPlatformView convertRect:gMockPlatformView.bounds -// toView:mockFlutterView]; -// XCTAssertTrue(CGRectEqualToRect(platformViewRectInFlutterView, CGRectMake(100, 100, 300, 300))); -// } - -// - (void)testBackdropFilterCorrectlyPushedAndReset { -// 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(); -// auto platform_view = std::make_unique( -// /*delegate=*/mock_delegate, -// /*rendering_api=*/mock_delegate.settings_.enable_impeller -// ? flutter::IOSRenderingAPI::kMetal -// : flutter::IOSRenderingAPI::kSoftware, -// /*platform_views_controller=*/flutterPlatformViewsController, -// /*task_runners=*/runners, -// /*worker_task_runner=*/nil, -// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - -// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = -// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; -// flutterPlatformViewsController->RegisterViewFactory( -// factory, @"MockFlutterPlatformView", -// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); -// FlutterResult result = ^(id result) { -// }; -// flutterPlatformViewsController->OnMethodCall( -// [FlutterMethodCall -// methodCallWithMethodName:@"create" -// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], -// result); - -// XCTAssertNotNil(gMockPlatformView); - -// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; -// flutterPlatformViewsController->SetFlutterView(mockFlutterView); -// // Create embedded view params -// flutter::MutatorsStack stack; -// // Layer tree always pushes a screen scale factor to the stack -// CGFloat screenScale = [UIScreen mainScreen].scale; -// SkMatrix screenScaleMatrix = SkMatrix::Scale(screenScale, screenScale); -// stack.PushTransform(screenScaleMatrix); - -// auto embeddedViewParams = -// std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); - -// flutterPlatformViewsController->BeginFrame(SkISize::Make(0, 0)); -// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); -// flutterPlatformViewsController->PushVisitedPlatformView(2); -// auto filter = std::make_shared(5, 2, flutter::DlTileMode::kClamp); -// flutterPlatformViewsController->PushFilterToVisitedPlatformViews( -// filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); -// flutterPlatformViewsController->CompositeEmbeddedView(2); -// XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]); -// ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; -// [mockFlutterView addSubview:childClippingView]; - -// [mockFlutterView setNeedsLayout]; -// [mockFlutterView layoutIfNeeded]; - -// // childClippingView has visual effect view with the correct configurations. -// NSUInteger numberOfExpectedVisualEffectView = 0; -// for (UIView* subview in childClippingView.subviews) { -// if (![subview isKindOfClass:[UIVisualEffectView class]]) { -// continue; -// } -// XCTAssertLessThan(numberOfExpectedVisualEffectView, 1u); -// if ([self validateOneVisualEffectView:subview -// expectedFrame:CGRectMake(0, 0, 10, 10) -// inputRadius:5]) { -// numberOfExpectedVisualEffectView++; -// } -// } -// XCTAssertEqual(numberOfExpectedVisualEffectView, 1u); - -// // New frame, with no filter pushed. -// auto embeddedViewParams2 = -// std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); -// flutterPlatformViewsController->BeginFrame(SkISize::Make(0, 0)); -// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams2)); -// flutterPlatformViewsController->CompositeEmbeddedView(2); -// XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]); - -// [mockFlutterView setNeedsLayout]; -// [mockFlutterView layoutIfNeeded]; - -// numberOfExpectedVisualEffectView = 0; -// for (UIView* subview in childClippingView.subviews) { -// if (![subview isKindOfClass:[UIVisualEffectView class]]) { -// continue; -// } -// numberOfExpectedVisualEffectView++; -// } -// XCTAssertEqual(numberOfExpectedVisualEffectView, 0u); -// } - -// - (void)testChildClippingViewShouldBeTheBoundingRectOfPlatformView { -// 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(); -// auto platform_view = std::make_unique( -// /*delegate=*/mock_delegate, -// /*rendering_api=*/mock_delegate.settings_.enable_impeller -// ? flutter::IOSRenderingAPI::kMetal -// : flutter::IOSRenderingAPI::kSoftware, -// /*platform_views_controller=*/flutterPlatformViewsController, -// /*task_runners=*/runners, -// /*worker_task_runner=*/nil, -// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - -// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = -// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; -// flutterPlatformViewsController->RegisterViewFactory( -// factory, @"MockFlutterPlatformView", -// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); -// FlutterResult result = ^(id result) { -// }; -// flutterPlatformViewsController->OnMethodCall( -// [FlutterMethodCall -// methodCallWithMethodName:@"create" -// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], -// result); - -// XCTAssertNotNil(gMockPlatformView); - -// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; -// flutterPlatformViewsController->SetFlutterView(mockFlutterView); -// // Create embedded view params -// flutter::MutatorsStack stack; -// // Layer tree always pushes a screen scale factor to the stack -// SkMatrix screenScaleMatrix = -// SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); -// stack.PushTransform(screenScaleMatrix); -// // Push a rotate matrix -// SkMatrix rotateMatrix; -// rotateMatrix.setRotate(10); -// stack.PushTransform(rotateMatrix); -// SkMatrix finalMatrix; -// finalMatrix.setConcat(screenScaleMatrix, rotateMatrix); - -// auto embeddedViewParams = -// std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); - -// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); -// flutterPlatformViewsController->CompositeEmbeddedView(2); -// CGRect platformViewRectInFlutterView = [gMockPlatformView convertRect:gMockPlatformView.bounds -// toView:mockFlutterView]; -// XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); -// ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; -// // The childclippingview's frame is set based on flow, but the platform view's frame is set based -// // on quartz. Although they should be the same, but we should tolerate small floating point -// // errors. -// XCTAssertLessThan(fabs(platformViewRectInFlutterView.origin.x - childClippingView.frame.origin.x), -// kFloatCompareEpsilon); -// XCTAssertLessThan(fabs(platformViewRectInFlutterView.origin.y - childClippingView.frame.origin.y), -// kFloatCompareEpsilon); -// XCTAssertLessThan( -// fabs(platformViewRectInFlutterView.size.width - childClippingView.frame.size.width), -// kFloatCompareEpsilon); -// XCTAssertLessThan( -// fabs(platformViewRectInFlutterView.size.height - childClippingView.frame.size.height), -// kFloatCompareEpsilon); -// } - -// - (void)testClipsDoNotInterceptWithPlatformViewShouldNotAddMaskView { -// 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(); -// auto platform_view = std::make_unique( -// /*delegate=*/mock_delegate, -// /*rendering_api=*/mock_delegate.settings_.enable_impeller -// ? flutter::IOSRenderingAPI::kMetal -// : flutter::IOSRenderingAPI::kSoftware, -// /*platform_views_controller=*/flutterPlatformViewsController, -// /*task_runners=*/runners, -// /*worker_task_runner=*/nil, -// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - -// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = -// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; -// flutterPlatformViewsController->RegisterViewFactory( -// factory, @"MockFlutterPlatformView", -// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); -// FlutterResult result = ^(id result) { -// }; -// flutterPlatformViewsController->OnMethodCall( -// [FlutterMethodCall -// methodCallWithMethodName:@"create" -// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], -// result); - -// XCTAssertNotNil(gMockPlatformView); - -// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 30, 30)]; -// flutterPlatformViewsController->SetFlutterView(mockFlutterView); -// // Create embedded view params. -// flutter::MutatorsStack stack; -// // Layer tree always pushes a screen scale factor to the stack. -// SkMatrix screenScaleMatrix = -// SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); -// stack.PushTransform(screenScaleMatrix); -// SkMatrix translateMatrix = SkMatrix::Translate(5, 5); -// // The platform view's rect for this test will be (5, 5, 10, 10). -// stack.PushTransform(translateMatrix); -// // Push a clip rect, big enough to contain the entire platform view bound. -// SkRect rect = SkRect::MakeXYWH(0, 0, 25, 25); -// stack.PushClipRect(rect); -// // Push a clip rrect, big enough to contain the entire platform view bound without clipping it. -// // Make the origin (-1, -1) so that the top left rounded corner isn't clipping the PlatformView. -// SkRect rect_for_rrect = SkRect::MakeXYWH(-1, -1, 25, 25); -// SkRRect rrect = SkRRect::MakeRectXY(rect_for_rrect, 1, 1); -// stack.PushClipRRect(rrect); - -// auto embeddedViewParams = std::make_unique( -// SkMatrix::Concat(screenScaleMatrix, translateMatrix), SkSize::Make(5, 5), stack); - -// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); -// flutterPlatformViewsController->CompositeEmbeddedView(2); -// gMockPlatformView.backgroundColor = UIColor.redColor; -// XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); -// ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; -// [mockFlutterView addSubview:childClippingView]; - -// [mockFlutterView setNeedsLayout]; -// [mockFlutterView layoutIfNeeded]; -// XCTAssertNil(childClippingView.maskView); -// } - -// - (void)testClipRRectOnlyHasCornersInterceptWithPlatformViewShouldAddMaskView { -// 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(); -// auto platform_view = std::make_unique( -// /*delegate=*/mock_delegate, -// /*rendering_api=*/mock_delegate.settings_.enable_impeller -// ? flutter::IOSRenderingAPI::kMetal -// : flutter::IOSRenderingAPI::kSoftware, -// /*platform_views_controller=*/flutterPlatformViewsController, -// /*task_runners=*/runners, -// /*worker_task_runner=*/nil, -// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - -// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = -// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; -// flutterPlatformViewsController->RegisterViewFactory( -// factory, @"MockFlutterPlatformView", -// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); -// FlutterResult result = ^(id result) { -// }; -// flutterPlatformViewsController->OnMethodCall( -// [FlutterMethodCall -// methodCallWithMethodName:@"create" -// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], -// result); - -// XCTAssertNotNil(gMockPlatformView); - -// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 30, 30)]; -// flutterPlatformViewsController->SetFlutterView(mockFlutterView); -// // Create embedded view params -// flutter::MutatorsStack stack; -// // Layer tree always pushes a screen scale factor to the stack. -// SkMatrix screenScaleMatrix = -// SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); -// stack.PushTransform(screenScaleMatrix); -// SkMatrix translateMatrix = SkMatrix::Translate(5, 5); -// // The platform view's rect for this test will be (5, 5, 10, 10). -// stack.PushTransform(translateMatrix); - -// // Push a clip rrect, the rect of the rrect is the same as the PlatformView of the corner should. -// // clip the PlatformView. -// SkRect rect_for_rrect = SkRect::MakeXYWH(0, 0, 10, 10); -// SkRRect rrect = SkRRect::MakeRectXY(rect_for_rrect, 1, 1); -// stack.PushClipRRect(rrect); - -// auto embeddedViewParams = std::make_unique( -// SkMatrix::Concat(screenScaleMatrix, translateMatrix), SkSize::Make(5, 5), stack); - -// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); -// flutterPlatformViewsController->CompositeEmbeddedView(2); -// gMockPlatformView.backgroundColor = UIColor.redColor; -// XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); -// ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; -// [mockFlutterView addSubview:childClippingView]; - -// [mockFlutterView setNeedsLayout]; -// [mockFlutterView layoutIfNeeded]; - -// XCTAssertNotNil(childClippingView.maskView); -// } - -// - (void)testClipRect { -// 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(); -// auto platform_view = std::make_unique( -// /*delegate=*/mock_delegate, -// /*rendering_api=*/mock_delegate.settings_.enable_impeller -// ? flutter::IOSRenderingAPI::kMetal -// : flutter::IOSRenderingAPI::kSoftware, -// /*platform_views_controller=*/flutterPlatformViewsController, -// /*task_runners=*/runners, -// /*worker_task_runner=*/nil, -// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - -// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = -// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; -// flutterPlatformViewsController->RegisterViewFactory( -// factory, @"MockFlutterPlatformView", -// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); -// FlutterResult result = ^(id result) { -// }; -// flutterPlatformViewsController->OnMethodCall( -// [FlutterMethodCall -// methodCallWithMethodName:@"create" -// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], -// result); - -// XCTAssertNotNil(gMockPlatformView); - -// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; -// flutterPlatformViewsController->SetFlutterView(mockFlutterView); -// // Create embedded view params -// flutter::MutatorsStack stack; -// // Layer tree always pushes a screen scale factor to the stack -// SkMatrix screenScaleMatrix = -// SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); -// stack.PushTransform(screenScaleMatrix); -// // Push a clip rect -// SkRect rect = SkRect::MakeXYWH(2, 2, 3, 3); -// stack.PushClipRect(rect); - -// auto embeddedViewParams = -// std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); - -// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); -// flutterPlatformViewsController->CompositeEmbeddedView(2); -// gMockPlatformView.backgroundColor = UIColor.redColor; -// XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); -// ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; -// [mockFlutterView addSubview:childClippingView]; - -// [mockFlutterView setNeedsLayout]; -// [mockFlutterView layoutIfNeeded]; - -// CGRect insideClipping = CGRectMake(2, 2, 3, 3); -// for (int i = 0; i < 10; i++) { -// for (int j = 0; j < 10; j++) { -// CGPoint point = CGPointMake(i, j); -// int alpha = [self alphaOfPoint:CGPointMake(i, j) onView:mockFlutterView]; -// if (CGRectContainsPoint(insideClipping, point)) { -// XCTAssertEqual(alpha, 255); -// } else { -// XCTAssertEqual(alpha, 0); -// } -// } -// } -// } - -// - (void)testClipRRect { -// 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(); -// auto platform_view = std::make_unique( -// /*delegate=*/mock_delegate, -// /*rendering_api=*/mock_delegate.settings_.enable_impeller -// ? flutter::IOSRenderingAPI::kMetal -// : flutter::IOSRenderingAPI::kSoftware, -// /*platform_views_controller=*/flutterPlatformViewsController, -// /*task_runners=*/runners, -// /*worker_task_runner=*/nil, -// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - -// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = -// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; -// flutterPlatformViewsController->RegisterViewFactory( -// factory, @"MockFlutterPlatformView", -// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); -// FlutterResult result = ^(id result) { -// }; -// flutterPlatformViewsController->OnMethodCall( -// [FlutterMethodCall -// methodCallWithMethodName:@"create" -// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], -// result); - -// XCTAssertNotNil(gMockPlatformView); - -// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; -// flutterPlatformViewsController->SetFlutterView(mockFlutterView); -// // Create embedded view params -// flutter::MutatorsStack stack; -// // Layer tree always pushes a screen scale factor to the stack -// SkMatrix screenScaleMatrix = -// SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); -// stack.PushTransform(screenScaleMatrix); -// // Push a clip rrect -// SkRRect rrect = SkRRect::MakeRectXY(SkRect::MakeXYWH(2, 2, 6, 6), 1, 1); -// stack.PushClipRRect(rrect); - -// auto embeddedViewParams = -// std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); - -// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); -// flutterPlatformViewsController->CompositeEmbeddedView(2); -// gMockPlatformView.backgroundColor = UIColor.redColor; -// XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); -// ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; -// [mockFlutterView addSubview:childClippingView]; - -// [mockFlutterView setNeedsLayout]; -// [mockFlutterView layoutIfNeeded]; - -// /* -// ClippingMask outterClipping -// 2 3 4 5 6 7 2 3 4 5 6 7 -// 2 / - - - - \ 2 + - - - - + -// 3 | | 3 | | -// 4 | | 4 | | -// 5 | | 5 | | -// 6 | | 6 | | -// 7 \ - - - - / 7 + - - - - + - -// innerClipping1 innerClipping2 -// 2 3 4 5 6 7 2 3 4 5 6 7 -// 2 + - - + 2 -// 3 | | 3 + - - - - + -// 4 | | 4 | | -// 5 | | 5 | | -// 6 | | 6 + - - - - + -// 7 + - - + 7 -// */ -// CGRect innerClipping1 = CGRectMake(3, 2, 4, 6); -// CGRect innerClipping2 = CGRectMake(2, 3, 6, 4); -// CGRect outterClipping = CGRectMake(2, 2, 6, 6); -// for (int i = 0; i < 10; i++) { -// for (int j = 0; j < 10; j++) { -// CGPoint point = CGPointMake(i, j); -// int alpha = [self alphaOfPoint:CGPointMake(i, j) onView:mockFlutterView]; -// if (CGRectContainsPoint(innerClipping1, point) || -// CGRectContainsPoint(innerClipping2, point)) { -// // Pixels inside either of the 2 inner clippings should be fully opaque. -// XCTAssertEqual(alpha, 255); -// } else if (CGRectContainsPoint(outterClipping, point)) { -// // Corner pixels (i.e. (2, 2), (2, 7), (7, 2) and (7, 7)) should be partially transparent. -// XCTAssert(0 < alpha && alpha < 255); -// } else { -// // Pixels outside outterClipping should be fully transparent. -// XCTAssertEqual(alpha, 0); -// } -// } -// } -// } - -// - (void)testClipPath { -// 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(); -// auto platform_view = std::make_unique( -// /*delegate=*/mock_delegate, -// /*rendering_api=*/mock_delegate.settings_.enable_impeller -// ? flutter::IOSRenderingAPI::kMetal -// : flutter::IOSRenderingAPI::kSoftware, -// /*platform_views_controller=*/flutterPlatformViewsController, -// /*task_runners=*/runners, -// /*worker_task_runner=*/nil, -// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - -// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = -// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; -// flutterPlatformViewsController->RegisterViewFactory( -// factory, @"MockFlutterPlatformView", -// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); -// FlutterResult result = ^(id result) { -// }; -// flutterPlatformViewsController->OnMethodCall( -// [FlutterMethodCall -// methodCallWithMethodName:@"create" -// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], -// result); - -// XCTAssertNotNil(gMockPlatformView); - -// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; -// flutterPlatformViewsController->SetFlutterView(mockFlutterView); -// // Create embedded view params -// flutter::MutatorsStack stack; -// // Layer tree always pushes a screen scale factor to the stack -// SkMatrix screenScaleMatrix = -// SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); -// stack.PushTransform(screenScaleMatrix); -// // Push a clip path -// SkPath path; -// path.addRoundRect(SkRect::MakeXYWH(2, 2, 6, 6), 1, 1); -// stack.PushClipPath(path); - -// auto embeddedViewParams = -// std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); - -// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); -// flutterPlatformViewsController->CompositeEmbeddedView(2); -// gMockPlatformView.backgroundColor = UIColor.redColor; -// XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); -// ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; -// [mockFlutterView addSubview:childClippingView]; - -// [mockFlutterView setNeedsLayout]; -// [mockFlutterView layoutIfNeeded]; - -// /* -// ClippingMask outterClipping -// 2 3 4 5 6 7 2 3 4 5 6 7 -// 2 / - - - - \ 2 + - - - - + -// 3 | | 3 | | -// 4 | | 4 | | -// 5 | | 5 | | -// 6 | | 6 | | -// 7 \ - - - - / 7 + - - - - + - -// innerClipping1 innerClipping2 -// 2 3 4 5 6 7 2 3 4 5 6 7 -// 2 + - - + 2 -// 3 | | 3 + - - - - + -// 4 | | 4 | | -// 5 | | 5 | | -// 6 | | 6 + - - - - + -// 7 + - - + 7 -// */ -// CGRect innerClipping1 = CGRectMake(3, 2, 4, 6); -// CGRect innerClipping2 = CGRectMake(2, 3, 6, 4); -// CGRect outterClipping = CGRectMake(2, 2, 6, 6); -// for (int i = 0; i < 10; i++) { -// for (int j = 0; j < 10; j++) { -// CGPoint point = CGPointMake(i, j); -// int alpha = [self alphaOfPoint:CGPointMake(i, j) onView:mockFlutterView]; -// if (CGRectContainsPoint(innerClipping1, point) || -// CGRectContainsPoint(innerClipping2, point)) { -// // Pixels inside either of the 2 inner clippings should be fully opaque. -// XCTAssertEqual(alpha, 255); -// } else if (CGRectContainsPoint(outterClipping, point)) { -// // Corner pixels (i.e. (2, 2), (2, 7), (7, 2) and (7, 7)) should be partially transparent. -// XCTAssert(0 < alpha && alpha < 255); -// } else { -// // Pixels outside outterClipping should be fully transparent. -// XCTAssertEqual(alpha, 0); -// } -// } -// } -// } - -// - (void)testSetFlutterViewControllerAfterCreateCanStillDispatchTouchEvents { -// 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(); -// auto platform_view = std::make_unique( -// /*delegate=*/mock_delegate, -// /*rendering_api=*/mock_delegate.settings_.enable_impeller -// ? flutter::IOSRenderingAPI::kMetal -// : flutter::IOSRenderingAPI::kSoftware, -// /*platform_views_controller=*/flutterPlatformViewsController, -// /*task_runners=*/runners, -// /*worker_task_runner=*/nil, -// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - -// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = -// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; -// flutterPlatformViewsController->RegisterViewFactory( -// factory, @"MockFlutterPlatformView", -// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); -// FlutterResult result = ^(id result) { -// }; -// flutterPlatformViewsController->OnMethodCall( -// [FlutterMethodCall -// methodCallWithMethodName:@"create" -// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], -// result); - -// XCTAssertNotNil(gMockPlatformView); - -// // Find touch inteceptor view -// UIView* touchInteceptorView = gMockPlatformView; -// while (touchInteceptorView != nil && -// ![touchInteceptorView isKindOfClass:[FlutterTouchInterceptingView class]]) { -// touchInteceptorView = touchInteceptorView.superview; -// } -// XCTAssertNotNil(touchInteceptorView); - -// // Find ForwardGestureRecognizer -// UIGestureRecognizer* forwardGectureRecognizer = nil; -// for (UIGestureRecognizer* gestureRecognizer in touchInteceptorView.gestureRecognizers) { -// if ([gestureRecognizer isKindOfClass:NSClassFromString(@"ForwardingGestureRecognizer")]) { -// forwardGectureRecognizer = gestureRecognizer; -// break; -// } -// } - -// // Before setting flutter view controller, events are not dispatched. -// NSSet* touches1 = [[NSSet alloc] init]; -// id event1 = OCMClassMock([UIEvent class]); -// id mockFlutterViewContoller = OCMClassMock([FlutterViewController class]); -// [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1]; -// OCMReject([mockFlutterViewContoller touchesBegan:touches1 withEvent:event1]); - -// // Set flutter view controller allows events to be dispatched. -// NSSet* touches2 = [[NSSet alloc] init]; -// id event2 = OCMClassMock([UIEvent class]); -// flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller); -// [forwardGectureRecognizer touchesBegan:touches2 withEvent:event2]; -// OCMVerify([mockFlutterViewContoller touchesBegan:touches2 withEvent:event2]); -// } - -// - (void)testSetFlutterViewControllerInTheMiddleOfTouchEventShouldStillAllowGesturesToBeHandled { -// 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(); -// auto platform_view = std::make_unique( -// /*delegate=*/mock_delegate, -// /*rendering_api=*/mock_delegate.settings_.enable_impeller -// ? flutter::IOSRenderingAPI::kMetal -// : flutter::IOSRenderingAPI::kSoftware, -// /*platform_views_controller=*/flutterPlatformViewsController, -// /*task_runners=*/runners, -// /*worker_task_runner=*/nil, -// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - -// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = -// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; -// flutterPlatformViewsController->RegisterViewFactory( -// factory, @"MockFlutterPlatformView", -// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); -// FlutterResult result = ^(id result) { -// }; -// flutterPlatformViewsController->OnMethodCall( -// [FlutterMethodCall -// methodCallWithMethodName:@"create" -// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], -// result); - -// XCTAssertNotNil(gMockPlatformView); - -// // Find touch inteceptor view -// UIView* touchInteceptorView = gMockPlatformView; -// while (touchInteceptorView != nil && -// ![touchInteceptorView isKindOfClass:[FlutterTouchInterceptingView class]]) { -// touchInteceptorView = touchInteceptorView.superview; -// } -// XCTAssertNotNil(touchInteceptorView); - -// // Find ForwardGestureRecognizer -// UIGestureRecognizer* forwardGectureRecognizer = nil; -// for (UIGestureRecognizer* gestureRecognizer in touchInteceptorView.gestureRecognizers) { -// if ([gestureRecognizer isKindOfClass:NSClassFromString(@"ForwardingGestureRecognizer")]) { -// forwardGectureRecognizer = gestureRecognizer; -// break; -// } -// } -// id mockFlutterViewContoller = OCMClassMock([FlutterViewController class]); -// { -// // ***** Sequence 1, finishing touch event with touchEnded ***** // -// flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller); - -// NSSet* touches1 = [[NSSet alloc] init]; -// id event1 = OCMClassMock([UIEvent class]); -// [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1]; -// OCMVerify([mockFlutterViewContoller touchesBegan:touches1 withEvent:event1]); - -// flutterPlatformViewsController->SetFlutterViewController(nil); - -// // Allow the touch events to finish -// NSSet* touches2 = [[NSSet alloc] init]; -// id event2 = OCMClassMock([UIEvent class]); -// [forwardGectureRecognizer touchesMoved:touches2 withEvent:event2]; -// OCMVerify([mockFlutterViewContoller touchesMoved:touches2 withEvent:event2]); - -// NSSet* touches3 = [[NSSet alloc] init]; -// id event3 = OCMClassMock([UIEvent class]); -// [forwardGectureRecognizer touchesEnded:touches3 withEvent:event3]; -// OCMVerify([mockFlutterViewContoller touchesEnded:touches3 withEvent:event3]); - -// // Now the 2nd touch sequence should not be allowed. -// NSSet* touches4 = [[NSSet alloc] init]; -// id event4 = OCMClassMock([UIEvent class]); -// [forwardGectureRecognizer touchesBegan:touches4 withEvent:event4]; -// OCMReject([mockFlutterViewContoller touchesBegan:touches4 withEvent:event4]); - -// NSSet* touches5 = [[NSSet alloc] init]; -// id event5 = OCMClassMock([UIEvent class]); -// [forwardGectureRecognizer touchesEnded:touches5 withEvent:event5]; -// OCMReject([mockFlutterViewContoller touchesEnded:touches5 withEvent:event5]); -// } - -// { -// // ***** Sequence 2, finishing touch event with touchCancelled ***** // -// flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller); - -// NSSet* touches1 = [[NSSet alloc] init]; -// id event1 = OCMClassMock([UIEvent class]); -// [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1]; -// OCMVerify([mockFlutterViewContoller touchesBegan:touches1 withEvent:event1]); - -// flutterPlatformViewsController->SetFlutterViewController(nil); - -// // Allow the touch events to finish -// NSSet* touches2 = [[NSSet alloc] init]; -// id event2 = OCMClassMock([UIEvent class]); -// [forwardGectureRecognizer touchesMoved:touches2 withEvent:event2]; -// OCMVerify([mockFlutterViewContoller touchesMoved:touches2 withEvent:event2]); - -// NSSet* touches3 = [[NSSet alloc] init]; -// id event3 = OCMClassMock([UIEvent class]); -// [forwardGectureRecognizer touchesCancelled:touches3 withEvent:event3]; -// OCMVerify([mockFlutterViewContoller forceTouchesCancelled:touches3]); - -// // Now the 2nd touch sequence should not be allowed. -// NSSet* touches4 = [[NSSet alloc] init]; -// id event4 = OCMClassMock([UIEvent class]); -// [forwardGectureRecognizer touchesBegan:touches4 withEvent:event4]; -// OCMReject([mockFlutterViewContoller touchesBegan:touches4 withEvent:event4]); - -// NSSet* touches5 = [[NSSet alloc] init]; -// id event5 = OCMClassMock([UIEvent class]); -// [forwardGectureRecognizer touchesEnded:touches5 withEvent:event5]; -// OCMReject([mockFlutterViewContoller touchesEnded:touches5 withEvent:event5]); -// } - -// flutterPlatformViewsController->Reset(); -// } - -// - (void) -// testSetFlutterViewControllerInTheMiddleOfTouchEventAllowsTheNewControllerToHandleSecondTouchSequence { -// 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(); -// auto platform_view = std::make_unique( -// /*delegate=*/mock_delegate, -// /*rendering_api=*/mock_delegate.settings_.enable_impeller -// ? flutter::IOSRenderingAPI::kMetal -// : flutter::IOSRenderingAPI::kSoftware, -// /*platform_views_controller=*/flutterPlatformViewsController, -// /*task_runners=*/runners, -// /*worker_task_runner=*/nil, -// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - -// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = -// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; -// flutterPlatformViewsController->RegisterViewFactory( -// factory, @"MockFlutterPlatformView", -// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); -// FlutterResult result = ^(id result) { -// }; -// flutterPlatformViewsController->OnMethodCall( -// [FlutterMethodCall -// methodCallWithMethodName:@"create" -// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], -// result); - -// XCTAssertNotNil(gMockPlatformView); - -// // Find touch inteceptor view -// UIView* touchInteceptorView = gMockPlatformView; -// while (touchInteceptorView != nil && -// ![touchInteceptorView isKindOfClass:[FlutterTouchInterceptingView class]]) { -// touchInteceptorView = touchInteceptorView.superview; -// } -// XCTAssertNotNil(touchInteceptorView); - -// // Find ForwardGestureRecognizer -// UIGestureRecognizer* forwardGectureRecognizer = nil; -// for (UIGestureRecognizer* gestureRecognizer in touchInteceptorView.gestureRecognizers) { -// if ([gestureRecognizer isKindOfClass:NSClassFromString(@"ForwardingGestureRecognizer")]) { -// forwardGectureRecognizer = gestureRecognizer; -// break; -// } -// } -// id mockFlutterViewContoller = OCMClassMock([FlutterViewController class]); - -// flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller); - -// // The touches in this sequence requires 1 touch object, we always create the NSSet with one item. -// NSSet* touches1 = [NSSet setWithObject:@1]; -// id event1 = OCMClassMock([UIEvent class]); -// [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1]; -// OCMVerify([mockFlutterViewContoller touchesBegan:touches1 withEvent:event1]); - -// FlutterViewController* mockFlutterViewContoller2 = OCMClassMock([FlutterViewController class]); -// flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller2); - -// // Touch events should still send to the old FlutterViewController if FlutterViewController -// // is updated in between. -// NSSet* touches2 = [NSSet setWithObject:@1]; -// id event2 = OCMClassMock([UIEvent class]); -// [forwardGectureRecognizer touchesBegan:touches2 withEvent:event2]; -// OCMVerify([mockFlutterViewContoller touchesBegan:touches2 withEvent:event2]); -// OCMReject([mockFlutterViewContoller2 touchesBegan:touches2 withEvent:event2]); - -// NSSet* touches3 = [NSSet setWithObject:@1]; -// id event3 = OCMClassMock([UIEvent class]); -// [forwardGectureRecognizer touchesMoved:touches3 withEvent:event3]; -// OCMVerify([mockFlutterViewContoller touchesMoved:touches3 withEvent:event3]); -// OCMReject([mockFlutterViewContoller2 touchesMoved:touches3 withEvent:event3]); - -// NSSet* touches4 = [NSSet setWithObject:@1]; -// id event4 = OCMClassMock([UIEvent class]); -// [forwardGectureRecognizer touchesEnded:touches4 withEvent:event4]; -// OCMVerify([mockFlutterViewContoller touchesEnded:touches4 withEvent:event4]); -// OCMReject([mockFlutterViewContoller2 touchesEnded:touches4 withEvent:event4]); - -// NSSet* touches5 = [NSSet setWithObject:@1]; -// id event5 = OCMClassMock([UIEvent class]); -// [forwardGectureRecognizer touchesEnded:touches5 withEvent:event5]; -// OCMVerify([mockFlutterViewContoller touchesEnded:touches5 withEvent:event5]); -// OCMReject([mockFlutterViewContoller2 touchesEnded:touches5 withEvent:event5]); - -// // Now the 2nd touch sequence should go to the new FlutterViewController - -// NSSet* touches6 = [NSSet setWithObject:@1]; -// id event6 = OCMClassMock([UIEvent class]); -// [forwardGectureRecognizer touchesBegan:touches6 withEvent:event6]; -// OCMVerify([mockFlutterViewContoller2 touchesBegan:touches6 withEvent:event6]); -// OCMReject([mockFlutterViewContoller touchesBegan:touches6 withEvent:event6]); - -// // Allow the touch events to finish -// NSSet* touches7 = [NSSet setWithObject:@1]; -// id event7 = OCMClassMock([UIEvent class]); -// [forwardGectureRecognizer touchesMoved:touches7 withEvent:event7]; -// OCMVerify([mockFlutterViewContoller2 touchesMoved:touches7 withEvent:event7]); -// OCMReject([mockFlutterViewContoller touchesMoved:touches7 withEvent:event7]); - -// NSSet* touches8 = [NSSet setWithObject:@1]; -// id event8 = OCMClassMock([UIEvent class]); -// [forwardGectureRecognizer touchesEnded:touches8 withEvent:event8]; -// OCMVerify([mockFlutterViewContoller2 touchesEnded:touches8 withEvent:event8]); -// OCMReject([mockFlutterViewContoller touchesEnded:touches8 withEvent:event8]); - -// flutterPlatformViewsController->Reset(); -// } - -// - (void)testFlutterPlatformViewTouchesCancelledEventAreForcedToBeCancelled { -// 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(); -// auto platform_view = std::make_unique( -// /*delegate=*/mock_delegate, -// /*rendering_api=*/mock_delegate.settings_.enable_impeller -// ? flutter::IOSRenderingAPI::kMetal -// : flutter::IOSRenderingAPI::kSoftware, -// /*platform_views_controller=*/flutterPlatformViewsController, -// /*task_runners=*/runners, -// /*worker_task_runner=*/nil, -// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - -// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = -// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; -// flutterPlatformViewsController->RegisterViewFactory( -// factory, @"MockFlutterPlatformView", -// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); -// FlutterResult result = ^(id result) { -// }; -// flutterPlatformViewsController->OnMethodCall( -// [FlutterMethodCall -// methodCallWithMethodName:@"create" -// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], -// result); - -// XCTAssertNotNil(gMockPlatformView); - -// // Find touch inteceptor view -// UIView* touchInteceptorView = gMockPlatformView; -// while (touchInteceptorView != nil && -// ![touchInteceptorView isKindOfClass:[FlutterTouchInterceptingView class]]) { -// touchInteceptorView = touchInteceptorView.superview; -// } -// XCTAssertNotNil(touchInteceptorView); - -// // Find ForwardGestureRecognizer -// UIGestureRecognizer* forwardGectureRecognizer = nil; -// for (UIGestureRecognizer* gestureRecognizer in touchInteceptorView.gestureRecognizers) { -// if ([gestureRecognizer isKindOfClass:NSClassFromString(@"ForwardingGestureRecognizer")]) { -// forwardGectureRecognizer = gestureRecognizer; -// break; -// } -// } -// id mockFlutterViewContoller = OCMClassMock([FlutterViewController class]); - -// flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller); - -// NSSet* touches1 = [NSSet setWithObject:@1]; -// id event1 = OCMClassMock([UIEvent class]); -// [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1]; - -// [forwardGectureRecognizer touchesCancelled:touches1 withEvent:event1]; -// OCMVerify([mockFlutterViewContoller forceTouchesCancelled:touches1]); - -// flutterPlatformViewsController->Reset(); -// } - -// - (void)testFlutterPlatformViewControllerSubmitFrameWithoutFlutterViewNotCrashing { -// 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(); -// auto platform_view = std::make_unique( -// /*delegate=*/mock_delegate, -// /*rendering_api=*/mock_delegate.settings_.enable_impeller -// ? flutter::IOSRenderingAPI::kMetal -// : flutter::IOSRenderingAPI::kSoftware, -// /*platform_views_controller=*/flutterPlatformViewsController, -// /*task_runners=*/runners, -// /*worker_task_runner=*/nil, -// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - -// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = -// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; -// flutterPlatformViewsController->RegisterViewFactory( -// factory, @"MockFlutterPlatformView", -// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); -// FlutterResult result = ^(id result) { -// }; -// flutterPlatformViewsController->OnMethodCall( -// [FlutterMethodCall -// methodCallWithMethodName:@"create" -// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], -// result); - -// XCTAssertNotNil(gMockPlatformView); - -// // Create embedded view params -// flutter::MutatorsStack stack; -// SkMatrix finalMatrix; - -// auto embeddedViewParams_1 = -// std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); - -// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams_1)); -// flutterPlatformViewsController->CompositeEmbeddedView(2); -// flutter::SurfaceFrame::FramebufferInfo framebuffer_info; -// auto mock_surface = std::make_unique( -// nullptr, framebuffer_info, -// [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return false; }, -// /*frame_size=*/SkISize::Make(800, 600)); -// XCTAssertFalse( -// flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface))); - -// auto embeddedViewParams_2 = -// std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); -// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams_2)); -// flutterPlatformViewsController->CompositeEmbeddedView(2); -// auto mock_surface_submit_true = std::make_unique( -// nullptr, 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_submit_true))); -// } - -// - (void) -// testFlutterPlatformViewControllerResetDeallocsPlatformViewWhenRootViewsNotBindedToFlutterView { -// 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(); -// auto platform_view = std::make_unique( -// /*delegate=*/mock_delegate, -// /*rendering_api=*/mock_delegate.settings_.enable_impeller -// ? flutter::IOSRenderingAPI::kMetal -// : flutter::IOSRenderingAPI::kSoftware, -// /*platform_views_controller=*/flutterPlatformViewsController, -// /*task_runners=*/runners, -// /*worker_task_runner=*/nil, -// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - -// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; -// flutterPlatformViewsController->SetFlutterView(mockFlutterView); - -// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = -// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; -// flutterPlatformViewsController->RegisterViewFactory( -// factory, @"MockFlutterPlatformView", -// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); -// FlutterResult result = ^(id result) { -// }; -// // autorelease pool to trigger an autorelease for all the root_views_ and touch_interceptors_. -// @autoreleasepool { -// flutterPlatformViewsController->OnMethodCall( -// [FlutterMethodCall -// methodCallWithMethodName:@"create" -// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], -// result); - -// flutter::MutatorsStack stack; -// SkMatrix finalMatrix; -// auto embeddedViewParams = -// std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); -// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); -// flutterPlatformViewsController->CompositeEmbeddedView(2); -// // Not calling |flutterPlatformViewsController::SubmitFrame| so that the platform views are not -// // added to flutter_view_. - -// XCTAssertNotNil(gMockPlatformView); -// flutterPlatformViewsController->Reset(); -// } -// XCTAssertNil(gMockPlatformView); -// } - -// - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { -// 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(); -// auto platform_view = std::make_unique( -// /*delegate=*/mock_delegate, -// /*rendering_api=*/mock_delegate.settings_.enable_impeller -// ? flutter::IOSRenderingAPI::kMetal -// : flutter::IOSRenderingAPI::kSoftware, -// /*platform_views_controller=*/flutterPlatformViewsController, -// /*task_runners=*/runners, -// /*worker_task_runner=*/nil, -// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - -// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; -// flutterPlatformViewsController->SetFlutterView(mockFlutterView); - -// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = -// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; -// flutterPlatformViewsController->RegisterViewFactory( -// factory, @"MockFlutterPlatformView", -// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); -// FlutterResult result = ^(id result) { -// }; - -// flutterPlatformViewsController->OnMethodCall( -// [FlutterMethodCall -// methodCallWithMethodName:@"create" -// arguments:@{@"id" : @0, @"viewType" : @"MockFlutterPlatformView"}], -// result); - -// // First frame, |EmbeddedViewCount| is not empty after composite. -// flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300)); -// flutter::MutatorsStack stack; -// SkMatrix finalMatrix; -// auto embeddedViewParams1 = -// std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); -// flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1)); -// flutterPlatformViewsController->CompositeEmbeddedView(0); -// XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 1UL); - -// // Second frame, |EmbeddedViewCount| should be empty at the start -// flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300)); -// XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 0UL); - -// auto embeddedViewParams2 = -// std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); -// flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams2)); -// flutterPlatformViewsController->CompositeEmbeddedView(0); -// XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 1UL); -// } - -// - (void) -// testFlutterPlatformViewControllerSubmitFrameShouldOrderSubviewsCorrectlyWithDifferentViewHierarchy { -// 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(); -// auto platform_view = std::make_unique( -// /*delegate=*/mock_delegate, -// /*rendering_api=*/mock_delegate.settings_.enable_impeller -// ? flutter::IOSRenderingAPI::kMetal -// : flutter::IOSRenderingAPI::kSoftware, -// /*platform_views_controller=*/flutterPlatformViewsController, -// /*task_runners=*/runners, -// /*worker_task_runner=*/nil, -// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - -// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; -// flutterPlatformViewsController->SetFlutterView(mockFlutterView); - -// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = -// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; -// 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(finalMatrix, SkSize::Make(300, 300), stack); -// flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1)); -// flutterPlatformViewsController->CompositeEmbeddedView(0); -// auto embeddedViewParams2 = -// std::make_unique(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 mock_sk_surface = SkSurfaces::Raster(image_info); -// flutter::SurfaceFrame::FramebufferInfo framebuffer_info; -// auto mock_surface = std::make_unique( -// 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 opposite order. -// embeddedViewParams2 = -// std::make_unique(finalMatrix, SkSize::Make(500, 500), stack); -// flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams2)); -// flutterPlatformViewsController->CompositeEmbeddedView(1); -// embeddedViewParams1 = -// std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); -// flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1)); -// flutterPlatformViewsController->CompositeEmbeddedView(0); - -// mock_sk_surface = SkSurfaces::Raster(image_info); -// mock_surface = std::make_unique( -// 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 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(); -// auto platform_view = std::make_unique( -// /*delegate=*/mock_delegate, -// /*rendering_api=*/mock_delegate.settings_.enable_impeller -// ? flutter::IOSRenderingAPI::kMetal -// : flutter::IOSRenderingAPI::kSoftware, -// /*platform_views_controller=*/flutterPlatformViewsController, -// /*task_runners=*/runners, -// /*worker_task_runner=*/nil, -// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - -// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; -// flutterPlatformViewsController->SetFlutterView(mockFlutterView); - -// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = -// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; -// 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(finalMatrix, SkSize::Make(300, 300), stack); -// flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1)); -// flutterPlatformViewsController->CompositeEmbeddedView(0); -// auto embeddedViewParams2 = -// std::make_unique(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 mock_sk_surface = SkSurfaces::Raster(image_info); -// flutter::SurfaceFrame::FramebufferInfo framebuffer_info; -// auto mock_surface = std::make_unique( -// 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(finalMatrix, SkSize::Make(300, 300), stack); -// flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1)); -// flutterPlatformViewsController->CompositeEmbeddedView(0); -// embeddedViewParams2 = -// std::make_unique(finalMatrix, SkSize::Make(500, 500), stack); -// flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams2)); -// flutterPlatformViewsController->CompositeEmbeddedView(1); - -// mock_sk_surface = SkSurfaces::Raster(image_info); -// mock_surface = std::make_unique( -// 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"); -// auto thread_task_runner_other = CreateNewThread("FlutterPlatformViewsTest2"); -// flutter::TaskRunners runners(/*label=*/self.name.UTF8String, -// /*platform=*/thread_task_runner_platform, -// /*raster=*/thread_task_runner_other, -// /*ui=*/thread_task_runner_other, -// /*io=*/thread_task_runner_other); -// auto flutterPlatformViewsController = std::make_shared(); -// auto platform_view = std::make_unique( -// /*delegate=*/mock_delegate, -// /*rendering_api=*/mock_delegate.settings_.enable_impeller -// ? flutter::IOSRenderingAPI::kMetal -// : flutter::IOSRenderingAPI::kSoftware, -// /*platform_views_controller=*/flutterPlatformViewsController, -// /*task_runners=*/runners, -// /*worker_task_runner=*/nil, -// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - -// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; -// flutterPlatformViewsController->SetFlutterView(mockFlutterView); - -// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = -// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; -// flutterPlatformViewsController->RegisterViewFactory( -// factory, @"MockFlutterPlatformView", -// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); -// XCTestExpectation* waitForPlatformView = -// [self expectationWithDescription:@"wait for platform view to be created"]; -// FlutterResult result = ^(id result) { -// [waitForPlatformView fulfill]; -// }; - -// flutterPlatformViewsController->OnMethodCall( -// [FlutterMethodCall -// methodCallWithMethodName:@"create" -// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], -// result); -// [self waitForExpectations:@[ waitForPlatformView ] timeout:30]; -// XCTAssertNotNil(gMockPlatformView); - -// flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300)); -// SkMatrix finalMatrix; -// flutter::MutatorsStack stack; -// auto embeddedViewParams = -// std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); -// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - -// fml::RefPtr raster_thread_merger = -// fml::MakeRefCounted(thread_task_runner_platform->GetTaskQueueId(), -// thread_task_runner_other->GetTaskQueueId()); -// XCTAssertEqual(flutterPlatformViewsController->PostPrerollAction(raster_thread_merger), -// flutter::PostPrerollResult::kSkipAndRetryFrame); -// XCTAssertFalse(raster_thread_merger->IsMerged()); - -// flutterPlatformViewsController->EndFrame(true, raster_thread_merger); -// XCTAssertTrue(raster_thread_merger->IsMerged()); - -// // Unmerge threads before the end of the test -// // TaskRunners are required to be unmerged before destruction. -// while (raster_thread_merger->DecrementLease() != fml::RasterThreadStatus::kUnmergedNow) { -// } -// } - -// - (int)alphaOfPoint:(CGPoint)point onView:(UIView*)view { -// unsigned char pixel[4] = {0}; - -// CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); - -// // Draw the pixel on `point` in the context. -// CGContextRef context = CGBitmapContextCreate( -// pixel, 1, 1, 8, 4, colorSpace, kCGBitmapAlphaInfoMask & kCGImageAlphaPremultipliedLast); -// CGContextTranslateCTM(context, -point.x, -point.y); -// [view.layer renderInContext:context]; - -// CGContextRelease(context); -// CGColorSpaceRelease(colorSpace); -// // Get the alpha from the pixel that we just rendered. -// return pixel[3]; -// } - -// - (void)testHasFirstResponderInViewHierarchySubtree_viewItselfBecomesFirstResponder { -// // For view to become the first responder, it must be a descendant of a UIWindow -// UIWindow* window = [[UIWindow alloc] init]; -// UITextField* textField = [[UITextField alloc] init]; -// [window addSubview:textField]; - -// [textField becomeFirstResponder]; -// XCTAssertTrue(textField.isFirstResponder); -// XCTAssertTrue(textField.flt_hasFirstResponderInViewHierarchySubtree); -// [textField resignFirstResponder]; -// XCTAssertFalse(textField.isFirstResponder); -// XCTAssertFalse(textField.flt_hasFirstResponderInViewHierarchySubtree); -// } - -// - (void)testHasFirstResponderInViewHierarchySubtree_descendantViewBecomesFirstResponder { -// // For view to become the first responder, it must be a descendant of a UIWindow -// UIWindow* window = [[UIWindow alloc] init]; -// UIView* view = [[UIView alloc] init]; -// UIView* childView = [[UIView alloc] init]; -// UITextField* textField = [[UITextField alloc] init]; -// [window addSubview:view]; -// [view addSubview:childView]; -// [childView addSubview:textField]; - -// [textField becomeFirstResponder]; -// XCTAssertTrue(textField.isFirstResponder); -// XCTAssertTrue(view.flt_hasFirstResponderInViewHierarchySubtree); -// [textField resignFirstResponder]; -// XCTAssertFalse(textField.isFirstResponder); -// XCTAssertFalse(view.flt_hasFirstResponderInViewHierarchySubtree); -// } - -// - (void)testFlutterClippingMaskViewPoolReuseViewsAfterRecycle { -// FlutterClippingMaskViewPool* pool = [[FlutterClippingMaskViewPool alloc] initWithCapacity:2]; -// FlutterClippingMaskView* view1 = [pool getMaskViewWithFrame:CGRectZero]; -// FlutterClippingMaskView* view2 = [pool getMaskViewWithFrame:CGRectZero]; -// [pool insertViewToPoolIfNeeded:view1]; -// [pool insertViewToPoolIfNeeded:view2]; -// CGRect newRect = CGRectMake(0, 0, 10, 10); -// FlutterClippingMaskView* view3 = [pool getMaskViewWithFrame:newRect]; -// FlutterClippingMaskView* view4 = [pool getMaskViewWithFrame:newRect]; -// // view3 and view4 should randomly get either of view1 and view2. -// NSSet* set1 = [NSSet setWithObjects:view1, view2, nil]; -// NSSet* set2 = [NSSet setWithObjects:view3, view4, nil]; -// XCTAssertEqualObjects(set1, set2); -// XCTAssertTrue(CGRectEqualToRect(view3.frame, newRect)); -// XCTAssertTrue(CGRectEqualToRect(view4.frame, newRect)); -// } - -// - (void)testFlutterClippingMaskViewPoolAllocsNewMaskViewsAfterReachingCapacity { -// FlutterClippingMaskViewPool* pool = [[FlutterClippingMaskViewPool alloc] initWithCapacity:2]; -// FlutterClippingMaskView* view1 = [pool getMaskViewWithFrame:CGRectZero]; -// FlutterClippingMaskView* view2 = [pool getMaskViewWithFrame:CGRectZero]; -// FlutterClippingMaskView* view3 = [pool getMaskViewWithFrame:CGRectZero]; -// XCTAssertNotEqual(view1, view3); -// XCTAssertNotEqual(view2, view3); -// } - -// - (void)testMaskViewsReleasedWhenPoolIsReleased { -// __weak UIView* weakView; -// @autoreleasepool { -// FlutterClippingMaskViewPool* pool = [[FlutterClippingMaskViewPool alloc] initWithCapacity:2]; -// FlutterClippingMaskView* view = [pool getMaskViewWithFrame:CGRectZero]; -// weakView = view; -// XCTAssertNotNil(weakView); -// } -// XCTAssertNil(weakView); -// } - -// - (void)testClipMaskViewIsReused { -// 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(); -// auto platform_view = std::make_unique( -// /*delegate=*/mock_delegate, -// /*rendering_api=*/mock_delegate.settings_.enable_impeller -// ? flutter::IOSRenderingAPI::kMetal -// : flutter::IOSRenderingAPI::kSoftware, -// /*platform_views_controller=*/flutterPlatformViewsController, -// /*task_runners=*/runners, -// /*worker_task_runner=*/nil, -// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - -// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = -// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; -// flutterPlatformViewsController->RegisterViewFactory( -// factory, @"MockFlutterPlatformView", -// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); -// FlutterResult result = ^(id result) { -// }; -// flutterPlatformViewsController->OnMethodCall( -// [FlutterMethodCall -// methodCallWithMethodName:@"create" -// arguments:@{@"id" : @1, @"viewType" : @"MockFlutterPlatformView"}], -// result); - -// XCTAssertNotNil(gMockPlatformView); -// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; -// flutterPlatformViewsController->SetFlutterView(mockFlutterView); -// // Create embedded view params -// flutter::MutatorsStack stack1; -// // Layer tree always pushes a screen scale factor to the stack -// SkMatrix screenScaleMatrix = -// SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); -// stack1.PushTransform(screenScaleMatrix); -// // Push a clip rect -// SkRect rect = SkRect::MakeXYWH(2, 2, 3, 3); -// stack1.PushClipRect(rect); - -// auto embeddedViewParams1 = std::make_unique( -// screenScaleMatrix, SkSize::Make(10, 10), stack1); - -// flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1)); -// flutterPlatformViewsController->CompositeEmbeddedView(1); -// UIView* childClippingView1 = gMockPlatformView.superview.superview; -// UIView* maskView1 = childClippingView1.maskView; -// XCTAssertNotNil(maskView1); - -// // Composite a new frame. -// flutterPlatformViewsController->BeginFrame(SkISize::Make(100, 100)); -// flutter::MutatorsStack stack2; -// auto embeddedViewParams2 = std::make_unique( -// screenScaleMatrix, SkSize::Make(10, 10), stack2); -// auto embeddedViewParams3 = std::make_unique( -// screenScaleMatrix, SkSize::Make(10, 10), stack2); -// flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams3)); -// flutterPlatformViewsController->CompositeEmbeddedView(1); -// childClippingView1 = gMockPlatformView.superview.superview; - -// // This overrides gMockPlatformView to point to the newly created platform view. -// flutterPlatformViewsController->OnMethodCall( -// [FlutterMethodCall -// methodCallWithMethodName:@"create" -// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], -// result); - -// auto embeddedViewParams4 = std::make_unique( -// screenScaleMatrix, SkSize::Make(10, 10), stack1); -// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams4)); -// flutterPlatformViewsController->CompositeEmbeddedView(2); -// UIView* childClippingView2 = gMockPlatformView.superview.superview; - -// UIView* maskView2 = childClippingView2.maskView; -// XCTAssertEqual(maskView1, maskView2); -// XCTAssertNotNil(childClippingView2.maskView); -// XCTAssertNil(childClippingView1.maskView); -// } - -// - (void)testDifferentClipMaskViewIsUsedForEachView { -// 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(); -// auto platform_view = std::make_unique( -// /*delegate=*/mock_delegate, -// /*rendering_api=*/mock_delegate.settings_.enable_impeller -// ? flutter::IOSRenderingAPI::kMetal -// : flutter::IOSRenderingAPI::kSoftware, -// /*platform_views_controller=*/flutterPlatformViewsController, -// /*task_runners=*/runners, -// /*worker_task_runner=*/nil, -// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - -// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = -// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; -// flutterPlatformViewsController->RegisterViewFactory( -// factory, @"MockFlutterPlatformView", -// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); -// FlutterResult result = ^(id result) { -// }; - -// flutterPlatformViewsController->OnMethodCall( -// [FlutterMethodCall -// methodCallWithMethodName:@"create" -// arguments:@{@"id" : @1, @"viewType" : @"MockFlutterPlatformView"}], -// result); -// UIView* view1 = gMockPlatformView; - -// // This overwrites `gMockPlatformView` to another view. -// flutterPlatformViewsController->OnMethodCall( -// [FlutterMethodCall -// methodCallWithMethodName:@"create" -// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], -// result); -// UIView* view2 = gMockPlatformView; - -// XCTAssertNotNil(gMockPlatformView); -// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; -// flutterPlatformViewsController->SetFlutterView(mockFlutterView); -// // Create embedded view params -// flutter::MutatorsStack stack1; -// // Layer tree always pushes a screen scale factor to the stack -// SkMatrix screenScaleMatrix = -// SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); -// stack1.PushTransform(screenScaleMatrix); -// // Push a clip rect -// SkRect rect = SkRect::MakeXYWH(2, 2, 3, 3); -// stack1.PushClipRect(rect); - -// auto embeddedViewParams1 = std::make_unique( -// screenScaleMatrix, SkSize::Make(10, 10), stack1); - -// flutter::MutatorsStack stack2; -// stack2.PushClipRect(rect); -// auto embeddedViewParams2 = std::make_unique( -// screenScaleMatrix, SkSize::Make(10, 10), stack2); - -// flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1)); -// flutterPlatformViewsController->CompositeEmbeddedView(1); -// UIView* childClippingView1 = view1.superview.superview; - -// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams2)); -// flutterPlatformViewsController->CompositeEmbeddedView(2); -// UIView* childClippingView2 = view2.superview.superview; -// UIView* maskView1 = childClippingView1.maskView; -// UIView* maskView2 = childClippingView2.maskView; -// XCTAssertNotEqual(maskView1, maskView2); -// } - -// - (void)testMaskViewUsesCAShapeLayerAsTheBackingLayer { -// 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(); -// auto platform_view = std::make_unique( -// /*delegate=*/mock_delegate, -// /*rendering_api=*/mock_delegate.settings_.enable_impeller -// ? flutter::IOSRenderingAPI::kMetal -// : flutter::IOSRenderingAPI::kSoftware, -// /*platform_views_controller=*/flutterPlatformViewsController, -// /*task_runners=*/runners, -// /*worker_task_runner=*/nil, -// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - -// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = -// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; -// flutterPlatformViewsController->RegisterViewFactory( -// factory, @"MockFlutterPlatformView", -// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); -// FlutterResult result = ^(id result) { -// }; - -// flutterPlatformViewsController->OnMethodCall( -// [FlutterMethodCall -// methodCallWithMethodName:@"create" -// arguments:@{@"id" : @1, @"viewType" : @"MockFlutterPlatformView"}], -// result); - -// XCTAssertNotNil(gMockPlatformView); -// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; -// flutterPlatformViewsController->SetFlutterView(mockFlutterView); -// // Create embedded view params -// flutter::MutatorsStack stack1; -// // Layer tree always pushes a screen scale factor to the stack -// SkMatrix screenScaleMatrix = -// SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); -// stack1.PushTransform(screenScaleMatrix); -// // Push a clip rect -// SkRect rect = SkRect::MakeXYWH(2, 2, 3, 3); -// stack1.PushClipRect(rect); - -// auto embeddedViewParams1 = std::make_unique( -// screenScaleMatrix, SkSize::Make(10, 10), stack1); - -// flutter::MutatorsStack stack2; -// stack2.PushClipRect(rect); -// auto embeddedViewParams2 = std::make_unique( -// screenScaleMatrix, SkSize::Make(10, 10), stack2); - -// flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1)); -// flutterPlatformViewsController->CompositeEmbeddedView(1); -// UIView* childClippingView = gMockPlatformView.superview.superview; - -// UIView* maskView = childClippingView.maskView; -// XCTAssert([maskView.layer isKindOfClass:[CAShapeLayer class]], -// @"Mask view must use CAShapeLayer as its backing layer."); -// } - -// // Return true if a correct visual effect view is found. It also implies all the validation in this -// // method passes. -// // -// // There are two fail states for this method. 1. One of the XCTAssert method failed; or 2. No -// // correct visual effect view found. -// - (BOOL)validateOneVisualEffectView:(UIView*)visualEffectView -// expectedFrame:(CGRect)frame -// inputRadius:(CGFloat)inputRadius { -// XCTAssertTrue(CGRectEqualToRect(visualEffectView.frame, frame)); -// for (UIView* view in visualEffectView.subviews) { -// if (![NSStringFromClass([view class]) hasSuffix:@"BackdropView"]) { -// continue; -// } -// XCTAssertEqual(view.layer.filters.count, 1u); -// NSObject* filter = view.layer.filters.firstObject; - -// XCTAssertEqualObjects([filter valueForKey:@"name"], @"gaussianBlur"); - -// NSObject* inputRadiusInFilter = [filter valueForKey:@"inputRadius"]; -// XCTAssertTrue([inputRadiusInFilter isKindOfClass:[NSNumber class]] && -// flutter::BlurRadiusEqualToBlurRadius(((NSNumber*)inputRadiusInFilter).floatValue, -// inputRadius)); -// return YES; -// } -// return NO; -// } - -// - (void)testDisposingViewInCompositionOrderDoNotCrash { -// 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(); -// auto platform_view = std::make_unique( -// /*delegate=*/mock_delegate, -// /*rendering_api=*/mock_delegate.settings_.enable_impeller -// ? flutter::IOSRenderingAPI::kMetal -// : flutter::IOSRenderingAPI::kSoftware, -// /*platform_views_controller=*/flutterPlatformViewsController, -// /*task_runners=*/runners, -// /*worker_task_runner=*/nil, -// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - -// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; -// flutterPlatformViewsController->SetFlutterView(mockFlutterView); - -// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = -// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; -// flutterPlatformViewsController->RegisterViewFactory( -// factory, @"MockFlutterPlatformView", -// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); -// FlutterResult result = ^(id result) { -// }; - -// flutterPlatformViewsController->OnMethodCall( -// [FlutterMethodCall -// methodCallWithMethodName:@"create" -// arguments:@{@"id" : @0, @"viewType" : @"MockFlutterPlatformView"}], -// result); -// flutterPlatformViewsController->OnMethodCall( -// [FlutterMethodCall -// methodCallWithMethodName:@"create" -// arguments:@{@"id" : @1, @"viewType" : @"MockFlutterPlatformView"}], -// result); - -// { -// // **** First frame, view id 0, 1 in the composition_order_, disposing view 0 is called. **** // -// // No view should be disposed, or removed from the composition order. -// flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300)); -// flutter::MutatorsStack stack; -// SkMatrix finalMatrix; -// auto embeddedViewParams0 = -// std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); -// flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams0)); -// flutterPlatformViewsController->CompositeEmbeddedView(0); - -// auto embeddedViewParams1 = -// std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); -// flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1)); -// flutterPlatformViewsController->CompositeEmbeddedView(1); -// XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 2UL); - -// XCTestExpectation* expectation = [self expectationWithDescription:@"dispose call ended."]; -// FlutterResult disposeResult = ^(id result) { -// [expectation fulfill]; -// }; - -// flutterPlatformViewsController->OnMethodCall( -// [FlutterMethodCall methodCallWithMethodName:@"dispose" arguments:@0], disposeResult); -// [self waitForExpectationsWithTimeout:30 handler:nil]; - -// const SkImageInfo image_info = SkImageInfo::MakeN32Premul(1000, 1000); -// sk_sp mock_sk_surface = SkSurfaces::Raster(image_info); -// flutter::SurfaceFrame::FramebufferInfo framebuffer_info; -// auto mock_surface = std::make_unique( -// 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))); - -// // Disposing won't remove embedded views until the view is removed from the composition_order_ -// XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 2UL); -// XCTAssertNotNil(flutterPlatformViewsController->GetPlatformViewByID(0)); -// XCTAssertNotNil(flutterPlatformViewsController->GetPlatformViewByID(1)); -// } - -// { -// // **** Second frame, view id 1 in the composition_order_, no disposing view is called, **** // -// // View 0 is removed from the composition order in this frame, hence also disposed. -// flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300)); -// flutter::MutatorsStack stack; -// SkMatrix finalMatrix; -// auto embeddedViewParams1 = -// std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); -// flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1)); -// flutterPlatformViewsController->CompositeEmbeddedView(1); - -// const SkImageInfo image_info = SkImageInfo::MakeN32Premul(1000, 1000); -// sk_sp mock_sk_surface = SkSurfaces::Raster(image_info); -// flutter::SurfaceFrame::FramebufferInfo framebuffer_info; -// auto mock_surface = std::make_unique( -// 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))); - -// // Disposing won't remove embedded views until the view is removed from the composition_order_ -// XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 1UL); -// XCTAssertNil(flutterPlatformViewsController->GetPlatformViewByID(0)); -// XCTAssertNotNil(flutterPlatformViewsController->GetPlatformViewByID(1)); -// } -// } -// - (void)testOnlyPlatformViewsAreRemovedWhenReset { -// 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(); -// auto platform_view = std::make_unique( -// /*delegate=*/mock_delegate, -// /*rendering_api=*/mock_delegate.settings_.enable_impeller -// ? flutter::IOSRenderingAPI::kMetal -// : flutter::IOSRenderingAPI::kSoftware, -// /*platform_views_controller=*/flutterPlatformViewsController, -// /*task_runners=*/runners, -// /*worker_task_runner=*/nil, -// /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - -// FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = -// [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; -// flutterPlatformViewsController->RegisterViewFactory( -// factory, @"MockFlutterPlatformView", -// FlutterPlatformViewGestureRecognizersBlockingPolicyEager); -// FlutterResult result = ^(id result) { -// }; -// flutterPlatformViewsController->OnMethodCall( -// [FlutterMethodCall -// methodCallWithMethodName:@"create" -// arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], -// result); -// UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; -// flutterPlatformViewsController->SetFlutterView(mockFlutterView); -// // Create embedded view params -// flutter::MutatorsStack stack; -// // Layer tree always pushes a screen scale factor to the stack -// SkMatrix screenScaleMatrix = -// SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); -// stack.PushTransform(screenScaleMatrix); -// // Push a translate matrix -// SkMatrix translateMatrix = SkMatrix::Translate(100, 100); -// stack.PushTransform(translateMatrix); -// SkMatrix finalMatrix; -// finalMatrix.setConcat(screenScaleMatrix, translateMatrix); - -// auto embeddedViewParams = -// std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); - -// flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); -// flutterPlatformViewsController->CompositeEmbeddedView(2); - -// // SKSurface is required if the root FlutterView is present. -// const SkImageInfo image_info = SkImageInfo::MakeN32Premul(1000, 1000); -// sk_sp mock_sk_surface = SkSurfaces::Raster(image_info); -// flutter::SurfaceFrame::FramebufferInfo framebuffer_info; -// auto mock_surface = std::make_unique( -// std::move(mock_sk_surface), framebuffer_info, -// [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; }, -// /*frame_size=*/SkISize::Make(800, 600)); - -// flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface)); - -// UIView* someView = [[UIView alloc] init]; -// [mockFlutterView addSubview:someView]; - -// flutterPlatformViewsController->Reset(); -// XCTAssertEqual(mockFlutterView.subviews.count, 1u); -// XCTAssertEqual(mockFlutterView.subviews.firstObject, someView); -// } - -// - (void)testFlutterTouchInterceptingViewLinksToAccessibilityContainer { -// FlutterTouchInterceptingView* touchInteceptorView = [[FlutterTouchInterceptingView alloc] init]; -// NSObject* container = [[NSObject alloc] init]; -// [touchInteceptorView setFlutterAccessibilityContainer:container]; -// XCTAssertEqualObjects([touchInteceptorView accessibilityContainer], container); -// } - -// @end +// Copyright 2013 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 +#import +#import + +#import "flutter/fml/thread.h" +#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h" +#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlatformViews.h" +#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h" +#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h" +#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterTouchInterceptingView_Test.h" +#import "flutter/shell/platform/darwin/ios/platform_view_ios.h" + +FLUTTER_ASSERT_ARC + +@class FlutterPlatformViewsTestMockPlatformView; +__weak static FlutterPlatformViewsTestMockPlatformView* gMockPlatformView = nil; +const float kFloatCompareEpsilon = 0.001; + +@interface FlutterPlatformViewsTestMockPlatformView : UIView +@end +@implementation FlutterPlatformViewsTestMockPlatformView + +- (instancetype)init { + self = [super init]; + if (self) { + gMockPlatformView = self; + } + return self; +} + +- (void)dealloc { + gMockPlatformView = nil; +} + +@end + +@interface FlutterPlatformViewsTestMockFlutterPlatformView : NSObject +@property(nonatomic, strong) UIView* view; +@property(nonatomic, assign) BOOL viewCreated; +@end + +@implementation FlutterPlatformViewsTestMockFlutterPlatformView + +- (instancetype)init { + if (self = [super init]) { + _view = [[FlutterPlatformViewsTestMockPlatformView alloc] init]; + _viewCreated = NO; + } + return self; +} + +- (UIView*)view { + [self checkViewCreatedOnce]; + return _view; +} + +- (void)checkViewCreatedOnce { + if (self.viewCreated) { + abort(); + } + self.viewCreated = YES; +} + +@end + +@interface FlutterPlatformViewsTestMockFlutterPlatformFactory + : NSObject +@end + +@implementation FlutterPlatformViewsTestMockFlutterPlatformFactory +- (NSObject*)createWithFrame:(CGRect)frame + viewIdentifier:(int64_t)viewId + arguments:(id _Nullable)args { + return [[FlutterPlatformViewsTestMockFlutterPlatformView alloc] init]; +} + +@end + +namespace flutter { +namespace { +class FlutterPlatformViewsTestMockPlatformViewDelegate : public PlatformView::Delegate { + public: + void OnPlatformViewCreated(std::unique_ptr surface) override {} + void OnPlatformViewDestroyed() override {} + void OnPlatformViewScheduleFrame() override {} + void OnPlatformViewAddView(int64_t view_id, + const ViewportMetrics& viewport_metrics, + AddViewCallback callback) override {} + void OnPlatformViewRemoveView(int64_t view_id, RemoveViewCallback callback) override {} + void OnPlatformViewSetNextFrameCallback(const fml::closure& closure) override {} + void OnPlatformViewSetViewportMetrics(int64_t view_id, const ViewportMetrics& metrics) override {} + const flutter::Settings& OnPlatformViewGetSettings() const override { return settings_; } + void OnPlatformViewDispatchPlatformMessage(std::unique_ptr message) override {} + void OnPlatformViewDispatchPointerDataPacket(std::unique_ptr packet) override { + } + void OnPlatformViewDispatchSemanticsAction(int32_t id, + SemanticsAction action, + fml::MallocMapping args) override {} + void OnPlatformViewSetSemanticsEnabled(bool enabled) override {} + void OnPlatformViewSetAccessibilityFeatures(int32_t flags) override {} + void OnPlatformViewRegisterTexture(std::shared_ptr texture) override {} + void OnPlatformViewUnregisterTexture(int64_t texture_id) override {} + void OnPlatformViewMarkTextureFrameAvailable(int64_t texture_id) override {} + + void LoadDartDeferredLibrary(intptr_t loading_unit_id, + std::unique_ptr snapshot_data, + std::unique_ptr snapshot_instructions) override { + } + void LoadDartDeferredLibraryError(intptr_t loading_unit_id, + const std::string error_message, + bool transient) override {} + void UpdateAssetResolverByType(std::unique_ptr updated_asset_resolver, + flutter::AssetResolver::AssetResolverType type) override {} + + flutter::Settings settings_; +}; + +} // namespace +} // namespace flutter + +namespace { +fml::RefPtr CreateNewThread(const std::string& name) { + auto thread = std::make_unique(name); + auto runner = thread->GetTaskRunner(); + return runner; +} +} // namespace + +@interface FlutterPlatformViewsTest : XCTestCase +@end + +@implementation FlutterPlatformViewsTest + +- (void)testFlutterViewOnlyCreateOnceInOneFrame { + 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(thread_task_runner); + auto platform_view = std::make_unique( + /*delegate=*/mock_delegate, + /*rendering_api=*/mock_delegate.settings_.enable_impeller + ? flutter::IOSRenderingAPI::kMetal + : flutter::IOSRenderingAPI::kSoftware, + /*platform_views_controller=*/flutterPlatformViewsController, + /*task_runners=*/runners, + /*worker_task_runner=*/nil, + /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + + FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = + [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; + flutterPlatformViewsController->RegisterViewFactory( + factory, @"MockFlutterPlatformView", + FlutterPlatformViewGestureRecognizersBlockingPolicyEager); + FlutterResult result = ^(id result) { + }; + flutterPlatformViewsController->OnMethodCall( + [FlutterMethodCall + methodCallWithMethodName:@"create" + arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], + result); + UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; + flutterPlatformViewsController->SetFlutterView(mockFlutterView); + // Create embedded view params + flutter::MutatorsStack stack; + // Layer tree always pushes a screen scale factor to the stack + SkMatrix screenScaleMatrix = + SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); + stack.PushTransform(screenScaleMatrix); + // Push a translate matrix + SkMatrix translateMatrix = SkMatrix::Translate(100, 100); + stack.PushTransform(translateMatrix); + SkMatrix finalMatrix; + finalMatrix.setConcat(screenScaleMatrix, translateMatrix); + + auto embeddedViewParams = + std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); + + flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); + flutterPlatformViewsController->CompositeEmbeddedView(2); + + XCTAssertNotNil(gMockPlatformView); + + flutterPlatformViewsController->Reset(); +} + +- (void)testCanCreatePlatformViewWithoutFlutterView { + 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(thread_task_runner); + auto platform_view = std::make_unique( + /*delegate=*/mock_delegate, + /*rendering_api=*/mock_delegate.settings_.enable_impeller + ? flutter::IOSRenderingAPI::kMetal + : flutter::IOSRenderingAPI::kSoftware, + /*platform_views_controller=*/flutterPlatformViewsController, + /*task_runners=*/runners, + /*worker_task_runner=*/nil, + /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + + FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = + [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; + flutterPlatformViewsController->RegisterViewFactory( + factory, @"MockFlutterPlatformView", + FlutterPlatformViewGestureRecognizersBlockingPolicyEager); + FlutterResult result = ^(id result) { + }; + flutterPlatformViewsController->OnMethodCall( + [FlutterMethodCall + methodCallWithMethodName:@"create" + arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], + result); + + XCTAssertNotNil(gMockPlatformView); +} + +- (void)testChildClippingViewHitTests { + ChildClippingView* childClippingView = + [[ChildClippingView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; + UIView* childView = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)]; + [childClippingView addSubview:childView]; + + XCTAssertFalse([childClippingView pointInside:CGPointMake(50, 50) withEvent:nil]); + XCTAssertFalse([childClippingView pointInside:CGPointMake(99, 100) withEvent:nil]); + XCTAssertFalse([childClippingView pointInside:CGPointMake(100, 99) withEvent:nil]); + XCTAssertFalse([childClippingView pointInside:CGPointMake(201, 200) withEvent:nil]); + XCTAssertFalse([childClippingView pointInside:CGPointMake(200, 201) withEvent:nil]); + XCTAssertFalse([childClippingView pointInside:CGPointMake(99, 200) withEvent:nil]); + XCTAssertFalse([childClippingView pointInside:CGPointMake(200, 299) withEvent:nil]); + + XCTAssertTrue([childClippingView pointInside:CGPointMake(150, 150) withEvent:nil]); + XCTAssertTrue([childClippingView pointInside:CGPointMake(100, 100) withEvent:nil]); + XCTAssertTrue([childClippingView pointInside:CGPointMake(199, 100) withEvent:nil]); + XCTAssertTrue([childClippingView pointInside:CGPointMake(100, 199) withEvent:nil]); + XCTAssertTrue([childClippingView pointInside:CGPointMake(199, 199) withEvent:nil]); +} + +- (void)testReleasesBackdropFilterSubviewsOnChildClippingViewDealloc { + __weak NSMutableArray* weakBackdropFilterSubviews = nil; + __weak UIVisualEffectView* weakVisualEffectView1 = nil; + __weak UIVisualEffectView* weakVisualEffectView2 = nil; + + @autoreleasepool { + ChildClippingView* clippingView = [[ChildClippingView alloc] initWithFrame:CGRectZero]; + UIVisualEffectView* visualEffectView1 = [[UIVisualEffectView alloc] + initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]]; + weakVisualEffectView1 = visualEffectView1; + PlatformViewFilter* platformViewFilter1 = + [[PlatformViewFilter alloc] initWithFrame:CGRectMake(0, 0, 10, 10) + blurRadius:5 + visualEffectView:visualEffectView1]; + + [clippingView applyBlurBackdropFilters:@[ platformViewFilter1 ]]; + + // Replace the blur filter to validate the original and new UIVisualEffectView are released. + UIVisualEffectView* visualEffectView2 = [[UIVisualEffectView alloc] + initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleDark]]; + weakVisualEffectView2 = visualEffectView2; + PlatformViewFilter* platformViewFilter2 = + [[PlatformViewFilter alloc] initWithFrame:CGRectMake(0, 0, 10, 10) + blurRadius:5 + visualEffectView:visualEffectView2]; + [clippingView applyBlurBackdropFilters:@[ platformViewFilter2 ]]; + + weakBackdropFilterSubviews = clippingView.backdropFilterSubviews; + XCTAssertNotNil(weakBackdropFilterSubviews); + clippingView = nil; + } + XCTAssertNil(weakBackdropFilterSubviews); + XCTAssertNil(weakVisualEffectView1); + XCTAssertNil(weakVisualEffectView2); +} + +- (void)testApplyBackdropFilter { + 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(thread_task_runner); + auto platform_view = std::make_unique( + /*delegate=*/mock_delegate, + /*rendering_api=*/mock_delegate.settings_.enable_impeller + ? flutter::IOSRenderingAPI::kMetal + : flutter::IOSRenderingAPI::kSoftware, + /*platform_views_controller=*/flutterPlatformViewsController, + /*task_runners=*/runners, + /*worker_task_runner=*/nil, + /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + + FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = + [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; + flutterPlatformViewsController->RegisterViewFactory( + factory, @"MockFlutterPlatformView", + FlutterPlatformViewGestureRecognizersBlockingPolicyEager); + FlutterResult result = ^(id result) { + }; + flutterPlatformViewsController->OnMethodCall( + [FlutterMethodCall + methodCallWithMethodName:@"create" + arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], + result); + + XCTAssertNotNil(gMockPlatformView); + + UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; + flutterPlatformViewsController->SetFlutterView(mockFlutterView); + // Create embedded view params + flutter::MutatorsStack stack; + // Layer tree always pushes a screen scale factor to the stack + CGFloat screenScale = [UIScreen mainScreen].scale; + SkMatrix screenScaleMatrix = SkMatrix::Scale(screenScale, screenScale); + stack.PushTransform(screenScaleMatrix); + // Push a backdrop filter + auto filter = std::make_shared(5, 2, flutter::DlTileMode::kClamp); + stack.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); + + auto embeddedViewParams = + std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); + + flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); + flutterPlatformViewsController->CompositeEmbeddedView(2); + XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]); + ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; + [mockFlutterView addSubview:childClippingView]; + + [mockFlutterView setNeedsLayout]; + [mockFlutterView layoutIfNeeded]; + + // childClippingView has visual effect view with the correct configurations. + NSUInteger numberOfExpectedVisualEffectView = 0; + for (UIView* subview in childClippingView.subviews) { + if (![subview isKindOfClass:[UIVisualEffectView class]]) { + continue; + } + XCTAssertLessThan(numberOfExpectedVisualEffectView, 1u); + if ([self validateOneVisualEffectView:subview + expectedFrame:CGRectMake(0, 0, 10, 10) + inputRadius:5]) { + numberOfExpectedVisualEffectView++; + } + } + XCTAssertEqual(numberOfExpectedVisualEffectView, 1u); +} + +- (void)testApplyBackdropFilterWithCorrectFrame { + 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(thread_task_runner); + auto platform_view = std::make_unique( + /*delegate=*/mock_delegate, + /*rendering_api=*/mock_delegate.settings_.enable_impeller + ? flutter::IOSRenderingAPI::kMetal + : flutter::IOSRenderingAPI::kSoftware, + /*platform_views_controller=*/flutterPlatformViewsController, + /*task_runners=*/runners, + /*worker_task_runner=*/nil, + /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + + FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = + [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; + flutterPlatformViewsController->RegisterViewFactory( + factory, @"MockFlutterPlatformView", + FlutterPlatformViewGestureRecognizersBlockingPolicyEager); + FlutterResult result = ^(id result) { + }; + flutterPlatformViewsController->OnMethodCall( + [FlutterMethodCall + methodCallWithMethodName:@"create" + arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], + result); + + XCTAssertNotNil(gMockPlatformView); + + UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; + flutterPlatformViewsController->SetFlutterView(mockFlutterView); + // Create embedded view params + flutter::MutatorsStack stack; + // Layer tree always pushes a screen scale factor to the stack + CGFloat screenScale = [UIScreen mainScreen].scale; + SkMatrix screenScaleMatrix = SkMatrix::Scale(screenScale, screenScale); + stack.PushTransform(screenScaleMatrix); + // Push a backdrop filter + auto filter = std::make_shared(5, 2, flutter::DlTileMode::kClamp); + stack.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 8, screenScale * 8)); + + auto embeddedViewParams = + std::make_unique(screenScaleMatrix, SkSize::Make(5, 10), stack); + + flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); + flutterPlatformViewsController->CompositeEmbeddedView(2); + XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]); + ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; + [mockFlutterView addSubview:childClippingView]; + + [mockFlutterView setNeedsLayout]; + [mockFlutterView layoutIfNeeded]; + + // childClippingView has visual effect view with the correct configurations. + NSUInteger numberOfExpectedVisualEffectView = 0; + for (UIView* subview in childClippingView.subviews) { + if (![subview isKindOfClass:[UIVisualEffectView class]]) { + continue; + } + XCTAssertLessThan(numberOfExpectedVisualEffectView, 1u); + if ([self validateOneVisualEffectView:subview + expectedFrame:CGRectMake(0, 0, 5, 8) + inputRadius:5]) { + numberOfExpectedVisualEffectView++; + } + } + XCTAssertEqual(numberOfExpectedVisualEffectView, 1u); +} + +- (void)testApplyMultipleBackdropFilters { + 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(thread_task_runner); + auto platform_view = std::make_unique( + /*delegate=*/mock_delegate, + /*rendering_api=*/mock_delegate.settings_.enable_impeller + ? flutter::IOSRenderingAPI::kMetal + : flutter::IOSRenderingAPI::kSoftware, + /*platform_views_controller=*/flutterPlatformViewsController, + /*task_runners=*/runners, + /*worker_task_runner=*/nil, + /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + + FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = + [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; + flutterPlatformViewsController->RegisterViewFactory( + factory, @"MockFlutterPlatformView", + FlutterPlatformViewGestureRecognizersBlockingPolicyEager); + FlutterResult result = ^(id result) { + }; + flutterPlatformViewsController->OnMethodCall( + [FlutterMethodCall + methodCallWithMethodName:@"create" + arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], + result); + + XCTAssertNotNil(gMockPlatformView); + + UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; + flutterPlatformViewsController->SetFlutterView(mockFlutterView); + // Create embedded view params + flutter::MutatorsStack stack; + // Layer tree always pushes a screen scale factor to the stack + CGFloat screenScale = [UIScreen mainScreen].scale; + SkMatrix screenScaleMatrix = SkMatrix::Scale(screenScale, screenScale); + stack.PushTransform(screenScaleMatrix); + // Push backdrop filters + for (int i = 0; i < 50; i++) { + auto filter = std::make_shared(i, 2, flutter::DlTileMode::kClamp); + stack.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); + } + + auto embeddedViewParams = + std::make_unique(screenScaleMatrix, SkSize::Make(20, 20), stack); + + flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); + flutterPlatformViewsController->CompositeEmbeddedView(2); + XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); + ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; + [mockFlutterView addSubview:childClippingView]; + + [mockFlutterView setNeedsLayout]; + [mockFlutterView layoutIfNeeded]; + + NSUInteger numberOfExpectedVisualEffectView = 0; + for (UIView* subview in childClippingView.subviews) { + if (![subview isKindOfClass:[UIVisualEffectView class]]) { + continue; + } + XCTAssertLessThan(numberOfExpectedVisualEffectView, 50u); + if ([self validateOneVisualEffectView:subview + expectedFrame:CGRectMake(0, 0, 10, 10) + inputRadius:(CGFloat)numberOfExpectedVisualEffectView]) { + numberOfExpectedVisualEffectView++; + } + } + XCTAssertEqual(numberOfExpectedVisualEffectView, (NSUInteger)numberOfExpectedVisualEffectView); +} + +- (void)testAddBackdropFilters { + 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(thread_task_runner); + auto platform_view = std::make_unique( + /*delegate=*/mock_delegate, + /*rendering_api=*/mock_delegate.settings_.enable_impeller + ? flutter::IOSRenderingAPI::kMetal + : flutter::IOSRenderingAPI::kSoftware, + /*platform_views_controller=*/flutterPlatformViewsController, + /*task_runners=*/runners, + /*worker_task_runner=*/nil, + /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + + FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = + [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; + flutterPlatformViewsController->RegisterViewFactory( + factory, @"MockFlutterPlatformView", + FlutterPlatformViewGestureRecognizersBlockingPolicyEager); + FlutterResult result = ^(id result) { + }; + flutterPlatformViewsController->OnMethodCall( + [FlutterMethodCall + methodCallWithMethodName:@"create" + arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], + result); + + XCTAssertNotNil(gMockPlatformView); + + UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; + flutterPlatformViewsController->SetFlutterView(mockFlutterView); + // Create embedded view params + flutter::MutatorsStack stack; + // Layer tree always pushes a screen scale factor to the stack + CGFloat screenScale = [UIScreen mainScreen].scale; + SkMatrix screenScaleMatrix = SkMatrix::Scale(screenScale, screenScale); + stack.PushTransform(screenScaleMatrix); + // Push a backdrop filter + auto filter = std::make_shared(5, 2, flutter::DlTileMode::kClamp); + stack.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); + + auto embeddedViewParams = + std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); + + flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); + flutterPlatformViewsController->CompositeEmbeddedView(2); + XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]); + ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; + [mockFlutterView addSubview:childClippingView]; + + [mockFlutterView setNeedsLayout]; + [mockFlutterView layoutIfNeeded]; + + NSMutableArray* originalVisualEffectViews = [[NSMutableArray alloc] init]; + for (UIView* subview in childClippingView.subviews) { + if (![subview isKindOfClass:[UIVisualEffectView class]]) { + continue; + } + XCTAssertLessThan(originalVisualEffectViews.count, 1u); + if ([self validateOneVisualEffectView:subview + expectedFrame:CGRectMake(0, 0, 10, 10) + inputRadius:(CGFloat)5]) { + [originalVisualEffectViews addObject:subview]; + } + } + XCTAssertEqual(originalVisualEffectViews.count, 1u); + + // + // Simulate adding 1 backdrop filter (create a new mutators stack) + // Create embedded view params + flutter::MutatorsStack stack2; + // Layer tree always pushes a screen scale factor to the stack + stack2.PushTransform(screenScaleMatrix); + // Push backdrop filters + for (int i = 0; i < 2; i++) { + stack2.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); + } + + embeddedViewParams = std::make_unique(screenScaleMatrix, + SkSize::Make(10, 10), stack2); + + flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); + flutterPlatformViewsController->CompositeEmbeddedView(2); + [mockFlutterView setNeedsLayout]; + [mockFlutterView layoutIfNeeded]; + + NSMutableArray* newVisualEffectViews = [[NSMutableArray alloc] init]; + for (UIView* subview in childClippingView.subviews) { + if (![subview isKindOfClass:[UIVisualEffectView class]]) { + continue; + } + XCTAssertLessThan(newVisualEffectViews.count, 2u); + + if ([self validateOneVisualEffectView:subview + expectedFrame:CGRectMake(0, 0, 10, 10) + inputRadius:(CGFloat)5]) { + [newVisualEffectViews addObject:subview]; + } + } + XCTAssertEqual(newVisualEffectViews.count, 2u); + for (NSUInteger i = 0; i < originalVisualEffectViews.count; i++) { + UIView* originalView = originalVisualEffectViews[i]; + UIView* newView = newVisualEffectViews[i]; + // Compare reference. + XCTAssertEqual(originalView, newView); + id mockOrignalView = OCMPartialMock(originalView); + OCMReject([mockOrignalView removeFromSuperview]); + [mockOrignalView stopMocking]; + } +} + +- (void)testRemoveBackdropFilters { + 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(thread_task_runner); + auto platform_view = std::make_unique( + /*delegate=*/mock_delegate, + /*rendering_api=*/mock_delegate.settings_.enable_impeller + ? flutter::IOSRenderingAPI::kMetal + : flutter::IOSRenderingAPI::kSoftware, + /*platform_views_controller=*/flutterPlatformViewsController, + /*task_runners=*/runners, + /*worker_task_runner=*/nil, + /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + + FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = + [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; + flutterPlatformViewsController->RegisterViewFactory( + factory, @"MockFlutterPlatformView", + FlutterPlatformViewGestureRecognizersBlockingPolicyEager); + FlutterResult result = ^(id result) { + }; + flutterPlatformViewsController->OnMethodCall( + [FlutterMethodCall + methodCallWithMethodName:@"create" + arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], + result); + + XCTAssertNotNil(gMockPlatformView); + + UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; + flutterPlatformViewsController->SetFlutterView(mockFlutterView); + // Create embedded view params + flutter::MutatorsStack stack; + // Layer tree always pushes a screen scale factor to the stack + CGFloat screenScale = [UIScreen mainScreen].scale; + SkMatrix screenScaleMatrix = SkMatrix::Scale(screenScale, screenScale); + stack.PushTransform(screenScaleMatrix); + // Push backdrop filters + auto filter = std::make_shared(5, 2, flutter::DlTileMode::kClamp); + for (int i = 0; i < 5; i++) { + stack.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); + } + + auto embeddedViewParams = + std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); + + flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); + flutterPlatformViewsController->CompositeEmbeddedView(2); + XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); + ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; + [mockFlutterView addSubview:childClippingView]; + + [mockFlutterView setNeedsLayout]; + [mockFlutterView layoutIfNeeded]; + + NSMutableArray* originalVisualEffectViews = [[NSMutableArray alloc] init]; + for (UIView* subview in childClippingView.subviews) { + if (![subview isKindOfClass:[UIVisualEffectView class]]) { + continue; + } + XCTAssertLessThan(originalVisualEffectViews.count, 5u); + if ([self validateOneVisualEffectView:subview + expectedFrame:CGRectMake(0, 0, 10, 10) + inputRadius:(CGFloat)5]) { + [originalVisualEffectViews addObject:subview]; + } + } + + // Simulate removing 1 backdrop filter (create a new mutators stack) + // Create embedded view params + flutter::MutatorsStack stack2; + // Layer tree always pushes a screen scale factor to the stack + stack2.PushTransform(screenScaleMatrix); + // Push backdrop filters + for (int i = 0; i < 4; i++) { + stack2.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); + } + + embeddedViewParams = std::make_unique(screenScaleMatrix, + SkSize::Make(10, 10), stack2); + + flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); + flutterPlatformViewsController->CompositeEmbeddedView(2); + [mockFlutterView setNeedsLayout]; + [mockFlutterView layoutIfNeeded]; + + NSMutableArray* newVisualEffectViews = [[NSMutableArray alloc] init]; + for (UIView* subview in childClippingView.subviews) { + if (![subview isKindOfClass:[UIVisualEffectView class]]) { + continue; + } + XCTAssertLessThan(newVisualEffectViews.count, 4u); + if ([self validateOneVisualEffectView:subview + expectedFrame:CGRectMake(0, 0, 10, 10) + inputRadius:(CGFloat)5]) { + [newVisualEffectViews addObject:subview]; + } + } + XCTAssertEqual(newVisualEffectViews.count, 4u); + + for (NSUInteger i = 0; i < newVisualEffectViews.count; i++) { + UIView* newView = newVisualEffectViews[i]; + id mockNewView = OCMPartialMock(newView); + UIView* originalView = originalVisualEffectViews[i]; + // Compare reference. + XCTAssertEqual(originalView, newView); + OCMReject([mockNewView removeFromSuperview]); + [mockNewView stopMocking]; + } + + // Simulate removing all backdrop filters (replace the mutators stack) + // Update embedded view params, delete except screenScaleMatrix + for (int i = 0; i < 5; i++) { + stack2.Pop(); + } + // No backdrop filters in the stack, so no nothing to push + + embeddedViewParams = std::make_unique(screenScaleMatrix, + SkSize::Make(10, 10), stack2); + + flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); + flutterPlatformViewsController->CompositeEmbeddedView(2); + [mockFlutterView setNeedsLayout]; + [mockFlutterView layoutIfNeeded]; + + NSUInteger numberOfExpectedVisualEffectView = 0u; + for (UIView* subview in childClippingView.subviews) { + if ([subview isKindOfClass:[UIVisualEffectView class]]) { + numberOfExpectedVisualEffectView++; + } + } + XCTAssertEqual(numberOfExpectedVisualEffectView, 0u); +} + +- (void)testEditBackdropFilters { + 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(thread_task_runner); + auto platform_view = std::make_unique( + /*delegate=*/mock_delegate, + /*rendering_api=*/mock_delegate.settings_.enable_impeller + ? flutter::IOSRenderingAPI::kMetal + : flutter::IOSRenderingAPI::kSoftware, + /*platform_views_controller=*/flutterPlatformViewsController, + /*task_runners=*/runners, + /*worker_task_runner=*/nil, + /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + + FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = + [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; + flutterPlatformViewsController->RegisterViewFactory( + factory, @"MockFlutterPlatformView", + FlutterPlatformViewGestureRecognizersBlockingPolicyEager); + FlutterResult result = ^(id result) { + }; + flutterPlatformViewsController->OnMethodCall( + [FlutterMethodCall + methodCallWithMethodName:@"create" + arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], + result); + + XCTAssertNotNil(gMockPlatformView); + + UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; + flutterPlatformViewsController->SetFlutterView(mockFlutterView); + // Create embedded view params + flutter::MutatorsStack stack; + // Layer tree always pushes a screen scale factor to the stack + CGFloat screenScale = [UIScreen mainScreen].scale; + SkMatrix screenScaleMatrix = SkMatrix::Scale(screenScale, screenScale); + stack.PushTransform(screenScaleMatrix); + // Push backdrop filters + auto filter = std::make_shared(5, 2, flutter::DlTileMode::kClamp); + for (int i = 0; i < 5; i++) { + stack.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); + } + + auto embeddedViewParams = + std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); + + flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); + flutterPlatformViewsController->CompositeEmbeddedView(2); + XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); + ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; + [mockFlutterView addSubview:childClippingView]; + + [mockFlutterView setNeedsLayout]; + [mockFlutterView layoutIfNeeded]; + + NSMutableArray* originalVisualEffectViews = [[NSMutableArray alloc] init]; + for (UIView* subview in childClippingView.subviews) { + if (![subview isKindOfClass:[UIVisualEffectView class]]) { + continue; + } + XCTAssertLessThan(originalVisualEffectViews.count, 5u); + if ([self validateOneVisualEffectView:subview + expectedFrame:CGRectMake(0, 0, 10, 10) + inputRadius:(CGFloat)5]) { + [originalVisualEffectViews addObject:subview]; + } + } + + // Simulate editing 1 backdrop filter in the middle of the stack (create a new mutators stack) + // Create embedded view params + flutter::MutatorsStack stack2; + // Layer tree always pushes a screen scale factor to the stack + stack2.PushTransform(screenScaleMatrix); + // Push backdrop filters + for (int i = 0; i < 5; i++) { + if (i == 3) { + auto filter2 = + std::make_shared(2, 5, flutter::DlTileMode::kClamp); + + stack2.PushBackdropFilter(filter2, + SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); + continue; + } + + stack2.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); + } + + embeddedViewParams = std::make_unique(screenScaleMatrix, + SkSize::Make(10, 10), stack2); + + flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); + flutterPlatformViewsController->CompositeEmbeddedView(2); + [mockFlutterView setNeedsLayout]; + [mockFlutterView layoutIfNeeded]; + + NSMutableArray* newVisualEffectViews = [[NSMutableArray alloc] init]; + for (UIView* subview in childClippingView.subviews) { + if (![subview isKindOfClass:[UIVisualEffectView class]]) { + continue; + } + XCTAssertLessThan(newVisualEffectViews.count, 5u); + CGFloat expectInputRadius = 5; + if (newVisualEffectViews.count == 3) { + expectInputRadius = 2; + } + if ([self validateOneVisualEffectView:subview + expectedFrame:CGRectMake(0, 0, 10, 10) + inputRadius:(CGFloat)expectInputRadius]) { + [newVisualEffectViews addObject:subview]; + } + } + XCTAssertEqual(newVisualEffectViews.count, 5u); + for (NSUInteger i = 0; i < newVisualEffectViews.count; i++) { + UIView* newView = newVisualEffectViews[i]; + id mockNewView = OCMPartialMock(newView); + UIView* originalView = originalVisualEffectViews[i]; + // Compare reference. + XCTAssertEqual(originalView, newView); + OCMReject([mockNewView removeFromSuperview]); + [mockNewView stopMocking]; + } + [newVisualEffectViews removeAllObjects]; + + // Simulate editing 1 backdrop filter in the beginning of the stack (replace the mutators stack) + // Update embedded view params, delete except screenScaleMatrix + for (int i = 0; i < 5; i++) { + stack2.Pop(); + } + // Push backdrop filters + for (int i = 0; i < 5; i++) { + if (i == 0) { + auto filter2 = + std::make_shared(2, 5, flutter::DlTileMode::kClamp); + stack2.PushBackdropFilter(filter2, + SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); + continue; + } + + stack2.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); + } + + embeddedViewParams = std::make_unique(screenScaleMatrix, + SkSize::Make(10, 10), stack2); + + flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); + flutterPlatformViewsController->CompositeEmbeddedView(2); + [mockFlutterView setNeedsLayout]; + [mockFlutterView layoutIfNeeded]; + + for (UIView* subview in childClippingView.subviews) { + if (![subview isKindOfClass:[UIVisualEffectView class]]) { + continue; + } + XCTAssertLessThan(newVisualEffectViews.count, 5u); + CGFloat expectInputRadius = 5; + if (newVisualEffectViews.count == 0) { + expectInputRadius = 2; + } + if ([self validateOneVisualEffectView:subview + expectedFrame:CGRectMake(0, 0, 10, 10) + inputRadius:(CGFloat)expectInputRadius]) { + [newVisualEffectViews addObject:subview]; + } + } + for (NSUInteger i = 0; i < newVisualEffectViews.count; i++) { + UIView* newView = newVisualEffectViews[i]; + id mockNewView = OCMPartialMock(newView); + UIView* originalView = originalVisualEffectViews[i]; + // Compare reference. + XCTAssertEqual(originalView, newView); + OCMReject([mockNewView removeFromSuperview]); + [mockNewView stopMocking]; + } + [newVisualEffectViews removeAllObjects]; + + // Simulate editing 1 backdrop filter in the end of the stack (replace the mutators stack) + // Update embedded view params, delete except screenScaleMatrix + for (int i = 0; i < 5; i++) { + stack2.Pop(); + } + // Push backdrop filters + for (int i = 0; i < 5; i++) { + if (i == 4) { + auto filter2 = + std::make_shared(2, 5, flutter::DlTileMode::kClamp); + stack2.PushBackdropFilter(filter2, + SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); + continue; + } + + stack2.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); + } + + embeddedViewParams = std::make_unique(screenScaleMatrix, + SkSize::Make(10, 10), stack2); + + flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); + flutterPlatformViewsController->CompositeEmbeddedView(2); + [mockFlutterView setNeedsLayout]; + [mockFlutterView layoutIfNeeded]; + + for (UIView* subview in childClippingView.subviews) { + if (![subview isKindOfClass:[UIVisualEffectView class]]) { + continue; + } + XCTAssertLessThan(newVisualEffectViews.count, 5u); + CGFloat expectInputRadius = 5; + if (newVisualEffectViews.count == 4) { + expectInputRadius = 2; + } + if ([self validateOneVisualEffectView:subview + expectedFrame:CGRectMake(0, 0, 10, 10) + inputRadius:(CGFloat)expectInputRadius]) { + [newVisualEffectViews addObject:subview]; + } + } + XCTAssertEqual(newVisualEffectViews.count, 5u); + + for (NSUInteger i = 0; i < newVisualEffectViews.count; i++) { + UIView* newView = newVisualEffectViews[i]; + id mockNewView = OCMPartialMock(newView); + UIView* originalView = originalVisualEffectViews[i]; + // Compare reference. + XCTAssertEqual(originalView, newView); + OCMReject([mockNewView removeFromSuperview]); + [mockNewView stopMocking]; + } + [newVisualEffectViews removeAllObjects]; + + // Simulate editing all backdrop filters in the stack (replace the mutators stack) + // Update embedded view params, delete except screenScaleMatrix + for (int i = 0; i < 5; i++) { + stack2.Pop(); + } + // Push backdrop filters + for (int i = 0; i < 5; i++) { + auto filter2 = std::make_shared(i, 2, flutter::DlTileMode::kClamp); + + stack2.PushBackdropFilter(filter2, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); + } + + embeddedViewParams = std::make_unique(screenScaleMatrix, + SkSize::Make(10, 10), stack2); + + flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); + flutterPlatformViewsController->CompositeEmbeddedView(2); + [mockFlutterView setNeedsLayout]; + [mockFlutterView layoutIfNeeded]; + + for (UIView* subview in childClippingView.subviews) { + if (![subview isKindOfClass:[UIVisualEffectView class]]) { + continue; + } + XCTAssertLessThan(newVisualEffectViews.count, 5u); + if ([self validateOneVisualEffectView:subview + expectedFrame:CGRectMake(0, 0, 10, 10) + inputRadius:(CGFloat)newVisualEffectViews.count]) { + [newVisualEffectViews addObject:subview]; + } + } + XCTAssertEqual(newVisualEffectViews.count, 5u); + + for (NSUInteger i = 0; i < newVisualEffectViews.count; i++) { + UIView* newView = newVisualEffectViews[i]; + id mockNewView = OCMPartialMock(newView); + UIView* originalView = originalVisualEffectViews[i]; + // Compare reference. + XCTAssertEqual(originalView, newView); + OCMReject([mockNewView removeFromSuperview]); + [mockNewView stopMocking]; + } + [newVisualEffectViews removeAllObjects]; +} + +- (void)testApplyBackdropFilterNotDlBlurImageFilter { + 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(thread_task_runner); + auto platform_view = std::make_unique( + /*delegate=*/mock_delegate, + /*rendering_api=*/mock_delegate.settings_.enable_impeller + ? flutter::IOSRenderingAPI::kMetal + : flutter::IOSRenderingAPI::kSoftware, + /*platform_views_controller=*/flutterPlatformViewsController, + /*task_runners=*/runners, + /*worker_task_runner=*/nil, + /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + + FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = + [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; + flutterPlatformViewsController->RegisterViewFactory( + factory, @"MockFlutterPlatformView", + FlutterPlatformViewGestureRecognizersBlockingPolicyEager); + FlutterResult result = ^(id result) { + }; + flutterPlatformViewsController->OnMethodCall( + [FlutterMethodCall + methodCallWithMethodName:@"create" + arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], + result); + + XCTAssertNotNil(gMockPlatformView); + + UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; + flutterPlatformViewsController->SetFlutterView(mockFlutterView); + // Create embedded view params + flutter::MutatorsStack stack; + // Layer tree always pushes a screen scale factor to the stack + CGFloat screenScale = [UIScreen mainScreen].scale; + SkMatrix screenScaleMatrix = SkMatrix::Scale(screenScale, screenScale); + stack.PushTransform(screenScaleMatrix); + // Push a dilate backdrop filter + auto dilateFilter = std::make_shared(5, 2); + stack.PushBackdropFilter(dilateFilter, SkRect::MakeEmpty()); + + auto embeddedViewParams = + std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); + + flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); + flutterPlatformViewsController->CompositeEmbeddedView(2); + XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]); + ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; + + [mockFlutterView addSubview:childClippingView]; + + [mockFlutterView setNeedsLayout]; + [mockFlutterView layoutIfNeeded]; + + NSUInteger numberOfExpectedVisualEffectView = 0; + for (UIView* subview in childClippingView.subviews) { + if ([subview isKindOfClass:[UIVisualEffectView class]]) { + numberOfExpectedVisualEffectView++; + } + } + XCTAssertEqual(numberOfExpectedVisualEffectView, 0u); + + // Simulate adding a non-DlBlurImageFilter in the middle of the stack (create a new mutators + // stack) Create embedded view params + flutter::MutatorsStack stack2; + // Layer tree always pushes a screen scale factor to the stack + stack2.PushTransform(screenScaleMatrix); + // Push backdrop filters and dilate filter + auto blurFilter = std::make_shared(5, 2, flutter::DlTileMode::kClamp); + + for (int i = 0; i < 5; i++) { + if (i == 2) { + stack2.PushBackdropFilter(dilateFilter, + SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); + continue; + } + + stack2.PushBackdropFilter(blurFilter, + SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); + } + + embeddedViewParams = std::make_unique(screenScaleMatrix, + SkSize::Make(10, 10), stack2); + + flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); + flutterPlatformViewsController->CompositeEmbeddedView(2); + [mockFlutterView setNeedsLayout]; + [mockFlutterView layoutIfNeeded]; + + numberOfExpectedVisualEffectView = 0; + for (UIView* subview in childClippingView.subviews) { + if (![subview isKindOfClass:[UIVisualEffectView class]]) { + continue; + } + XCTAssertLessThan(numberOfExpectedVisualEffectView, 4u); + if ([self validateOneVisualEffectView:subview + expectedFrame:CGRectMake(0, 0, 10, 10) + inputRadius:(CGFloat)5]) { + numberOfExpectedVisualEffectView++; + } + } + XCTAssertEqual(numberOfExpectedVisualEffectView, 4u); + + // Simulate adding a non-DlBlurImageFilter to the beginning of the stack (replace the mutators + // stack) Update embedded view params, delete except screenScaleMatrix + for (int i = 0; i < 5; i++) { + stack2.Pop(); + } + // Push backdrop filters and dilate filter + for (int i = 0; i < 5; i++) { + if (i == 0) { + stack2.PushBackdropFilter(dilateFilter, + SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); + continue; + } + + stack2.PushBackdropFilter(blurFilter, + SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); + } + + embeddedViewParams = std::make_unique(screenScaleMatrix, + SkSize::Make(10, 10), stack2); + + flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); + flutterPlatformViewsController->CompositeEmbeddedView(2); + [mockFlutterView setNeedsLayout]; + [mockFlutterView layoutIfNeeded]; + + numberOfExpectedVisualEffectView = 0; + for (UIView* subview in childClippingView.subviews) { + if (![subview isKindOfClass:[UIVisualEffectView class]]) { + continue; + } + XCTAssertLessThan(numberOfExpectedVisualEffectView, 4u); + if ([self validateOneVisualEffectView:subview + expectedFrame:CGRectMake(0, 0, 10, 10) + inputRadius:(CGFloat)5]) { + numberOfExpectedVisualEffectView++; + } + } + XCTAssertEqual(numberOfExpectedVisualEffectView, 4u); + + // Simulate adding a non-DlBlurImageFilter to the end of the stack (replace the mutators stack) + // Update embedded view params, delete except screenScaleMatrix + for (int i = 0; i < 5; i++) { + stack2.Pop(); + } + // Push backdrop filters and dilate filter + for (int i = 0; i < 5; i++) { + if (i == 4) { + stack2.PushBackdropFilter(dilateFilter, + SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); + continue; + } + + stack2.PushBackdropFilter(blurFilter, + SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); + } + + embeddedViewParams = std::make_unique(screenScaleMatrix, + SkSize::Make(10, 10), stack2); + + flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); + flutterPlatformViewsController->CompositeEmbeddedView(2); + [mockFlutterView setNeedsLayout]; + [mockFlutterView layoutIfNeeded]; + + numberOfExpectedVisualEffectView = 0; + for (UIView* subview in childClippingView.subviews) { + if (![subview isKindOfClass:[UIVisualEffectView class]]) { + continue; + } + XCTAssertLessThan(numberOfExpectedVisualEffectView, 4u); + if ([self validateOneVisualEffectView:subview + expectedFrame:CGRectMake(0, 0, 10, 10) + inputRadius:(CGFloat)5]) { + numberOfExpectedVisualEffectView++; + } + } + XCTAssertEqual(numberOfExpectedVisualEffectView, 4u); + + // Simulate adding only non-DlBlurImageFilter to the stack (replace the mutators stack) + // Update embedded view params, delete except screenScaleMatrix + for (int i = 0; i < 5; i++) { + stack2.Pop(); + } + // Push dilate filters + for (int i = 0; i < 5; i++) { + stack2.PushBackdropFilter(dilateFilter, + SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); + } + + embeddedViewParams = std::make_unique(screenScaleMatrix, + SkSize::Make(10, 10), stack2); + + flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); + flutterPlatformViewsController->CompositeEmbeddedView(2); + [mockFlutterView setNeedsLayout]; + [mockFlutterView layoutIfNeeded]; + + numberOfExpectedVisualEffectView = 0; + for (UIView* subview in childClippingView.subviews) { + if ([subview isKindOfClass:[UIVisualEffectView class]]) { + numberOfExpectedVisualEffectView++; + } + } + XCTAssertEqual(numberOfExpectedVisualEffectView, 0u); +} + +- (void)testApplyBackdropFilterCorrectAPI { + [PlatformViewFilter resetPreparation]; + // The gaussianBlur filter is extracted from UIVisualEffectView. + // Each test requires a new PlatformViewFilter + // Valid UIVisualEffectView API + UIVisualEffectView* visualEffectView = [[UIVisualEffectView alloc] + initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]]; + PlatformViewFilter* platformViewFilter = + [[PlatformViewFilter alloc] initWithFrame:CGRectMake(0, 0, 10, 10) + blurRadius:5 + visualEffectView:visualEffectView]; + XCTAssertNotNil(platformViewFilter); +} + +- (void)testApplyBackdropFilterAPIChangedInvalidUIVisualEffectView { + [PlatformViewFilter resetPreparation]; + UIVisualEffectView* visualEffectView = [[UIVisualEffectView alloc] init]; + PlatformViewFilter* platformViewFilter = + [[PlatformViewFilter alloc] initWithFrame:CGRectMake(0, 0, 10, 10) + blurRadius:5 + visualEffectView:visualEffectView]; + XCTAssertNil(platformViewFilter); +} + +- (void)testApplyBackdropFilterAPIChangedNoGaussianBlurFilter { + [PlatformViewFilter resetPreparation]; + UIVisualEffectView* editedUIVisualEffectView = [[UIVisualEffectView alloc] + initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]]; + NSArray* subviews = editedUIVisualEffectView.subviews; + for (UIView* view in subviews) { + if ([NSStringFromClass([view class]) hasSuffix:@"BackdropView"]) { + for (CIFilter* filter in view.layer.filters) { + if ([[filter valueForKey:@"name"] isEqual:@"gaussianBlur"]) { + [filter setValue:@"notGaussianBlur" forKey:@"name"]; + break; + } + } + break; + } + } + PlatformViewFilter* platformViewFilter = + [[PlatformViewFilter alloc] initWithFrame:CGRectMake(0, 0, 10, 10) + blurRadius:5 + visualEffectView:editedUIVisualEffectView]; + XCTAssertNil(platformViewFilter); +} + +- (void)testApplyBackdropFilterAPIChangedInvalidInputRadius { + [PlatformViewFilter resetPreparation]; + UIVisualEffectView* editedUIVisualEffectView = [[UIVisualEffectView alloc] + initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]]; + NSArray* subviews = editedUIVisualEffectView.subviews; + for (UIView* view in subviews) { + if ([NSStringFromClass([view class]) hasSuffix:@"BackdropView"]) { + for (CIFilter* filter in view.layer.filters) { + if ([[filter valueForKey:@"name"] isEqual:@"gaussianBlur"]) { + [filter setValue:@"invalidInputRadius" forKey:@"inputRadius"]; + break; + } + } + break; + } + } + + PlatformViewFilter* platformViewFilter = + [[PlatformViewFilter alloc] initWithFrame:CGRectMake(0, 0, 10, 10) + blurRadius:5 + visualEffectView:editedUIVisualEffectView]; + XCTAssertNil(platformViewFilter); +} + +- (void)testBackdropFilterVisualEffectSubviewBackgroundColor { + __weak UIVisualEffectView* weakVisualEffectView; + + @autoreleasepool { + UIVisualEffectView* visualEffectView = [[UIVisualEffectView alloc] + initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]]; + weakVisualEffectView = visualEffectView; + PlatformViewFilter* platformViewFilter = + [[PlatformViewFilter alloc] initWithFrame:CGRectMake(0, 0, 10, 10) + blurRadius:5 + visualEffectView:visualEffectView]; + CGColorRef visualEffectSubviewBackgroundColor = nil; + for (UIView* view in [platformViewFilter backdropFilterView].subviews) { + if ([NSStringFromClass([view class]) hasSuffix:@"VisualEffectSubview"]) { + visualEffectSubviewBackgroundColor = view.layer.backgroundColor; + } + } + XCTAssertTrue( + CGColorEqualToColor(visualEffectSubviewBackgroundColor, UIColor.clearColor.CGColor)); + } + XCTAssertNil(weakVisualEffectView); +} + +- (void)testCompositePlatformView { + 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(thread_task_runner); + auto platform_view = std::make_unique( + /*delegate=*/mock_delegate, + /*rendering_api=*/mock_delegate.settings_.enable_impeller + ? flutter::IOSRenderingAPI::kMetal + : flutter::IOSRenderingAPI::kSoftware, + /*platform_views_controller=*/flutterPlatformViewsController, + /*task_runners=*/runners, + /*worker_task_runner=*/nil, + /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + + FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = + [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; + flutterPlatformViewsController->RegisterViewFactory( + factory, @"MockFlutterPlatformView", + FlutterPlatformViewGestureRecognizersBlockingPolicyEager); + FlutterResult result = ^(id result) { + }; + flutterPlatformViewsController->OnMethodCall( + [FlutterMethodCall + methodCallWithMethodName:@"create" + arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], + result); + + XCTAssertNotNil(gMockPlatformView); + + UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; + flutterPlatformViewsController->SetFlutterView(mockFlutterView); + // Create embedded view params + flutter::MutatorsStack stack; + // Layer tree always pushes a screen scale factor to the stack + SkMatrix screenScaleMatrix = + SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); + stack.PushTransform(screenScaleMatrix); + // Push a translate matrix + SkMatrix translateMatrix = SkMatrix::Translate(100, 100); + stack.PushTransform(translateMatrix); + SkMatrix finalMatrix; + finalMatrix.setConcat(screenScaleMatrix, translateMatrix); + + auto embeddedViewParams = + std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); + + flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); + flutterPlatformViewsController->CompositeEmbeddedView(2); + CGRect platformViewRectInFlutterView = [gMockPlatformView convertRect:gMockPlatformView.bounds + toView:mockFlutterView]; + XCTAssertTrue(CGRectEqualToRect(platformViewRectInFlutterView, CGRectMake(100, 100, 300, 300))); +} + +- (void)testBackdropFilterCorrectlyPushedAndReset { + 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(thread_task_runner); + auto platform_view = std::make_unique( + /*delegate=*/mock_delegate, + /*rendering_api=*/mock_delegate.settings_.enable_impeller + ? flutter::IOSRenderingAPI::kMetal + : flutter::IOSRenderingAPI::kSoftware, + /*platform_views_controller=*/flutterPlatformViewsController, + /*task_runners=*/runners, + /*worker_task_runner=*/nil, + /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + + FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = + [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; + flutterPlatformViewsController->RegisterViewFactory( + factory, @"MockFlutterPlatformView", + FlutterPlatformViewGestureRecognizersBlockingPolicyEager); + FlutterResult result = ^(id result) { + }; + flutterPlatformViewsController->OnMethodCall( + [FlutterMethodCall + methodCallWithMethodName:@"create" + arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], + result); + + XCTAssertNotNil(gMockPlatformView); + + UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; + flutterPlatformViewsController->SetFlutterView(mockFlutterView); + // Create embedded view params + flutter::MutatorsStack stack; + // Layer tree always pushes a screen scale factor to the stack + CGFloat screenScale = [UIScreen mainScreen].scale; + SkMatrix screenScaleMatrix = SkMatrix::Scale(screenScale, screenScale); + stack.PushTransform(screenScaleMatrix); + + auto embeddedViewParams = + std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); + + flutterPlatformViewsController->BeginFrame(SkISize::Make(0, 0)); + flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); + flutterPlatformViewsController->PushVisitedPlatformView(2); + auto filter = std::make_shared(5, 2, flutter::DlTileMode::kClamp); + flutterPlatformViewsController->PushFilterToVisitedPlatformViews( + filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); + flutterPlatformViewsController->CompositeEmbeddedView(2); + XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]); + ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; + [mockFlutterView addSubview:childClippingView]; + + [mockFlutterView setNeedsLayout]; + [mockFlutterView layoutIfNeeded]; + + // childClippingView has visual effect view with the correct configurations. + NSUInteger numberOfExpectedVisualEffectView = 0; + for (UIView* subview in childClippingView.subviews) { + if (![subview isKindOfClass:[UIVisualEffectView class]]) { + continue; + } + XCTAssertLessThan(numberOfExpectedVisualEffectView, 1u); + if ([self validateOneVisualEffectView:subview + expectedFrame:CGRectMake(0, 0, 10, 10) + inputRadius:5]) { + numberOfExpectedVisualEffectView++; + } + } + XCTAssertEqual(numberOfExpectedVisualEffectView, 1u); + + // New frame, with no filter pushed. + auto embeddedViewParams2 = + std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); + flutterPlatformViewsController->BeginFrame(SkISize::Make(0, 0)); + flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams2)); + flutterPlatformViewsController->CompositeEmbeddedView(2); + XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]); + + [mockFlutterView setNeedsLayout]; + [mockFlutterView layoutIfNeeded]; + + numberOfExpectedVisualEffectView = 0; + for (UIView* subview in childClippingView.subviews) { + if (![subview isKindOfClass:[UIVisualEffectView class]]) { + continue; + } + numberOfExpectedVisualEffectView++; + } + XCTAssertEqual(numberOfExpectedVisualEffectView, 0u); +} + +- (void)testChildClippingViewShouldBeTheBoundingRectOfPlatformView { + 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(thread_task_runner); + auto platform_view = std::make_unique( + /*delegate=*/mock_delegate, + /*rendering_api=*/mock_delegate.settings_.enable_impeller + ? flutter::IOSRenderingAPI::kMetal + : flutter::IOSRenderingAPI::kSoftware, + /*platform_views_controller=*/flutterPlatformViewsController, + /*task_runners=*/runners, + /*worker_task_runner=*/nil, + /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + + FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = + [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; + flutterPlatformViewsController->RegisterViewFactory( + factory, @"MockFlutterPlatformView", + FlutterPlatformViewGestureRecognizersBlockingPolicyEager); + FlutterResult result = ^(id result) { + }; + flutterPlatformViewsController->OnMethodCall( + [FlutterMethodCall + methodCallWithMethodName:@"create" + arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], + result); + + XCTAssertNotNil(gMockPlatformView); + + UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; + flutterPlatformViewsController->SetFlutterView(mockFlutterView); + // Create embedded view params + flutter::MutatorsStack stack; + // Layer tree always pushes a screen scale factor to the stack + SkMatrix screenScaleMatrix = + SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); + stack.PushTransform(screenScaleMatrix); + // Push a rotate matrix + SkMatrix rotateMatrix; + rotateMatrix.setRotate(10); + stack.PushTransform(rotateMatrix); + SkMatrix finalMatrix; + finalMatrix.setConcat(screenScaleMatrix, rotateMatrix); + + auto embeddedViewParams = + std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); + + flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); + flutterPlatformViewsController->CompositeEmbeddedView(2); + CGRect platformViewRectInFlutterView = [gMockPlatformView convertRect:gMockPlatformView.bounds + toView:mockFlutterView]; + XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); + ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; + // The childclippingview's frame is set based on flow, but the platform view's frame is set based + // on quartz. Although they should be the same, but we should tolerate small floating point + // errors. + XCTAssertLessThan(fabs(platformViewRectInFlutterView.origin.x - childClippingView.frame.origin.x), + kFloatCompareEpsilon); + XCTAssertLessThan(fabs(platformViewRectInFlutterView.origin.y - childClippingView.frame.origin.y), + kFloatCompareEpsilon); + XCTAssertLessThan( + fabs(platformViewRectInFlutterView.size.width - childClippingView.frame.size.width), + kFloatCompareEpsilon); + XCTAssertLessThan( + fabs(platformViewRectInFlutterView.size.height - childClippingView.frame.size.height), + kFloatCompareEpsilon); +} + +- (void)testClipsDoNotInterceptWithPlatformViewShouldNotAddMaskView { + 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(thread_task_runner); + auto platform_view = std::make_unique( + /*delegate=*/mock_delegate, + /*rendering_api=*/mock_delegate.settings_.enable_impeller + ? flutter::IOSRenderingAPI::kMetal + : flutter::IOSRenderingAPI::kSoftware, + /*platform_views_controller=*/flutterPlatformViewsController, + /*task_runners=*/runners, + /*worker_task_runner=*/nil, + /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + + FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = + [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; + flutterPlatformViewsController->RegisterViewFactory( + factory, @"MockFlutterPlatformView", + FlutterPlatformViewGestureRecognizersBlockingPolicyEager); + FlutterResult result = ^(id result) { + }; + flutterPlatformViewsController->OnMethodCall( + [FlutterMethodCall + methodCallWithMethodName:@"create" + arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], + result); + + XCTAssertNotNil(gMockPlatformView); + + UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 30, 30)]; + flutterPlatformViewsController->SetFlutterView(mockFlutterView); + // Create embedded view params. + flutter::MutatorsStack stack; + // Layer tree always pushes a screen scale factor to the stack. + SkMatrix screenScaleMatrix = + SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); + stack.PushTransform(screenScaleMatrix); + SkMatrix translateMatrix = SkMatrix::Translate(5, 5); + // The platform view's rect for this test will be (5, 5, 10, 10). + stack.PushTransform(translateMatrix); + // Push a clip rect, big enough to contain the entire platform view bound. + SkRect rect = SkRect::MakeXYWH(0, 0, 25, 25); + stack.PushClipRect(rect); + // Push a clip rrect, big enough to contain the entire platform view bound without clipping it. + // Make the origin (-1, -1) so that the top left rounded corner isn't clipping the PlatformView. + SkRect rect_for_rrect = SkRect::MakeXYWH(-1, -1, 25, 25); + SkRRect rrect = SkRRect::MakeRectXY(rect_for_rrect, 1, 1); + stack.PushClipRRect(rrect); + + auto embeddedViewParams = std::make_unique( + SkMatrix::Concat(screenScaleMatrix, translateMatrix), SkSize::Make(5, 5), stack); + + flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); + flutterPlatformViewsController->CompositeEmbeddedView(2); + gMockPlatformView.backgroundColor = UIColor.redColor; + XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); + ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; + [mockFlutterView addSubview:childClippingView]; + + [mockFlutterView setNeedsLayout]; + [mockFlutterView layoutIfNeeded]; + XCTAssertNil(childClippingView.maskView); +} + +- (void)testClipRRectOnlyHasCornersInterceptWithPlatformViewShouldAddMaskView { + 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(thread_task_runner); + auto platform_view = std::make_unique( + /*delegate=*/mock_delegate, + /*rendering_api=*/mock_delegate.settings_.enable_impeller + ? flutter::IOSRenderingAPI::kMetal + : flutter::IOSRenderingAPI::kSoftware, + /*platform_views_controller=*/flutterPlatformViewsController, + /*task_runners=*/runners, + /*worker_task_runner=*/nil, + /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + + FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = + [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; + flutterPlatformViewsController->RegisterViewFactory( + factory, @"MockFlutterPlatformView", + FlutterPlatformViewGestureRecognizersBlockingPolicyEager); + FlutterResult result = ^(id result) { + }; + flutterPlatformViewsController->OnMethodCall( + [FlutterMethodCall + methodCallWithMethodName:@"create" + arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], + result); + + XCTAssertNotNil(gMockPlatformView); + + UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 30, 30)]; + flutterPlatformViewsController->SetFlutterView(mockFlutterView); + // Create embedded view params + flutter::MutatorsStack stack; + // Layer tree always pushes a screen scale factor to the stack. + SkMatrix screenScaleMatrix = + SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); + stack.PushTransform(screenScaleMatrix); + SkMatrix translateMatrix = SkMatrix::Translate(5, 5); + // The platform view's rect for this test will be (5, 5, 10, 10). + stack.PushTransform(translateMatrix); + + // Push a clip rrect, the rect of the rrect is the same as the PlatformView of the corner should. + // clip the PlatformView. + SkRect rect_for_rrect = SkRect::MakeXYWH(0, 0, 10, 10); + SkRRect rrect = SkRRect::MakeRectXY(rect_for_rrect, 1, 1); + stack.PushClipRRect(rrect); + + auto embeddedViewParams = std::make_unique( + SkMatrix::Concat(screenScaleMatrix, translateMatrix), SkSize::Make(5, 5), stack); + + flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); + flutterPlatformViewsController->CompositeEmbeddedView(2); + gMockPlatformView.backgroundColor = UIColor.redColor; + XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); + ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; + [mockFlutterView addSubview:childClippingView]; + + [mockFlutterView setNeedsLayout]; + [mockFlutterView layoutIfNeeded]; + + XCTAssertNotNil(childClippingView.maskView); +} + +- (void)testClipRect { + 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(thread_task_runner); + auto platform_view = std::make_unique( + /*delegate=*/mock_delegate, + /*rendering_api=*/mock_delegate.settings_.enable_impeller + ? flutter::IOSRenderingAPI::kMetal + : flutter::IOSRenderingAPI::kSoftware, + /*platform_views_controller=*/flutterPlatformViewsController, + /*task_runners=*/runners, + /*worker_task_runner=*/nil, + /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + + FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = + [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; + flutterPlatformViewsController->RegisterViewFactory( + factory, @"MockFlutterPlatformView", + FlutterPlatformViewGestureRecognizersBlockingPolicyEager); + FlutterResult result = ^(id result) { + }; + flutterPlatformViewsController->OnMethodCall( + [FlutterMethodCall + methodCallWithMethodName:@"create" + arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], + result); + + XCTAssertNotNil(gMockPlatformView); + + UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; + flutterPlatformViewsController->SetFlutterView(mockFlutterView); + // Create embedded view params + flutter::MutatorsStack stack; + // Layer tree always pushes a screen scale factor to the stack + SkMatrix screenScaleMatrix = + SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); + stack.PushTransform(screenScaleMatrix); + // Push a clip rect + SkRect rect = SkRect::MakeXYWH(2, 2, 3, 3); + stack.PushClipRect(rect); + + auto embeddedViewParams = + std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); + + flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); + flutterPlatformViewsController->CompositeEmbeddedView(2); + gMockPlatformView.backgroundColor = UIColor.redColor; + XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); + ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; + [mockFlutterView addSubview:childClippingView]; + + [mockFlutterView setNeedsLayout]; + [mockFlutterView layoutIfNeeded]; + + CGRect insideClipping = CGRectMake(2, 2, 3, 3); + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + CGPoint point = CGPointMake(i, j); + int alpha = [self alphaOfPoint:CGPointMake(i, j) onView:mockFlutterView]; + if (CGRectContainsPoint(insideClipping, point)) { + XCTAssertEqual(alpha, 255); + } else { + XCTAssertEqual(alpha, 0); + } + } + } +} + +- (void)testClipRRect { + 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(thread_task_runner); + auto platform_view = std::make_unique( + /*delegate=*/mock_delegate, + /*rendering_api=*/mock_delegate.settings_.enable_impeller + ? flutter::IOSRenderingAPI::kMetal + : flutter::IOSRenderingAPI::kSoftware, + /*platform_views_controller=*/flutterPlatformViewsController, + /*task_runners=*/runners, + /*worker_task_runner=*/nil, + /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + + FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = + [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; + flutterPlatformViewsController->RegisterViewFactory( + factory, @"MockFlutterPlatformView", + FlutterPlatformViewGestureRecognizersBlockingPolicyEager); + FlutterResult result = ^(id result) { + }; + flutterPlatformViewsController->OnMethodCall( + [FlutterMethodCall + methodCallWithMethodName:@"create" + arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], + result); + + XCTAssertNotNil(gMockPlatformView); + + UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; + flutterPlatformViewsController->SetFlutterView(mockFlutterView); + // Create embedded view params + flutter::MutatorsStack stack; + // Layer tree always pushes a screen scale factor to the stack + SkMatrix screenScaleMatrix = + SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); + stack.PushTransform(screenScaleMatrix); + // Push a clip rrect + SkRRect rrect = SkRRect::MakeRectXY(SkRect::MakeXYWH(2, 2, 6, 6), 1, 1); + stack.PushClipRRect(rrect); + + auto embeddedViewParams = + std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); + + flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); + flutterPlatformViewsController->CompositeEmbeddedView(2); + gMockPlatformView.backgroundColor = UIColor.redColor; + XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); + ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; + [mockFlutterView addSubview:childClippingView]; + + [mockFlutterView setNeedsLayout]; + [mockFlutterView layoutIfNeeded]; + + /* + ClippingMask outterClipping + 2 3 4 5 6 7 2 3 4 5 6 7 + 2 / - - - - \ 2 + - - - - + + 3 | | 3 | | + 4 | | 4 | | + 5 | | 5 | | + 6 | | 6 | | + 7 \ - - - - / 7 + - - - - + + + innerClipping1 innerClipping2 + 2 3 4 5 6 7 2 3 4 5 6 7 + 2 + - - + 2 + 3 | | 3 + - - - - + + 4 | | 4 | | + 5 | | 5 | | + 6 | | 6 + - - - - + + 7 + - - + 7 + */ + CGRect innerClipping1 = CGRectMake(3, 2, 4, 6); + CGRect innerClipping2 = CGRectMake(2, 3, 6, 4); + CGRect outterClipping = CGRectMake(2, 2, 6, 6); + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + CGPoint point = CGPointMake(i, j); + int alpha = [self alphaOfPoint:CGPointMake(i, j) onView:mockFlutterView]; + if (CGRectContainsPoint(innerClipping1, point) || + CGRectContainsPoint(innerClipping2, point)) { + // Pixels inside either of the 2 inner clippings should be fully opaque. + XCTAssertEqual(alpha, 255); + } else if (CGRectContainsPoint(outterClipping, point)) { + // Corner pixels (i.e. (2, 2), (2, 7), (7, 2) and (7, 7)) should be partially transparent. + XCTAssert(0 < alpha && alpha < 255); + } else { + // Pixels outside outterClipping should be fully transparent. + XCTAssertEqual(alpha, 0); + } + } + } +} + +- (void)testClipPath { + 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(thread_task_runner); + auto platform_view = std::make_unique( + /*delegate=*/mock_delegate, + /*rendering_api=*/mock_delegate.settings_.enable_impeller + ? flutter::IOSRenderingAPI::kMetal + : flutter::IOSRenderingAPI::kSoftware, + /*platform_views_controller=*/flutterPlatformViewsController, + /*task_runners=*/runners, + /*worker_task_runner=*/nil, + /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + + FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = + [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; + flutterPlatformViewsController->RegisterViewFactory( + factory, @"MockFlutterPlatformView", + FlutterPlatformViewGestureRecognizersBlockingPolicyEager); + FlutterResult result = ^(id result) { + }; + flutterPlatformViewsController->OnMethodCall( + [FlutterMethodCall + methodCallWithMethodName:@"create" + arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], + result); + + XCTAssertNotNil(gMockPlatformView); + + UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; + flutterPlatformViewsController->SetFlutterView(mockFlutterView); + // Create embedded view params + flutter::MutatorsStack stack; + // Layer tree always pushes a screen scale factor to the stack + SkMatrix screenScaleMatrix = + SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); + stack.PushTransform(screenScaleMatrix); + // Push a clip path + SkPath path; + path.addRoundRect(SkRect::MakeXYWH(2, 2, 6, 6), 1, 1); + stack.PushClipPath(path); + + auto embeddedViewParams = + std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); + + flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); + flutterPlatformViewsController->CompositeEmbeddedView(2); + gMockPlatformView.backgroundColor = UIColor.redColor; + XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); + ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; + [mockFlutterView addSubview:childClippingView]; + + [mockFlutterView setNeedsLayout]; + [mockFlutterView layoutIfNeeded]; + + /* + ClippingMask outterClipping + 2 3 4 5 6 7 2 3 4 5 6 7 + 2 / - - - - \ 2 + - - - - + + 3 | | 3 | | + 4 | | 4 | | + 5 | | 5 | | + 6 | | 6 | | + 7 \ - - - - / 7 + - - - - + + + innerClipping1 innerClipping2 + 2 3 4 5 6 7 2 3 4 5 6 7 + 2 + - - + 2 + 3 | | 3 + - - - - + + 4 | | 4 | | + 5 | | 5 | | + 6 | | 6 + - - - - + + 7 + - - + 7 + */ + CGRect innerClipping1 = CGRectMake(3, 2, 4, 6); + CGRect innerClipping2 = CGRectMake(2, 3, 6, 4); + CGRect outterClipping = CGRectMake(2, 2, 6, 6); + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + CGPoint point = CGPointMake(i, j); + int alpha = [self alphaOfPoint:CGPointMake(i, j) onView:mockFlutterView]; + if (CGRectContainsPoint(innerClipping1, point) || + CGRectContainsPoint(innerClipping2, point)) { + // Pixels inside either of the 2 inner clippings should be fully opaque. + XCTAssertEqual(alpha, 255); + } else if (CGRectContainsPoint(outterClipping, point)) { + // Corner pixels (i.e. (2, 2), (2, 7), (7, 2) and (7, 7)) should be partially transparent. + XCTAssert(0 < alpha && alpha < 255); + } else { + // Pixels outside outterClipping should be fully transparent. + XCTAssertEqual(alpha, 0); + } + } + } +} + +- (void)testSetFlutterViewControllerAfterCreateCanStillDispatchTouchEvents { + 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(thread_task_runner); + auto platform_view = std::make_unique( + /*delegate=*/mock_delegate, + /*rendering_api=*/mock_delegate.settings_.enable_impeller + ? flutter::IOSRenderingAPI::kMetal + : flutter::IOSRenderingAPI::kSoftware, + /*platform_views_controller=*/flutterPlatformViewsController, + /*task_runners=*/runners, + /*worker_task_runner=*/nil, + /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + + FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = + [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; + flutterPlatformViewsController->RegisterViewFactory( + factory, @"MockFlutterPlatformView", + FlutterPlatformViewGestureRecognizersBlockingPolicyEager); + FlutterResult result = ^(id result) { + }; + flutterPlatformViewsController->OnMethodCall( + [FlutterMethodCall + methodCallWithMethodName:@"create" + arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], + result); + + XCTAssertNotNil(gMockPlatformView); + + // Find touch inteceptor view + UIView* touchInteceptorView = gMockPlatformView; + while (touchInteceptorView != nil && + ![touchInteceptorView isKindOfClass:[FlutterTouchInterceptingView class]]) { + touchInteceptorView = touchInteceptorView.superview; + } + XCTAssertNotNil(touchInteceptorView); + + // Find ForwardGestureRecognizer + UIGestureRecognizer* forwardGectureRecognizer = nil; + for (UIGestureRecognizer* gestureRecognizer in touchInteceptorView.gestureRecognizers) { + if ([gestureRecognizer isKindOfClass:NSClassFromString(@"ForwardingGestureRecognizer")]) { + forwardGectureRecognizer = gestureRecognizer; + break; + } + } + + // Before setting flutter view controller, events are not dispatched. + NSSet* touches1 = [[NSSet alloc] init]; + id event1 = OCMClassMock([UIEvent class]); + id mockFlutterViewContoller = OCMClassMock([FlutterViewController class]); + [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1]; + OCMReject([mockFlutterViewContoller touchesBegan:touches1 withEvent:event1]); + + // Set flutter view controller allows events to be dispatched. + NSSet* touches2 = [[NSSet alloc] init]; + id event2 = OCMClassMock([UIEvent class]); + flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller); + [forwardGectureRecognizer touchesBegan:touches2 withEvent:event2]; + OCMVerify([mockFlutterViewContoller touchesBegan:touches2 withEvent:event2]); +} + +- (void)testSetFlutterViewControllerInTheMiddleOfTouchEventShouldStillAllowGesturesToBeHandled { + 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(thread_task_runner); + auto platform_view = std::make_unique( + /*delegate=*/mock_delegate, + /*rendering_api=*/mock_delegate.settings_.enable_impeller + ? flutter::IOSRenderingAPI::kMetal + : flutter::IOSRenderingAPI::kSoftware, + /*platform_views_controller=*/flutterPlatformViewsController, + /*task_runners=*/runners, + /*worker_task_runner=*/nil, + /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + + FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = + [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; + flutterPlatformViewsController->RegisterViewFactory( + factory, @"MockFlutterPlatformView", + FlutterPlatformViewGestureRecognizersBlockingPolicyEager); + FlutterResult result = ^(id result) { + }; + flutterPlatformViewsController->OnMethodCall( + [FlutterMethodCall + methodCallWithMethodName:@"create" + arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], + result); + + XCTAssertNotNil(gMockPlatformView); + + // Find touch inteceptor view + UIView* touchInteceptorView = gMockPlatformView; + while (touchInteceptorView != nil && + ![touchInteceptorView isKindOfClass:[FlutterTouchInterceptingView class]]) { + touchInteceptorView = touchInteceptorView.superview; + } + XCTAssertNotNil(touchInteceptorView); + + // Find ForwardGestureRecognizer + UIGestureRecognizer* forwardGectureRecognizer = nil; + for (UIGestureRecognizer* gestureRecognizer in touchInteceptorView.gestureRecognizers) { + if ([gestureRecognizer isKindOfClass:NSClassFromString(@"ForwardingGestureRecognizer")]) { + forwardGectureRecognizer = gestureRecognizer; + break; + } + } + id mockFlutterViewContoller = OCMClassMock([FlutterViewController class]); + { + // ***** Sequence 1, finishing touch event with touchEnded ***** // + flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller); + + NSSet* touches1 = [[NSSet alloc] init]; + id event1 = OCMClassMock([UIEvent class]); + [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1]; + OCMVerify([mockFlutterViewContoller touchesBegan:touches1 withEvent:event1]); + + flutterPlatformViewsController->SetFlutterViewController(nil); + + // Allow the touch events to finish + NSSet* touches2 = [[NSSet alloc] init]; + id event2 = OCMClassMock([UIEvent class]); + [forwardGectureRecognizer touchesMoved:touches2 withEvent:event2]; + OCMVerify([mockFlutterViewContoller touchesMoved:touches2 withEvent:event2]); + + NSSet* touches3 = [[NSSet alloc] init]; + id event3 = OCMClassMock([UIEvent class]); + [forwardGectureRecognizer touchesEnded:touches3 withEvent:event3]; + OCMVerify([mockFlutterViewContoller touchesEnded:touches3 withEvent:event3]); + + // Now the 2nd touch sequence should not be allowed. + NSSet* touches4 = [[NSSet alloc] init]; + id event4 = OCMClassMock([UIEvent class]); + [forwardGectureRecognizer touchesBegan:touches4 withEvent:event4]; + OCMReject([mockFlutterViewContoller touchesBegan:touches4 withEvent:event4]); + + NSSet* touches5 = [[NSSet alloc] init]; + id event5 = OCMClassMock([UIEvent class]); + [forwardGectureRecognizer touchesEnded:touches5 withEvent:event5]; + OCMReject([mockFlutterViewContoller touchesEnded:touches5 withEvent:event5]); + } + + { + // ***** Sequence 2, finishing touch event with touchCancelled ***** // + flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller); + + NSSet* touches1 = [[NSSet alloc] init]; + id event1 = OCMClassMock([UIEvent class]); + [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1]; + OCMVerify([mockFlutterViewContoller touchesBegan:touches1 withEvent:event1]); + + flutterPlatformViewsController->SetFlutterViewController(nil); + + // Allow the touch events to finish + NSSet* touches2 = [[NSSet alloc] init]; + id event2 = OCMClassMock([UIEvent class]); + [forwardGectureRecognizer touchesMoved:touches2 withEvent:event2]; + OCMVerify([mockFlutterViewContoller touchesMoved:touches2 withEvent:event2]); + + NSSet* touches3 = [[NSSet alloc] init]; + id event3 = OCMClassMock([UIEvent class]); + [forwardGectureRecognizer touchesCancelled:touches3 withEvent:event3]; + OCMVerify([mockFlutterViewContoller forceTouchesCancelled:touches3]); + + // Now the 2nd touch sequence should not be allowed. + NSSet* touches4 = [[NSSet alloc] init]; + id event4 = OCMClassMock([UIEvent class]); + [forwardGectureRecognizer touchesBegan:touches4 withEvent:event4]; + OCMReject([mockFlutterViewContoller touchesBegan:touches4 withEvent:event4]); + + NSSet* touches5 = [[NSSet alloc] init]; + id event5 = OCMClassMock([UIEvent class]); + [forwardGectureRecognizer touchesEnded:touches5 withEvent:event5]; + OCMReject([mockFlutterViewContoller touchesEnded:touches5 withEvent:event5]); + } + + flutterPlatformViewsController->Reset(); +} + +- (void) + testSetFlutterViewControllerInTheMiddleOfTouchEventAllowsTheNewControllerToHandleSecondTouchSequence { + 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(thread_task_runner); + auto platform_view = std::make_unique( + /*delegate=*/mock_delegate, + /*rendering_api=*/mock_delegate.settings_.enable_impeller + ? flutter::IOSRenderingAPI::kMetal + : flutter::IOSRenderingAPI::kSoftware, + /*platform_views_controller=*/flutterPlatformViewsController, + /*task_runners=*/runners, + /*worker_task_runner=*/nil, + /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + + FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = + [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; + flutterPlatformViewsController->RegisterViewFactory( + factory, @"MockFlutterPlatformView", + FlutterPlatformViewGestureRecognizersBlockingPolicyEager); + FlutterResult result = ^(id result) { + }; + flutterPlatformViewsController->OnMethodCall( + [FlutterMethodCall + methodCallWithMethodName:@"create" + arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], + result); + + XCTAssertNotNil(gMockPlatformView); + + // Find touch inteceptor view + UIView* touchInteceptorView = gMockPlatformView; + while (touchInteceptorView != nil && + ![touchInteceptorView isKindOfClass:[FlutterTouchInterceptingView class]]) { + touchInteceptorView = touchInteceptorView.superview; + } + XCTAssertNotNil(touchInteceptorView); + + // Find ForwardGestureRecognizer + UIGestureRecognizer* forwardGectureRecognizer = nil; + for (UIGestureRecognizer* gestureRecognizer in touchInteceptorView.gestureRecognizers) { + if ([gestureRecognizer isKindOfClass:NSClassFromString(@"ForwardingGestureRecognizer")]) { + forwardGectureRecognizer = gestureRecognizer; + break; + } + } + id mockFlutterViewContoller = OCMClassMock([FlutterViewController class]); + + flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller); + + // The touches in this sequence requires 1 touch object, we always create the NSSet with one item. + NSSet* touches1 = [NSSet setWithObject:@1]; + id event1 = OCMClassMock([UIEvent class]); + [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1]; + OCMVerify([mockFlutterViewContoller touchesBegan:touches1 withEvent:event1]); + + FlutterViewController* mockFlutterViewContoller2 = OCMClassMock([FlutterViewController class]); + flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller2); + + // Touch events should still send to the old FlutterViewController if FlutterViewController + // is updated in between. + NSSet* touches2 = [NSSet setWithObject:@1]; + id event2 = OCMClassMock([UIEvent class]); + [forwardGectureRecognizer touchesBegan:touches2 withEvent:event2]; + OCMVerify([mockFlutterViewContoller touchesBegan:touches2 withEvent:event2]); + OCMReject([mockFlutterViewContoller2 touchesBegan:touches2 withEvent:event2]); + + NSSet* touches3 = [NSSet setWithObject:@1]; + id event3 = OCMClassMock([UIEvent class]); + [forwardGectureRecognizer touchesMoved:touches3 withEvent:event3]; + OCMVerify([mockFlutterViewContoller touchesMoved:touches3 withEvent:event3]); + OCMReject([mockFlutterViewContoller2 touchesMoved:touches3 withEvent:event3]); + + NSSet* touches4 = [NSSet setWithObject:@1]; + id event4 = OCMClassMock([UIEvent class]); + [forwardGectureRecognizer touchesEnded:touches4 withEvent:event4]; + OCMVerify([mockFlutterViewContoller touchesEnded:touches4 withEvent:event4]); + OCMReject([mockFlutterViewContoller2 touchesEnded:touches4 withEvent:event4]); + + NSSet* touches5 = [NSSet setWithObject:@1]; + id event5 = OCMClassMock([UIEvent class]); + [forwardGectureRecognizer touchesEnded:touches5 withEvent:event5]; + OCMVerify([mockFlutterViewContoller touchesEnded:touches5 withEvent:event5]); + OCMReject([mockFlutterViewContoller2 touchesEnded:touches5 withEvent:event5]); + + // Now the 2nd touch sequence should go to the new FlutterViewController + + NSSet* touches6 = [NSSet setWithObject:@1]; + id event6 = OCMClassMock([UIEvent class]); + [forwardGectureRecognizer touchesBegan:touches6 withEvent:event6]; + OCMVerify([mockFlutterViewContoller2 touchesBegan:touches6 withEvent:event6]); + OCMReject([mockFlutterViewContoller touchesBegan:touches6 withEvent:event6]); + + // Allow the touch events to finish + NSSet* touches7 = [NSSet setWithObject:@1]; + id event7 = OCMClassMock([UIEvent class]); + [forwardGectureRecognizer touchesMoved:touches7 withEvent:event7]; + OCMVerify([mockFlutterViewContoller2 touchesMoved:touches7 withEvent:event7]); + OCMReject([mockFlutterViewContoller touchesMoved:touches7 withEvent:event7]); + + NSSet* touches8 = [NSSet setWithObject:@1]; + id event8 = OCMClassMock([UIEvent class]); + [forwardGectureRecognizer touchesEnded:touches8 withEvent:event8]; + OCMVerify([mockFlutterViewContoller2 touchesEnded:touches8 withEvent:event8]); + OCMReject([mockFlutterViewContoller touchesEnded:touches8 withEvent:event8]); + + flutterPlatformViewsController->Reset(); +} + +- (void)testFlutterPlatformViewTouchesCancelledEventAreForcedToBeCancelled { + 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(thread_task_runner); + auto platform_view = std::make_unique( + /*delegate=*/mock_delegate, + /*rendering_api=*/mock_delegate.settings_.enable_impeller + ? flutter::IOSRenderingAPI::kMetal + : flutter::IOSRenderingAPI::kSoftware, + /*platform_views_controller=*/flutterPlatformViewsController, + /*task_runners=*/runners, + /*worker_task_runner=*/nil, + /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + + FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = + [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; + flutterPlatformViewsController->RegisterViewFactory( + factory, @"MockFlutterPlatformView", + FlutterPlatformViewGestureRecognizersBlockingPolicyEager); + FlutterResult result = ^(id result) { + }; + flutterPlatformViewsController->OnMethodCall( + [FlutterMethodCall + methodCallWithMethodName:@"create" + arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], + result); + + XCTAssertNotNil(gMockPlatformView); + + // Find touch inteceptor view + UIView* touchInteceptorView = gMockPlatformView; + while (touchInteceptorView != nil && + ![touchInteceptorView isKindOfClass:[FlutterTouchInterceptingView class]]) { + touchInteceptorView = touchInteceptorView.superview; + } + XCTAssertNotNil(touchInteceptorView); + + // Find ForwardGestureRecognizer + UIGestureRecognizer* forwardGectureRecognizer = nil; + for (UIGestureRecognizer* gestureRecognizer in touchInteceptorView.gestureRecognizers) { + if ([gestureRecognizer isKindOfClass:NSClassFromString(@"ForwardingGestureRecognizer")]) { + forwardGectureRecognizer = gestureRecognizer; + break; + } + } + id mockFlutterViewContoller = OCMClassMock([FlutterViewController class]); + + flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller); + + NSSet* touches1 = [NSSet setWithObject:@1]; + id event1 = OCMClassMock([UIEvent class]); + [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1]; + + [forwardGectureRecognizer touchesCancelled:touches1 withEvent:event1]; + OCMVerify([mockFlutterViewContoller forceTouchesCancelled:touches1]); + + flutterPlatformViewsController->Reset(); +} + +- (void)testFlutterPlatformViewControllerSubmitFrameWithoutFlutterViewNotCrashing { + 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(thread_task_runner); + auto platform_view = std::make_unique( + /*delegate=*/mock_delegate, + /*rendering_api=*/mock_delegate.settings_.enable_impeller + ? flutter::IOSRenderingAPI::kMetal + : flutter::IOSRenderingAPI::kSoftware, + /*platform_views_controller=*/flutterPlatformViewsController, + /*task_runners=*/runners, + /*worker_task_runner=*/nil, + /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + + FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = + [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; + flutterPlatformViewsController->RegisterViewFactory( + factory, @"MockFlutterPlatformView", + FlutterPlatformViewGestureRecognizersBlockingPolicyEager); + FlutterResult result = ^(id result) { + }; + flutterPlatformViewsController->OnMethodCall( + [FlutterMethodCall + methodCallWithMethodName:@"create" + arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], + result); + + XCTAssertNotNil(gMockPlatformView); + + // Create embedded view params + flutter::MutatorsStack stack; + SkMatrix finalMatrix; + + auto embeddedViewParams_1 = + std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); + + flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams_1)); + flutterPlatformViewsController->CompositeEmbeddedView(2); + flutter::SurfaceFrame::FramebufferInfo framebuffer_info; + auto mock_surface = std::make_unique( + nullptr, framebuffer_info, + [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return false; }, + /*frame_size=*/SkISize::Make(800, 600)); + XCTAssertFalse( + flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface))); + + auto embeddedViewParams_2 = + std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); + flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams_2)); + flutterPlatformViewsController->CompositeEmbeddedView(2); + auto mock_surface_submit_true = std::make_unique( + nullptr, 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_submit_true))); +} + +- (void) + testFlutterPlatformViewControllerResetDeallocsPlatformViewWhenRootViewsNotBindedToFlutterView { + 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(thread_task_runner); + auto platform_view = std::make_unique( + /*delegate=*/mock_delegate, + /*rendering_api=*/mock_delegate.settings_.enable_impeller + ? flutter::IOSRenderingAPI::kMetal + : flutter::IOSRenderingAPI::kSoftware, + /*platform_views_controller=*/flutterPlatformViewsController, + /*task_runners=*/runners, + /*worker_task_runner=*/nil, + /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + + UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; + flutterPlatformViewsController->SetFlutterView(mockFlutterView); + + FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = + [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; + flutterPlatformViewsController->RegisterViewFactory( + factory, @"MockFlutterPlatformView", + FlutterPlatformViewGestureRecognizersBlockingPolicyEager); + FlutterResult result = ^(id result) { + }; + // autorelease pool to trigger an autorelease for all the root_views_ and touch_interceptors_. + @autoreleasepool { + flutterPlatformViewsController->OnMethodCall( + [FlutterMethodCall + methodCallWithMethodName:@"create" + arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], + result); + + flutter::MutatorsStack stack; + SkMatrix finalMatrix; + auto embeddedViewParams = + std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); + flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); + flutterPlatformViewsController->CompositeEmbeddedView(2); + // Not calling |flutterPlatformViewsController::SubmitFrame| so that the platform views are not + // added to flutter_view_. + + XCTAssertNotNil(gMockPlatformView); + flutterPlatformViewsController->Reset(); + } + XCTAssertNil(gMockPlatformView); +} + +- (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { + 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(thread_task_runner); + auto platform_view = std::make_unique( + /*delegate=*/mock_delegate, + /*rendering_api=*/mock_delegate.settings_.enable_impeller + ? flutter::IOSRenderingAPI::kMetal + : flutter::IOSRenderingAPI::kSoftware, + /*platform_views_controller=*/flutterPlatformViewsController, + /*task_runners=*/runners, + /*worker_task_runner=*/nil, + /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + + UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; + flutterPlatformViewsController->SetFlutterView(mockFlutterView); + + FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = + [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; + flutterPlatformViewsController->RegisterViewFactory( + factory, @"MockFlutterPlatformView", + FlutterPlatformViewGestureRecognizersBlockingPolicyEager); + FlutterResult result = ^(id result) { + }; + + flutterPlatformViewsController->OnMethodCall( + [FlutterMethodCall + methodCallWithMethodName:@"create" + arguments:@{@"id" : @0, @"viewType" : @"MockFlutterPlatformView"}], + result); + + // First frame, |EmbeddedViewCount| is not empty after composite. + flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300)); + flutter::MutatorsStack stack; + SkMatrix finalMatrix; + auto embeddedViewParams1 = + std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); + flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1)); + flutterPlatformViewsController->CompositeEmbeddedView(0); + XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 1UL); + + // Second frame, |EmbeddedViewCount| should be empty at the start + flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300)); + XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 0UL); + + auto embeddedViewParams2 = + std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); + flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams2)); + flutterPlatformViewsController->CompositeEmbeddedView(0); + XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 1UL); +} + +- (void) + testFlutterPlatformViewControllerSubmitFrameShouldOrderSubviewsCorrectlyWithDifferentViewHierarchy { + 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(thread_task_runner); + auto platform_view = std::make_unique( + /*delegate=*/mock_delegate, + /*rendering_api=*/mock_delegate.settings_.enable_impeller + ? flutter::IOSRenderingAPI::kMetal + : flutter::IOSRenderingAPI::kSoftware, + /*platform_views_controller=*/flutterPlatformViewsController, + /*task_runners=*/runners, + /*worker_task_runner=*/nil, + /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + + UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; + flutterPlatformViewsController->SetFlutterView(mockFlutterView); + + FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = + [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; + 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(finalMatrix, SkSize::Make(300, 300), stack); + flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1)); + flutterPlatformViewsController->CompositeEmbeddedView(0); + auto embeddedViewParams2 = + std::make_unique(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 mock_sk_surface = SkSurfaces::Raster(image_info); + flutter::SurfaceFrame::FramebufferInfo framebuffer_info; + auto mock_surface = std::make_unique( + 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 opposite order. + embeddedViewParams2 = + std::make_unique(finalMatrix, SkSize::Make(500, 500), stack); + flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams2)); + flutterPlatformViewsController->CompositeEmbeddedView(1); + embeddedViewParams1 = + std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); + flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1)); + flutterPlatformViewsController->CompositeEmbeddedView(0); + + mock_sk_surface = SkSurfaces::Raster(image_info); + mock_surface = std::make_unique( + 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 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(thread_task_runner); + auto platform_view = std::make_unique( + /*delegate=*/mock_delegate, + /*rendering_api=*/mock_delegate.settings_.enable_impeller + ? flutter::IOSRenderingAPI::kMetal + : flutter::IOSRenderingAPI::kSoftware, + /*platform_views_controller=*/flutterPlatformViewsController, + /*task_runners=*/runners, + /*worker_task_runner=*/nil, + /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + + UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; + flutterPlatformViewsController->SetFlutterView(mockFlutterView); + + FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = + [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; + 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(finalMatrix, SkSize::Make(300, 300), stack); + flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1)); + flutterPlatformViewsController->CompositeEmbeddedView(0); + auto embeddedViewParams2 = + std::make_unique(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 mock_sk_surface = SkSurfaces::Raster(image_info); + flutter::SurfaceFrame::FramebufferInfo framebuffer_info; + auto mock_surface = std::make_unique( + 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(finalMatrix, SkSize::Make(300, 300), stack); + flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1)); + flutterPlatformViewsController->CompositeEmbeddedView(0); + embeddedViewParams2 = + std::make_unique(finalMatrix, SkSize::Make(500, 500), stack); + flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams2)); + flutterPlatformViewsController->CompositeEmbeddedView(1); + + mock_sk_surface = SkSurfaces::Raster(image_info); + mock_surface = std::make_unique( + 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"); + auto thread_task_runner_other = CreateNewThread("FlutterPlatformViewsTest2"); + flutter::TaskRunners runners(/*label=*/self.name.UTF8String, + /*platform=*/thread_task_runner_platform, + /*raster=*/thread_task_runner_other, + /*ui=*/thread_task_runner_other, + /*io=*/thread_task_runner_other); + auto flutterPlatformViewsController = + std::make_shared(thread_task_runner_platform); + auto platform_view = std::make_unique( + /*delegate=*/mock_delegate, + /*rendering_api=*/mock_delegate.settings_.enable_impeller + ? flutter::IOSRenderingAPI::kMetal + : flutter::IOSRenderingAPI::kSoftware, + /*platform_views_controller=*/flutterPlatformViewsController, + /*task_runners=*/runners, + /*worker_task_runner=*/nil, + /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + + UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; + flutterPlatformViewsController->SetFlutterView(mockFlutterView); + + FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = + [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; + flutterPlatformViewsController->RegisterViewFactory( + factory, @"MockFlutterPlatformView", + FlutterPlatformViewGestureRecognizersBlockingPolicyEager); + XCTestExpectation* waitForPlatformView = + [self expectationWithDescription:@"wait for platform view to be created"]; + FlutterResult result = ^(id result) { + [waitForPlatformView fulfill]; + }; + + flutterPlatformViewsController->OnMethodCall( + [FlutterMethodCall + methodCallWithMethodName:@"create" + arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], + result); + [self waitForExpectations:@[ waitForPlatformView ] timeout:30]; + XCTAssertNotNil(gMockPlatformView); + + flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300)); + SkMatrix finalMatrix; + flutter::MutatorsStack stack; + auto embeddedViewParams = + std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); + flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); + + fml::RefPtr raster_thread_merger = + fml::MakeRefCounted(thread_task_runner_platform->GetTaskQueueId(), + thread_task_runner_other->GetTaskQueueId()); + XCTAssertEqual(flutterPlatformViewsController->PostPrerollAction(raster_thread_merger), + flutter::PostPrerollResult::kSkipAndRetryFrame); + XCTAssertFalse(raster_thread_merger->IsMerged()); + + flutterPlatformViewsController->EndFrame(true, raster_thread_merger); + XCTAssertTrue(raster_thread_merger->IsMerged()); + + // Unmerge threads before the end of the test + // TaskRunners are required to be unmerged before destruction. + while (raster_thread_merger->DecrementLease() != fml::RasterThreadStatus::kUnmergedNow) { + } +} + +- (int)alphaOfPoint:(CGPoint)point onView:(UIView*)view { + unsigned char pixel[4] = {0}; + + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + + // Draw the pixel on `point` in the context. + CGContextRef context = CGBitmapContextCreate( + pixel, 1, 1, 8, 4, colorSpace, kCGBitmapAlphaInfoMask & kCGImageAlphaPremultipliedLast); + CGContextTranslateCTM(context, -point.x, -point.y); + [view.layer renderInContext:context]; + + CGContextRelease(context); + CGColorSpaceRelease(colorSpace); + // Get the alpha from the pixel that we just rendered. + return pixel[3]; +} + +- (void)testHasFirstResponderInViewHierarchySubtree_viewItselfBecomesFirstResponder { + // For view to become the first responder, it must be a descendant of a UIWindow + UIWindow* window = [[UIWindow alloc] init]; + UITextField* textField = [[UITextField alloc] init]; + [window addSubview:textField]; + + [textField becomeFirstResponder]; + XCTAssertTrue(textField.isFirstResponder); + XCTAssertTrue(textField.flt_hasFirstResponderInViewHierarchySubtree); + [textField resignFirstResponder]; + XCTAssertFalse(textField.isFirstResponder); + XCTAssertFalse(textField.flt_hasFirstResponderInViewHierarchySubtree); +} + +- (void)testHasFirstResponderInViewHierarchySubtree_descendantViewBecomesFirstResponder { + // For view to become the first responder, it must be a descendant of a UIWindow + UIWindow* window = [[UIWindow alloc] init]; + UIView* view = [[UIView alloc] init]; + UIView* childView = [[UIView alloc] init]; + UITextField* textField = [[UITextField alloc] init]; + [window addSubview:view]; + [view addSubview:childView]; + [childView addSubview:textField]; + + [textField becomeFirstResponder]; + XCTAssertTrue(textField.isFirstResponder); + XCTAssertTrue(view.flt_hasFirstResponderInViewHierarchySubtree); + [textField resignFirstResponder]; + XCTAssertFalse(textField.isFirstResponder); + XCTAssertFalse(view.flt_hasFirstResponderInViewHierarchySubtree); +} + +- (void)testFlutterClippingMaskViewPoolReuseViewsAfterRecycle { + FlutterClippingMaskViewPool* pool = [[FlutterClippingMaskViewPool alloc] initWithCapacity:2]; + FlutterClippingMaskView* view1 = [pool getMaskViewWithFrame:CGRectZero]; + FlutterClippingMaskView* view2 = [pool getMaskViewWithFrame:CGRectZero]; + [pool insertViewToPoolIfNeeded:view1]; + [pool insertViewToPoolIfNeeded:view2]; + CGRect newRect = CGRectMake(0, 0, 10, 10); + FlutterClippingMaskView* view3 = [pool getMaskViewWithFrame:newRect]; + FlutterClippingMaskView* view4 = [pool getMaskViewWithFrame:newRect]; + // view3 and view4 should randomly get either of view1 and view2. + NSSet* set1 = [NSSet setWithObjects:view1, view2, nil]; + NSSet* set2 = [NSSet setWithObjects:view3, view4, nil]; + XCTAssertEqualObjects(set1, set2); + XCTAssertTrue(CGRectEqualToRect(view3.frame, newRect)); + XCTAssertTrue(CGRectEqualToRect(view4.frame, newRect)); +} + +- (void)testFlutterClippingMaskViewPoolAllocsNewMaskViewsAfterReachingCapacity { + FlutterClippingMaskViewPool* pool = [[FlutterClippingMaskViewPool alloc] initWithCapacity:2]; + FlutterClippingMaskView* view1 = [pool getMaskViewWithFrame:CGRectZero]; + FlutterClippingMaskView* view2 = [pool getMaskViewWithFrame:CGRectZero]; + FlutterClippingMaskView* view3 = [pool getMaskViewWithFrame:CGRectZero]; + XCTAssertNotEqual(view1, view3); + XCTAssertNotEqual(view2, view3); +} + +- (void)testMaskViewsReleasedWhenPoolIsReleased { + __weak UIView* weakView; + @autoreleasepool { + FlutterClippingMaskViewPool* pool = [[FlutterClippingMaskViewPool alloc] initWithCapacity:2]; + FlutterClippingMaskView* view = [pool getMaskViewWithFrame:CGRectZero]; + weakView = view; + XCTAssertNotNil(weakView); + } + XCTAssertNil(weakView); +} + +- (void)testClipMaskViewIsReused { + 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(thread_task_runner); + auto platform_view = std::make_unique( + /*delegate=*/mock_delegate, + /*rendering_api=*/mock_delegate.settings_.enable_impeller + ? flutter::IOSRenderingAPI::kMetal + : flutter::IOSRenderingAPI::kSoftware, + /*platform_views_controller=*/flutterPlatformViewsController, + /*task_runners=*/runners, + /*worker_task_runner=*/nil, + /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + + FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = + [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; + flutterPlatformViewsController->RegisterViewFactory( + factory, @"MockFlutterPlatformView", + FlutterPlatformViewGestureRecognizersBlockingPolicyEager); + FlutterResult result = ^(id result) { + }; + flutterPlatformViewsController->OnMethodCall( + [FlutterMethodCall + methodCallWithMethodName:@"create" + arguments:@{@"id" : @1, @"viewType" : @"MockFlutterPlatformView"}], + result); + + XCTAssertNotNil(gMockPlatformView); + UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; + flutterPlatformViewsController->SetFlutterView(mockFlutterView); + // Create embedded view params + flutter::MutatorsStack stack1; + // Layer tree always pushes a screen scale factor to the stack + SkMatrix screenScaleMatrix = + SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); + stack1.PushTransform(screenScaleMatrix); + // Push a clip rect + SkRect rect = SkRect::MakeXYWH(2, 2, 3, 3); + stack1.PushClipRect(rect); + + auto embeddedViewParams1 = std::make_unique( + screenScaleMatrix, SkSize::Make(10, 10), stack1); + + flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1)); + flutterPlatformViewsController->CompositeEmbeddedView(1); + UIView* childClippingView1 = gMockPlatformView.superview.superview; + UIView* maskView1 = childClippingView1.maskView; + XCTAssertNotNil(maskView1); + + // Composite a new frame. + flutterPlatformViewsController->BeginFrame(SkISize::Make(100, 100)); + flutter::MutatorsStack stack2; + auto embeddedViewParams2 = std::make_unique( + screenScaleMatrix, SkSize::Make(10, 10), stack2); + auto embeddedViewParams3 = std::make_unique( + screenScaleMatrix, SkSize::Make(10, 10), stack2); + flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams3)); + flutterPlatformViewsController->CompositeEmbeddedView(1); + childClippingView1 = gMockPlatformView.superview.superview; + + // This overrides gMockPlatformView to point to the newly created platform view. + flutterPlatformViewsController->OnMethodCall( + [FlutterMethodCall + methodCallWithMethodName:@"create" + arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], + result); + + auto embeddedViewParams4 = std::make_unique( + screenScaleMatrix, SkSize::Make(10, 10), stack1); + flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams4)); + flutterPlatformViewsController->CompositeEmbeddedView(2); + UIView* childClippingView2 = gMockPlatformView.superview.superview; + + UIView* maskView2 = childClippingView2.maskView; + XCTAssertEqual(maskView1, maskView2); + XCTAssertNotNil(childClippingView2.maskView); + XCTAssertNil(childClippingView1.maskView); +} + +- (void)testDifferentClipMaskViewIsUsedForEachView { + 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(thread_task_runner); + auto platform_view = std::make_unique( + /*delegate=*/mock_delegate, + /*rendering_api=*/mock_delegate.settings_.enable_impeller + ? flutter::IOSRenderingAPI::kMetal + : flutter::IOSRenderingAPI::kSoftware, + /*platform_views_controller=*/flutterPlatformViewsController, + /*task_runners=*/runners, + /*worker_task_runner=*/nil, + /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + + FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = + [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; + flutterPlatformViewsController->RegisterViewFactory( + factory, @"MockFlutterPlatformView", + FlutterPlatformViewGestureRecognizersBlockingPolicyEager); + FlutterResult result = ^(id result) { + }; + + flutterPlatformViewsController->OnMethodCall( + [FlutterMethodCall + methodCallWithMethodName:@"create" + arguments:@{@"id" : @1, @"viewType" : @"MockFlutterPlatformView"}], + result); + UIView* view1 = gMockPlatformView; + + // This overwrites `gMockPlatformView` to another view. + flutterPlatformViewsController->OnMethodCall( + [FlutterMethodCall + methodCallWithMethodName:@"create" + arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], + result); + UIView* view2 = gMockPlatformView; + + XCTAssertNotNil(gMockPlatformView); + UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; + flutterPlatformViewsController->SetFlutterView(mockFlutterView); + // Create embedded view params + flutter::MutatorsStack stack1; + // Layer tree always pushes a screen scale factor to the stack + SkMatrix screenScaleMatrix = + SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); + stack1.PushTransform(screenScaleMatrix); + // Push a clip rect + SkRect rect = SkRect::MakeXYWH(2, 2, 3, 3); + stack1.PushClipRect(rect); + + auto embeddedViewParams1 = std::make_unique( + screenScaleMatrix, SkSize::Make(10, 10), stack1); + + flutter::MutatorsStack stack2; + stack2.PushClipRect(rect); + auto embeddedViewParams2 = std::make_unique( + screenScaleMatrix, SkSize::Make(10, 10), stack2); + + flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1)); + flutterPlatformViewsController->CompositeEmbeddedView(1); + UIView* childClippingView1 = view1.superview.superview; + + flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams2)); + flutterPlatformViewsController->CompositeEmbeddedView(2); + UIView* childClippingView2 = view2.superview.superview; + UIView* maskView1 = childClippingView1.maskView; + UIView* maskView2 = childClippingView2.maskView; + XCTAssertNotEqual(maskView1, maskView2); +} + +- (void)testMaskViewUsesCAShapeLayerAsTheBackingLayer { + 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(thread_task_runner); + auto platform_view = std::make_unique( + /*delegate=*/mock_delegate, + /*rendering_api=*/mock_delegate.settings_.enable_impeller + ? flutter::IOSRenderingAPI::kMetal + : flutter::IOSRenderingAPI::kSoftware, + /*platform_views_controller=*/flutterPlatformViewsController, + /*task_runners=*/runners, + /*worker_task_runner=*/nil, + /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + + FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = + [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; + flutterPlatformViewsController->RegisterViewFactory( + factory, @"MockFlutterPlatformView", + FlutterPlatformViewGestureRecognizersBlockingPolicyEager); + FlutterResult result = ^(id result) { + }; + + flutterPlatformViewsController->OnMethodCall( + [FlutterMethodCall + methodCallWithMethodName:@"create" + arguments:@{@"id" : @1, @"viewType" : @"MockFlutterPlatformView"}], + result); + + XCTAssertNotNil(gMockPlatformView); + UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; + flutterPlatformViewsController->SetFlutterView(mockFlutterView); + // Create embedded view params + flutter::MutatorsStack stack1; + // Layer tree always pushes a screen scale factor to the stack + SkMatrix screenScaleMatrix = + SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); + stack1.PushTransform(screenScaleMatrix); + // Push a clip rect + SkRect rect = SkRect::MakeXYWH(2, 2, 3, 3); + stack1.PushClipRect(rect); + + auto embeddedViewParams1 = std::make_unique( + screenScaleMatrix, SkSize::Make(10, 10), stack1); + + flutter::MutatorsStack stack2; + stack2.PushClipRect(rect); + auto embeddedViewParams2 = std::make_unique( + screenScaleMatrix, SkSize::Make(10, 10), stack2); + + flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1)); + flutterPlatformViewsController->CompositeEmbeddedView(1); + UIView* childClippingView = gMockPlatformView.superview.superview; + + UIView* maskView = childClippingView.maskView; + XCTAssert([maskView.layer isKindOfClass:[CAShapeLayer class]], + @"Mask view must use CAShapeLayer as its backing layer."); +} + +// Return true if a correct visual effect view is found. It also implies all the validation in this +// method passes. +// +// There are two fail states for this method. 1. One of the XCTAssert method failed; or 2. No +// correct visual effect view found. +- (BOOL)validateOneVisualEffectView:(UIView*)visualEffectView + expectedFrame:(CGRect)frame + inputRadius:(CGFloat)inputRadius { + XCTAssertTrue(CGRectEqualToRect(visualEffectView.frame, frame)); + for (UIView* view in visualEffectView.subviews) { + if (![NSStringFromClass([view class]) hasSuffix:@"BackdropView"]) { + continue; + } + XCTAssertEqual(view.layer.filters.count, 1u); + NSObject* filter = view.layer.filters.firstObject; + + XCTAssertEqualObjects([filter valueForKey:@"name"], @"gaussianBlur"); + + NSObject* inputRadiusInFilter = [filter valueForKey:@"inputRadius"]; + XCTAssertTrue([inputRadiusInFilter isKindOfClass:[NSNumber class]] && + flutter::BlurRadiusEqualToBlurRadius(((NSNumber*)inputRadiusInFilter).floatValue, + inputRadius)); + return YES; + } + return NO; +} + +- (void)testDisposingViewInCompositionOrderDoNotCrash { + 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(thread_task_runner); + auto platform_view = std::make_unique( + /*delegate=*/mock_delegate, + /*rendering_api=*/mock_delegate.settings_.enable_impeller + ? flutter::IOSRenderingAPI::kMetal + : flutter::IOSRenderingAPI::kSoftware, + /*platform_views_controller=*/flutterPlatformViewsController, + /*task_runners=*/runners, + /*worker_task_runner=*/nil, + /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + + UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; + flutterPlatformViewsController->SetFlutterView(mockFlutterView); + + FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = + [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; + flutterPlatformViewsController->RegisterViewFactory( + factory, @"MockFlutterPlatformView", + FlutterPlatformViewGestureRecognizersBlockingPolicyEager); + FlutterResult result = ^(id result) { + }; + + flutterPlatformViewsController->OnMethodCall( + [FlutterMethodCall + methodCallWithMethodName:@"create" + arguments:@{@"id" : @0, @"viewType" : @"MockFlutterPlatformView"}], + result); + flutterPlatformViewsController->OnMethodCall( + [FlutterMethodCall + methodCallWithMethodName:@"create" + arguments:@{@"id" : @1, @"viewType" : @"MockFlutterPlatformView"}], + result); + + { + // **** First frame, view id 0, 1 in the composition_order_, disposing view 0 is called. **** // + // No view should be disposed, or removed from the composition order. + flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300)); + flutter::MutatorsStack stack; + SkMatrix finalMatrix; + auto embeddedViewParams0 = + std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); + flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams0)); + flutterPlatformViewsController->CompositeEmbeddedView(0); + + auto embeddedViewParams1 = + std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); + flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1)); + flutterPlatformViewsController->CompositeEmbeddedView(1); + XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 2UL); + + XCTestExpectation* expectation = [self expectationWithDescription:@"dispose call ended."]; + FlutterResult disposeResult = ^(id result) { + [expectation fulfill]; + }; + + flutterPlatformViewsController->OnMethodCall( + [FlutterMethodCall methodCallWithMethodName:@"dispose" arguments:@0], disposeResult); + [self waitForExpectationsWithTimeout:30 handler:nil]; + + const SkImageInfo image_info = SkImageInfo::MakeN32Premul(1000, 1000); + sk_sp mock_sk_surface = SkSurfaces::Raster(image_info); + flutter::SurfaceFrame::FramebufferInfo framebuffer_info; + auto mock_surface = std::make_unique( + 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))); + + // Disposing won't remove embedded views until the view is removed from the composition_order_ + XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 2UL); + XCTAssertNotNil(flutterPlatformViewsController->GetPlatformViewByID(0)); + XCTAssertNotNil(flutterPlatformViewsController->GetPlatformViewByID(1)); + } + + { + // **** Second frame, view id 1 in the composition_order_, no disposing view is called, **** // + // View 0 is removed from the composition order in this frame, hence also disposed. + flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300)); + flutter::MutatorsStack stack; + SkMatrix finalMatrix; + auto embeddedViewParams1 = + std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); + flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1)); + flutterPlatformViewsController->CompositeEmbeddedView(1); + + const SkImageInfo image_info = SkImageInfo::MakeN32Premul(1000, 1000); + sk_sp mock_sk_surface = SkSurfaces::Raster(image_info); + flutter::SurfaceFrame::FramebufferInfo framebuffer_info; + auto mock_surface = std::make_unique( + 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))); + + // Disposing won't remove embedded views until the view is removed from the composition_order_ + XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 1UL); + XCTAssertNil(flutterPlatformViewsController->GetPlatformViewByID(0)); + XCTAssertNotNil(flutterPlatformViewsController->GetPlatformViewByID(1)); + } +} +- (void)testOnlyPlatformViewsAreRemovedWhenReset { + 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(thread_task_runner); + auto platform_view = std::make_unique( + /*delegate=*/mock_delegate, + /*rendering_api=*/mock_delegate.settings_.enable_impeller + ? flutter::IOSRenderingAPI::kMetal + : flutter::IOSRenderingAPI::kSoftware, + /*platform_views_controller=*/flutterPlatformViewsController, + /*task_runners=*/runners, + /*worker_task_runner=*/nil, + /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + + FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = + [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; + flutterPlatformViewsController->RegisterViewFactory( + factory, @"MockFlutterPlatformView", + FlutterPlatformViewGestureRecognizersBlockingPolicyEager); + FlutterResult result = ^(id result) { + }; + flutterPlatformViewsController->OnMethodCall( + [FlutterMethodCall + methodCallWithMethodName:@"create" + arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], + result); + UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; + flutterPlatformViewsController->SetFlutterView(mockFlutterView); + // Create embedded view params + flutter::MutatorsStack stack; + // Layer tree always pushes a screen scale factor to the stack + SkMatrix screenScaleMatrix = + SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); + stack.PushTransform(screenScaleMatrix); + // Push a translate matrix + SkMatrix translateMatrix = SkMatrix::Translate(100, 100); + stack.PushTransform(translateMatrix); + SkMatrix finalMatrix; + finalMatrix.setConcat(screenScaleMatrix, translateMatrix); + + auto embeddedViewParams = + std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); + + flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); + flutterPlatformViewsController->CompositeEmbeddedView(2); + + // SKSurface is required if the root FlutterView is present. + const SkImageInfo image_info = SkImageInfo::MakeN32Premul(1000, 1000); + sk_sp mock_sk_surface = SkSurfaces::Raster(image_info); + flutter::SurfaceFrame::FramebufferInfo framebuffer_info; + auto mock_surface = std::make_unique( + std::move(mock_sk_surface), framebuffer_info, + [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; }, + /*frame_size=*/SkISize::Make(800, 600)); + + flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface)); + + UIView* someView = [[UIView alloc] init]; + [mockFlutterView addSubview:someView]; + + flutterPlatformViewsController->Reset(); + XCTAssertEqual(mockFlutterView.subviews.count, 1u); + XCTAssertEqual(mockFlutterView.subviews.firstObject, someView); +} + +- (void)testFlutterTouchInterceptingViewLinksToAccessibilityContainer { + FlutterTouchInterceptingView* touchInteceptorView = [[FlutterTouchInterceptingView alloc] init]; + NSObject* container = [[NSObject alloc] init]; + [touchInteceptorView setFlutterAccessibilityContainer:container]; + XCTAssertEqualObjects([touchInteceptorView accessibilityContainer], container); +} + +@end diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index 9f9eec98726ae..2c21fca0f694f 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -6,6 +6,7 @@ #define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERPLATFORMVIEWS_INTERNAL_H_ #import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlatformViews.h" +#include "fml/task_runner.h" #include "impeller/base/thread_safety.h" #include "third_party/skia/include/core/SkRect.h" @@ -212,7 +213,7 @@ class FlutterPlatformViewLayerPool { class FlutterPlatformViewsController { public: - FlutterPlatformViewsController(); + FlutterPlatformViewsController(const fml::RefPtr& platform_task_runner); ~FlutterPlatformViewsController(); @@ -271,8 +272,7 @@ class FlutterPlatformViewsController { bool SubmitFrame(GrDirectContext* gr_context, const std::shared_ptr& ios_context, - std::unique_ptr frame, - fml::RefPtr platform_task_runner); + std::unique_ptr frame); void OnMethodCall(FlutterMethodCall* call, FlutterResult result) __attribute__((cf_audited_transfer)); @@ -402,9 +402,6 @@ class FlutterPlatformViewsController { std::map gesture_recognizers_blocking_policies_; - // WeakPtrFactory must be the last member. - std::unique_ptr> weak_factory_; - #if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG // A set to keep track of embedded views that does not have (0, 0) origin. // An insertion triggers a warning message about non-zero origin logged on the debug console. @@ -412,6 +409,11 @@ class FlutterPlatformViewsController { std::unordered_set non_zero_origin_views_; #endif + fml::RefPtr platform_task_runner_; + + // WeakPtrFactory must be the last member. + std::unique_ptr> weak_factory_; + FML_DISALLOW_COPY_AND_ASSIGN(FlutterPlatformViewsController); }; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm index c11bd1da28500..b3a1083048a68 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm @@ -27,8 +27,10 @@ FlutterPlatformViewLayer::~FlutterPlatformViewLayer() = default; -FlutterPlatformViewsController::FlutterPlatformViewsController() +FlutterPlatformViewsController::FlutterPlatformViewsController( + const fml::RefPtr& platform_task_runner) : layer_pool_(std::make_unique()), + platform_task_runner_(platform_task_runner), weak_factory_(std::make_unique>(this)) { mask_view_pool_.reset( [[FlutterClippingMaskViewPool alloc] initWithCapacity:kFlutterClippingMaskViewPoolCapacity]); diff --git a/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm b/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm index ec823293681c8..b5f4e001e2802 100644 --- a/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm +++ b/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm @@ -280,7 +280,7 @@ - (void)testSemanticsDeallocated { /*io=*/thread_task_runner); auto flutterPlatformViewsController = - std::make_shared(); + std::make_shared(thread_task_runner); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -341,7 +341,7 @@ - (void)testSemanticsDeallocatedWithoutLoadingView { /*io=*/thread_task_runner); auto flutterPlatformViewsController = - std::make_shared(); + std::make_shared(thread_task_runner); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -387,7 +387,8 @@ - (void)testReplacedSemanticsDoesNotCleanupChildren { /*ui=*/thread_task_runner, /*io=*/thread_task_runner); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = + std::make_shared(thread_task_runner); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -483,7 +484,8 @@ - (void)testScrollableSemanticsDeallocated { /*ui=*/thread_task_runner, /*io=*/thread_task_runner); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = + std::make_shared(thread_task_runner); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -557,7 +559,8 @@ - (void)testBridgeReplacesSemanticsNode { /*ui=*/thread_task_runner, /*io=*/thread_task_runner); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = + std::make_shared(thread_task_runner); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -2177,7 +2180,7 @@ - (void)testPlatformViewDestructorDoesNotCallSemanticsAPIs { id mockFlutterViewController = OCMClassMock([FlutterViewController class]); auto flutterPlatformViewsController = - std::make_shared(); + std::make_shared(thread_task_runner); OCMStub([mockFlutterViewController platformViewsController]) .andReturn(flutterPlatformViewsController.get()); auto weakFactory = std::make_unique>( diff --git a/shell/platform/darwin/ios/ios_external_view_embedder.h b/shell/platform/darwin/ios/ios_external_view_embedder.h index 9118c5d277900..953966378b15a 100644 --- a/shell/platform/darwin/ios/ios_external_view_embedder.h +++ b/shell/platform/darwin/ios/ios_external_view_embedder.h @@ -57,8 +57,7 @@ class IOSExternalViewEmbedder : public ExternalViewEmbedder { int64_t flutter_view_id, GrDirectContext* context, const std::shared_ptr& aiks_context, - std::unique_ptr frame, - fml::RefPtr platform_task_runner) override; + std::unique_ptr frame) override; // |ExternalViewEmbedder| void EndFrame(bool should_resubmit_frame, diff --git a/shell/platform/darwin/ios/ios_external_view_embedder.mm b/shell/platform/darwin/ios/ios_external_view_embedder.mm index 30d13b14dae26..36501b3eaa1cc 100644 --- a/shell/platform/darwin/ios/ios_external_view_embedder.mm +++ b/shell/platform/darwin/ios/ios_external_view_embedder.mm @@ -3,6 +3,7 @@ // found in the LICENSE file. #import "flutter/shell/platform/darwin/ios/ios_external_view_embedder.h" +#include "fml/task_runner.h" #include "flutter/common/constants.h" @@ -74,14 +75,13 @@ int64_t flutter_view_id, GrDirectContext* context, const std::shared_ptr& aiks_context, - std::unique_ptr frame, - fml::RefPtr platform_task_runner) { + std::unique_ptr frame) { TRACE_EVENT0("flutter", "IOSExternalViewEmbedder::SubmitFlutterView"); // TODO(dkwingsmt): This class only supports rendering into the implicit view. // Properly support multi-view in the future. FML_DCHECK(flutter_view_id == kFlutterImplicitViewId); FML_CHECK(platform_views_controller_); - platform_views_controller_->SubmitFrame(context, ios_context_, std::move(frame), platform_task_runner); + platform_views_controller_->SubmitFrame(context, ios_context_, std::move(frame)); TRACE_EVENT0("flutter", "IOSExternalViewEmbedder::DidSubmitFrame"); } diff --git a/shell/platform/embedder/embedder_external_view_embedder.cc b/shell/platform/embedder/embedder_external_view_embedder.cc index 0f95b44dfb07a..01c813c8028c0 100644 --- a/shell/platform/embedder/embedder_external_view_embedder.cc +++ b/shell/platform/embedder/embedder_external_view_embedder.cc @@ -422,8 +422,7 @@ void EmbedderExternalViewEmbedder::SubmitFlutterView( int64_t flutter_view_id, GrDirectContext* context, const std::shared_ptr& aiks_context, - std::unique_ptr frame, - fml::RefPtr platform_runner) { + std::unique_ptr frame) { // The unordered_map render_target_cache creates a new entry if the view ID is // unrecognized. EmbedderRenderTargetCache& render_target_cache = diff --git a/shell/platform/embedder/embedder_external_view_embedder.h b/shell/platform/embedder/embedder_external_view_embedder.h index f46b29292db36..141d928125820 100644 --- a/shell/platform/embedder/embedder_external_view_embedder.h +++ b/shell/platform/embedder/embedder_external_view_embedder.h @@ -107,8 +107,7 @@ class EmbedderExternalViewEmbedder final : public ExternalViewEmbedder { int64_t flutter_view_id, GrDirectContext* context, const std::shared_ptr& aiks_context, - std::unique_ptr frame, - fml::RefPtr platform_runner) override; + std::unique_ptr frame) override; // |ExternalViewEmbedder| DlCanvas* GetRootCanvas() override; From 912cd40b5391262c6eb5fe38d635c2b6317db3d6 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Fri, 12 Jul 2024 12:44:27 -0700 Subject: [PATCH 09/49] linting. --- flow/embedded_views.h | 1 - flow/surface_frame.h | 1 - shell/common/shell_test_external_view_embedder.h | 2 -- shell/gpu/gpu_surface_metal_impeller.mm | 1 - .../android/external_view_embedder/external_view_embedder.h | 1 - .../darwin/ios/framework/Source/FlutterPlatformViews_Internal.h | 2 +- shell/platform/darwin/ios/ios_external_view_embedder.h | 2 +- shell/platform/darwin/ios/ios_external_view_embedder.mm | 2 +- 8 files changed, 3 insertions(+), 9 deletions(-) diff --git a/flow/embedded_views.h b/flow/embedded_views.h index be05535dd4322..22b2c1e2db967 100644 --- a/flow/embedded_views.h +++ b/flow/embedded_views.h @@ -14,7 +14,6 @@ #include "flutter/flow/surface_frame.h" #include "flutter/fml/memory/ref_counted.h" #include "flutter/fml/raster_thread_merger.h" -#include "fml/task_runner.h" #include "third_party/skia/include/core/SkMatrix.h" #include "third_party/skia/include/core/SkPath.h" #include "third_party/skia/include/core/SkRRect.h" diff --git a/flow/surface_frame.h b/flow/surface_frame.h index 8a50fa00154de..85f89a652c50b 100644 --- a/flow/surface_frame.h +++ b/flow/surface_frame.h @@ -14,7 +14,6 @@ #include "flutter/fml/macros.h" #include "flutter/fml/time/time_point.h" -#include "fml/synchronization/count_down_latch.h" #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkSurface.h" diff --git a/shell/common/shell_test_external_view_embedder.h b/shell/common/shell_test_external_view_embedder.h index a13044ce158c8..b000c239435fb 100644 --- a/shell/common/shell_test_external_view_embedder.h +++ b/shell/common/shell_test_external_view_embedder.h @@ -7,8 +7,6 @@ #include "flutter/flow/embedded_views.h" #include "flutter/fml/raster_thread_merger.h" -#include "fml/memory/ref_ptr.h" -#include "fml/task_runner.h" namespace flutter { diff --git a/shell/gpu/gpu_surface_metal_impeller.mm b/shell/gpu/gpu_surface_metal_impeller.mm index 6e77e81286889..b0eb55707c5e8 100644 --- a/shell/gpu/gpu_surface_metal_impeller.mm +++ b/shell/gpu/gpu_surface_metal_impeller.mm @@ -44,7 +44,6 @@ [[NSBundle mainBundle] objectForInfoDictionaryKey:@"FLTDisablePartialRepaint"]; if (disablePartialRepaint != nil) { disable_partial_repaint_ = disablePartialRepaint.boolValue; - ; } } diff --git a/shell/platform/android/external_view_embedder/external_view_embedder.h b/shell/platform/android/external_view_embedder/external_view_embedder.h index 9bac37572cedd..ab00870276ffc 100644 --- a/shell/platform/android/external_view_embedder/external_view_embedder.h +++ b/shell/platform/android/external_view_embedder/external_view_embedder.h @@ -13,7 +13,6 @@ #include "flutter/shell/platform/android/external_view_embedder/surface_pool.h" #include "flutter/shell/platform/android/jni/platform_view_android_jni.h" #include "flutter/shell/platform/android/surface/android_surface.h" -#include "fml/memory/ref_ptr.h" namespace flutter { diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index 2c21fca0f694f..caed10c79a6a7 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -213,7 +213,7 @@ class FlutterPlatformViewLayerPool { class FlutterPlatformViewsController { public: - FlutterPlatformViewsController(const fml::RefPtr& platform_task_runner); + explicit FlutterPlatformViewsController(const fml::RefPtr& platform_task_runner); ~FlutterPlatformViewsController(); diff --git a/shell/platform/darwin/ios/ios_external_view_embedder.h b/shell/platform/darwin/ios/ios_external_view_embedder.h index 953966378b15a..cc7ec59b34800 100644 --- a/shell/platform/darwin/ios/ios_external_view_embedder.h +++ b/shell/platform/darwin/ios/ios_external_view_embedder.h @@ -14,7 +14,7 @@ class IOSExternalViewEmbedder : public ExternalViewEmbedder { public: IOSExternalViewEmbedder(const std::shared_ptr& platform_views_controller, - std::shared_ptr context); + const std::shared_ptr& context); // |ExternalViewEmbedder| virtual ~IOSExternalViewEmbedder() override; diff --git a/shell/platform/darwin/ios/ios_external_view_embedder.mm b/shell/platform/darwin/ios/ios_external_view_embedder.mm index 36501b3eaa1cc..fc7b68f1c79f5 100644 --- a/shell/platform/darwin/ios/ios_external_view_embedder.mm +++ b/shell/platform/darwin/ios/ios_external_view_embedder.mm @@ -13,7 +13,7 @@ IOSExternalViewEmbedder::IOSExternalViewEmbedder( const std::shared_ptr& platform_views_controller, - std::shared_ptr context) + const std::shared_ptr& context) : platform_views_controller_(platform_views_controller), ios_context_(std::move(context)) { FML_CHECK(ios_context_); } From 1f65cc34adf8bc3e5d7cda2bce38b173d474e514 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Fri, 12 Jul 2024 12:57:14 -0700 Subject: [PATCH 10/49] skip post message if there are no platform views. --- .../darwin/ios/framework/Source/FlutterPlatformViews.mm | 9 +-------- .../ios/framework/Source/FlutterPlatformViews_Internal.h | 4 ---- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 9f001b3160297..33215c2b9081c 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -615,7 +615,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, std::unique_ptr background_frame) { TRACE_EVENT0("flutter", "FlutterPlatformViewsController::SubmitFrame"); - if (flutter_view_ == nullptr) { + if (flutter_view_ == nullptr || composition_order_.empty()) { return background_frame->Submit(); } @@ -854,13 +854,6 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, return active_composition_order; } -bool FlutterPlatformViewsController::HasPlatformViewLayerAlready( - GrDirectContext* gr_context, - const std::shared_ptr& ios_context, - MTLPixelFormat pixel_format) { - return true; -} - std::shared_ptr FlutterPlatformViewsController::GetExistingLayer() { return layer_pool_->GetNextLayer(); } diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index caed10c79a6a7..d40b5f41a2472 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -330,10 +330,6 @@ class FlutterPlatformViewsController { void CompositeWithParams(int64_t view_id, const EmbeddedViewParams& params); - bool HasPlatformViewLayerAlready(GrDirectContext* gr_context, - const std::shared_ptr& ios_context, - MTLPixelFormat pixel_format); - std::shared_ptr GetExistingLayer(); void CreateLayer(GrDirectContext* gr_context, From 53adc817ff3b48f4b5af74b1481349fb5b31145e Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Fri, 12 Jul 2024 13:34:51 -0700 Subject: [PATCH 11/49] linting. --- .../ios/framework/Source/FlutterPlatformViews.mm | 13 +++++++------ .../darwin/ios/ios_external_view_embedder.mm | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 33215c2b9081c..8e15ae96b0b15 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -722,11 +722,12 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, // This flutter view is never the last in a frame, since we always submit the // underlay view last. - frame->set_submit_info({.frame_boundary = false, - .present_with_transaction = true, - .submit_receiver = [&callbacks](SurfaceFrame::DeferredSubmit cb) { - callbacks.push_back(cb); - }}); + frame->set_submit_info( + {.frame_boundary = false, + .present_with_transaction = true, + .submit_receiver = [&callbacks](const SurfaceFrame::DeferredSubmit& cb) { + callbacks.push_back(cb); + }}); layer->did_submit_last_frame = frame->Submit(); } @@ -748,7 +749,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, background_frame->set_submit_info( {.present_with_transaction = true, - .submit_receiver = [&callbacks](SurfaceFrame::DeferredSubmit cb) { + .submit_receiver = [&callbacks](const SurfaceFrame::DeferredSubmit& cb) { callbacks.push_back(cb); }}); did_submit &= background_frame->Submit(); diff --git a/shell/platform/darwin/ios/ios_external_view_embedder.mm b/shell/platform/darwin/ios/ios_external_view_embedder.mm index fc7b68f1c79f5..523123aa271a1 100644 --- a/shell/platform/darwin/ios/ios_external_view_embedder.mm +++ b/shell/platform/darwin/ios/ios_external_view_embedder.mm @@ -14,7 +14,7 @@ IOSExternalViewEmbedder::IOSExternalViewEmbedder( const std::shared_ptr& platform_views_controller, const std::shared_ptr& context) - : platform_views_controller_(platform_views_controller), ios_context_(std::move(context)) { + : platform_views_controller_(platform_views_controller), ios_context_(context) { FML_CHECK(ios_context_); } From 870b9509941e6c55e4f20bdef73a53b43ef504aa Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Mon, 15 Jul 2024 09:44:23 -0700 Subject: [PATCH 12/49] dispose views in callback. --- .../framework/Source/FlutterPlatformViews.mm | 34 +++++++++++-------- .../Source/FlutterPlatformViews_Internal.h | 2 +- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 8e15ae96b0b15..1b541ec7f19a9 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -760,13 +760,17 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, FML_DCHECK(unused_layers.empty() || missing_layer_count == 0); layer_pool_->RecycleLayers(); - auto task = fml::MakeCopyable([&, platform_view_layers = std::move(platform_view_layers), - missing_layer_count, // - current_composition_params = current_composition_params_, // - views_to_recomposite = views_to_recomposite_, // - callbacks = callbacks, // - composition_order = composition_order_, // - unused_layers = unused_layers]() mutable { + // Dispose unused Flutter Views. + auto views_to_dispose = DisposeViews(); + + platform_task_runner_->PostTask([&, platform_view_layers = std::move(platform_view_layers), + missing_layer_count, // + current_composition_params = current_composition_params_, // + views_to_recomposite = views_to_recomposite_, // + callbacks = callbacks, // + composition_order = composition_order_, // + unused_layers = unused_layers, + views_to_dispose]() mutable { TRACE_EVENT0("flutter", "FlutterPlatformViewsController::SubmitFrame::CATransaction"); [CATransaction begin]; @@ -780,7 +784,9 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, } // Dispose unused Flutter Views. - DisposeViews(); + for (auto& view : views_to_dispose) { + [view removeFromSuperview]; + } // Composite Platform Views. for (auto view_id : views_to_recomposite) { @@ -811,8 +817,6 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, // If that case, we need to commit the transaction. [CATransaction commit]; }); - - platform_task_runner_->PostTask(task); return did_submit; } @@ -909,13 +913,12 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, } } -void FlutterPlatformViewsController::DisposeViews() { +std::vector FlutterPlatformViewsController::DisposeViews() { + std::vector views; if (views_to_dispose_.empty()) { - return; + return views; } - FML_DCHECK([[NSThread currentThread] isMainThread]); - std::unordered_set views_to_composite(composition_order_.begin(), composition_order_.end()); std::unordered_set views_to_delay_dispose; @@ -925,7 +928,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, continue; } UIView* root_view = root_views_[viewId].get(); - [root_view removeFromSuperview]; + views.push_back(root_view); views_.erase(viewId); touch_interceptors_.erase(viewId); root_views_.erase(viewId); @@ -935,6 +938,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, } views_to_dispose_ = std::move(views_to_delay_dispose); + return views; } void FlutterPlatformViewsController::ResetFrameState() { diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index d40b5f41a2472..bc313eacfddb7 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -308,7 +308,7 @@ class FlutterPlatformViewsController { void OnRejectGesture(FlutterMethodCall* call, FlutterResult result) __attribute__((cf_audited_transfer)); // Dispose the views in `views_to_dispose_`. - void DisposeViews(); + std::vector DisposeViews(); // Traverse the `mutators_stack` and return the number of clip operations. int CountClips(const MutatorsStack& mutators_stack); From f8267bde160bf1aad92e5decd0cbb2d07531c86b Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Mon, 15 Jul 2024 09:44:47 -0700 Subject: [PATCH 13/49] ++ --- .../darwin/ios/framework/Source/FlutterPlatformViews.mm | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 1b541ec7f19a9..e921c93129fa6 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -769,8 +769,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, views_to_recomposite = views_to_recomposite_, // callbacks = callbacks, // composition_order = composition_order_, // - unused_layers = unused_layers, - views_to_dispose]() mutable { + unused_layers = unused_layers, views_to_dispose]() mutable { TRACE_EVENT0("flutter", "FlutterPlatformViewsController::SubmitFrame::CATransaction"); [CATransaction begin]; From 2b9a8253896bbee955a64173323226b5223a5502 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Tue, 16 Jul 2024 16:54:13 -0700 Subject: [PATCH 14/49] tear down. --- .../framework/Source/FlutterPlatformViews.mm | 29 +++++--- .../Source/FlutterPlatformViewsTest.mm | 72 ++----------------- .../darwin/ios/ios_external_view_embedder.mm | 1 + 3 files changed, 25 insertions(+), 77 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index e921c93129fa6..ebd09c3fd780b 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -2,12 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include -#include "flow/surface_frame.h" #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h" -#include "fml/logging.h" #include +#include + +#include "flow/surface_frame.h" +#include "fml/logging.h" #include "flutter/fml/make_copyable.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" @@ -593,11 +594,17 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, } void FlutterPlatformViewsController::Reset() { - FML_DCHECK([[NSThread currentThread] isMainThread]); - // for (int64_t view_id : active_composition_order_) { - // UIView* sub_view = root_views_[view_id].get(); - // [sub_view removeFromSuperview]; - // } + std::vector views; + for (int64_t view_id : composition_order_) { + views.push_back(root_views_[view_id].get()); + } + + platform_task_runner_->PostTask([views = views]() { + for (auto* sub_view : views) { + [sub_view removeFromSuperview]; + } + }); + root_views_.clear(); touch_interceptors_.clear(); views_.clear(); @@ -798,9 +805,9 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, // Create Missing Layers for (auto i = 0u; i < missing_layer_count; i++) { - CreateLayer(gr_context, // - ios_context, // - MTLPixelFormatBGRA10_XR // + CreateLayer(gr_context, // + ios_context, // + ((FlutterView*)flutter_view_.get()).pixelFormat // ); } diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm index 2a273974cafc8..c8aae9aa3df39 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm @@ -5,6 +5,7 @@ #import #import #import +#include "fml/synchronization/count_down_latch.h" #import "flutter/fml/thread.h" #import "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h" @@ -2746,77 +2747,16 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { /*frame_size=*/SkISize::Make(800, 600)); XCTAssertTrue( flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface))); + + std::shared_ptr latch = std::make_shared(1u); + thread_task_runner->PostTask([&]() { latch->CountDown(); }); + latch->Wait(); + 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"); - auto thread_task_runner_other = CreateNewThread("FlutterPlatformViewsTest2"); - flutter::TaskRunners runners(/*label=*/self.name.UTF8String, - /*platform=*/thread_task_runner_platform, - /*raster=*/thread_task_runner_other, - /*ui=*/thread_task_runner_other, - /*io=*/thread_task_runner_other); - auto flutterPlatformViewsController = - std::make_shared(thread_task_runner_platform); - auto platform_view = std::make_unique( - /*delegate=*/mock_delegate, - /*rendering_api=*/mock_delegate.settings_.enable_impeller - ? flutter::IOSRenderingAPI::kMetal - : flutter::IOSRenderingAPI::kSoftware, - /*platform_views_controller=*/flutterPlatformViewsController, - /*task_runners=*/runners, - /*worker_task_runner=*/nil, - /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); - - FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = - [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; - flutterPlatformViewsController->RegisterViewFactory( - factory, @"MockFlutterPlatformView", - FlutterPlatformViewGestureRecognizersBlockingPolicyEager); - XCTestExpectation* waitForPlatformView = - [self expectationWithDescription:@"wait for platform view to be created"]; - FlutterResult result = ^(id result) { - [waitForPlatformView fulfill]; - }; - - flutterPlatformViewsController->OnMethodCall( - [FlutterMethodCall - methodCallWithMethodName:@"create" - arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], - result); - [self waitForExpectations:@[ waitForPlatformView ] timeout:30]; - XCTAssertNotNil(gMockPlatformView); - - flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300)); - SkMatrix finalMatrix; - flutter::MutatorsStack stack; - auto embeddedViewParams = - std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); - flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - - fml::RefPtr raster_thread_merger = - fml::MakeRefCounted(thread_task_runner_platform->GetTaskQueueId(), - thread_task_runner_other->GetTaskQueueId()); - XCTAssertEqual(flutterPlatformViewsController->PostPrerollAction(raster_thread_merger), - flutter::PostPrerollResult::kSkipAndRetryFrame); - XCTAssertFalse(raster_thread_merger->IsMerged()); - - flutterPlatformViewsController->EndFrame(true, raster_thread_merger); - XCTAssertTrue(raster_thread_merger->IsMerged()); - - // Unmerge threads before the end of the test - // TaskRunners are required to be unmerged before destruction. - while (raster_thread_merger->DecrementLease() != fml::RasterThreadStatus::kUnmergedNow) { - } -} - - (int)alphaOfPoint:(CGPoint)point onView:(UIView*)view { unsigned char pixel[4] = {0}; diff --git a/shell/platform/darwin/ios/ios_external_view_embedder.mm b/shell/platform/darwin/ios/ios_external_view_embedder.mm index 523123aa271a1..92d1e2dd227f2 100644 --- a/shell/platform/darwin/ios/ios_external_view_embedder.mm +++ b/shell/platform/darwin/ios/ios_external_view_embedder.mm @@ -77,6 +77,7 @@ const std::shared_ptr& aiks_context, std::unique_ptr frame) { TRACE_EVENT0("flutter", "IOSExternalViewEmbedder::SubmitFlutterView"); + // TODO(dkwingsmt): This class only supports rendering into the implicit view. // Properly support multi-view in the future. FML_DCHECK(flutter_view_id == kFlutterImplicitViewId); From 0a7dc5afd8c2c239dc388f8d9ff48fad5a5af8e5 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Tue, 16 Jul 2024 17:30:27 -0700 Subject: [PATCH 15/49] reserve. --- .../platform/darwin/ios/framework/Source/FlutterPlatformViews.mm | 1 + 1 file changed, 1 insertion(+) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index ebd09c3fd780b..ed4d7cd534ae4 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -595,6 +595,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, void FlutterPlatformViewsController::Reset() { std::vector views; + views.reserve(composition_order_.size()); for (int64_t view_id : composition_order_) { views.push_back(root_views_[view_id].get()); } From 2745e88892772bbe15537ce16682af5ccb8440e8 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Tue, 16 Jul 2024 19:49:16 -0700 Subject: [PATCH 16/49] ++ --- .../Source/FlutterPlatformViewsTest.mm | 65 +++++++++++++++++++ .../Source/FlutterPlatformViews_Internal.h | 9 ++- 2 files changed, 72 insertions(+), 2 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm index c8aae9aa3df39..e16dc78498eb8 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm @@ -1955,6 +1955,9 @@ - (void)testClipPath { flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + gMockPlatformView.backgroundColor = UIColor.redColor; XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; @@ -2418,6 +2421,9 @@ - (void)testFlutterPlatformViewControllerSubmitFrameWithoutFlutterViewNotCrashin flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams_1)); flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + flutter::SurfaceFrame::FramebufferInfo framebuffer_info; auto mock_surface = std::make_unique( nullptr, framebuffer_info, @@ -2430,6 +2436,9 @@ - (void)testFlutterPlatformViewControllerSubmitFrameWithoutFlutterViewNotCrashin std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams_2)); flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + auto mock_surface_submit_true = std::make_unique( nullptr, framebuffer_info, [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; }, @@ -2483,6 +2492,9 @@ - (void)testFlutterPlatformViewControllerSubmitFrameWithoutFlutterViewNotCrashin std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + // Not calling |flutterPlatformViewsController::SubmitFrame| so that the platform views are not // added to flutter_view_. @@ -2537,6 +2549,9 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1)); flutterPlatformViewsController->CompositeEmbeddedView(0); + flutterPlatformViewsController->CompositeWithParams( + 0, flutterPlatformViewsController->GetCompositionParams(0)); + XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 1UL); // Second frame, |EmbeddedViewCount| should be empty at the start @@ -2547,6 +2562,9 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams2)); flutterPlatformViewsController->CompositeEmbeddedView(0); + flutterPlatformViewsController->CompositeWithParams( + 0, flutterPlatformViewsController->GetCompositionParams(0)); + XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 1UL); } @@ -2603,10 +2621,15 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1)); flutterPlatformViewsController->CompositeEmbeddedView(0); + flutterPlatformViewsController->CompositeWithParams( + 0, flutterPlatformViewsController->GetCompositionParams(0)); + auto embeddedViewParams2 = std::make_unique(finalMatrix, SkSize::Make(500, 500), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams2)); flutterPlatformViewsController->CompositeEmbeddedView(1); + flutterPlatformViewsController->CompositeWithParams( + 1, flutterPlatformViewsController->GetCompositionParams(1)); // SKSurface is required if the root FlutterView is present. const SkImageInfo image_info = SkImageInfo::MakeN32Premul(1000, 1000); @@ -2634,10 +2657,15 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { std::make_unique(finalMatrix, SkSize::Make(500, 500), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams2)); flutterPlatformViewsController->CompositeEmbeddedView(1); + flutterPlatformViewsController->CompositeWithParams( + 1, flutterPlatformViewsController->GetCompositionParams(1)); + embeddedViewParams1 = std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1)); flutterPlatformViewsController->CompositeEmbeddedView(0); + flutterPlatformViewsController->CompositeWithParams( + 0, flutterPlatformViewsController->GetCompositionParams(0)); mock_sk_surface = SkSurfaces::Raster(image_info); mock_surface = std::make_unique( @@ -2704,10 +2732,15 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1)); flutterPlatformViewsController->CompositeEmbeddedView(0); + flutterPlatformViewsController->CompositeWithParams( + 0, flutterPlatformViewsController->GetCompositionParams(0)); + auto embeddedViewParams2 = std::make_unique(finalMatrix, SkSize::Make(500, 500), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams2)); flutterPlatformViewsController->CompositeEmbeddedView(1); + flutterPlatformViewsController->CompositeWithParams( + 1, flutterPlatformViewsController->GetCompositionParams(1)); // SKSurface is required if the root FlutterView is present. const SkImageInfo image_info = SkImageInfo::MakeN32Premul(1000, 1000); @@ -2735,10 +2768,15 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1)); flutterPlatformViewsController->CompositeEmbeddedView(0); + flutterPlatformViewsController->CompositeWithParams( + 0, flutterPlatformViewsController->GetCompositionParams(0)); + embeddedViewParams2 = std::make_unique(finalMatrix, SkSize::Make(500, 500), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams2)); flutterPlatformViewsController->CompositeEmbeddedView(1); + flutterPlatformViewsController->CompositeWithParams( + 1, flutterPlatformViewsController->GetCompositionParams(1)); mock_sk_surface = SkSurfaces::Raster(image_info); mock_surface = std::make_unique( @@ -2894,6 +2932,9 @@ - (void)testClipMaskViewIsReused { flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1)); flutterPlatformViewsController->CompositeEmbeddedView(1); + flutterPlatformViewsController->CompositeWithParams( + 1, flutterPlatformViewsController->GetCompositionParams(1)); + UIView* childClippingView1 = gMockPlatformView.superview.superview; UIView* maskView1 = childClippingView1.maskView; XCTAssertNotNil(maskView1); @@ -2907,6 +2948,9 @@ - (void)testClipMaskViewIsReused { screenScaleMatrix, SkSize::Make(10, 10), stack2); flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams3)); flutterPlatformViewsController->CompositeEmbeddedView(1); + flutterPlatformViewsController->CompositeWithParams( + 1, flutterPlatformViewsController->GetCompositionParams(1)); + childClippingView1 = gMockPlatformView.superview.superview; // This overrides gMockPlatformView to point to the newly created platform view. @@ -2920,6 +2964,9 @@ - (void)testClipMaskViewIsReused { screenScaleMatrix, SkSize::Make(10, 10), stack1); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams4)); flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + UIView* childClippingView2 = gMockPlatformView.superview.superview; UIView* maskView2 = childClippingView2.maskView; @@ -2994,10 +3041,16 @@ - (void)testDifferentClipMaskViewIsUsedForEachView { flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1)); flutterPlatformViewsController->CompositeEmbeddedView(1); + flutterPlatformViewsController->CompositeWithParams( + 1, flutterPlatformViewsController->GetCompositionParams(1)); + UIView* childClippingView1 = view1.superview.superview; flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams2)); flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + UIView* childClippingView2 = view2.superview.superview; UIView* maskView1 = childClippingView1.maskView; UIView* maskView2 = childClippingView2.maskView; @@ -3061,6 +3114,9 @@ - (void)testMaskViewUsesCAShapeLayerAsTheBackingLayer { flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1)); flutterPlatformViewsController->CompositeEmbeddedView(1); + flutterPlatformViewsController->CompositeWithParams( + 1, flutterPlatformViewsController->GetCompositionParams(1)); + UIView* childClippingView = gMockPlatformView.superview.superview; UIView* maskView = childClippingView.maskView; @@ -3147,11 +3203,16 @@ - (void)testDisposingViewInCompositionOrderDoNotCrash { std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams0)); flutterPlatformViewsController->CompositeEmbeddedView(0); + flutterPlatformViewsController->CompositeWithParams( + 0, flutterPlatformViewsController->GetCompositionParams(0)); auto embeddedViewParams1 = std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1)); flutterPlatformViewsController->CompositeEmbeddedView(1); + flutterPlatformViewsController->CompositeWithParams( + 1, flutterPlatformViewsController->GetCompositionParams(1)); + XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 2UL); XCTestExpectation* expectation = [self expectationWithDescription:@"dispose call ended."]; @@ -3189,6 +3250,8 @@ - (void)testDisposingViewInCompositionOrderDoNotCrash { std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1)); flutterPlatformViewsController->CompositeEmbeddedView(1); + flutterPlatformViewsController->CompositeWithParams( + 1, flutterPlatformViewsController->GetCompositionParams(1)); const SkImageInfo image_info = SkImageInfo::MakeN32Premul(1000, 1000); sk_sp mock_sk_surface = SkSurfaces::Raster(image_info); @@ -3257,6 +3320,8 @@ - (void)testOnlyPlatformViewsAreRemovedWhenReset { flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); // SKSurface is required if the root FlutterView is present. const SkImageInfo image_info = SkImageInfo::MakeN32Premul(1000, 1000); diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index bc313eacfddb7..276601e8addfa 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -288,6 +288,13 @@ class FlutterPlatformViewsController { // Pushes the view id of a visted platform view to the list of visied platform views. void PushVisitedPlatformView(int64_t view_id) { visited_platform_views_.push_back(view_id); } + // Visible for testing. + void CompositeWithParams(int64_t view_id, const EmbeddedViewParams& params); + + const EmbeddedViewParams& GetCompositionParams(int64_t view_id) const { + return current_composition_params_.find(view_id)->second; + } + private: static const size_t kMaxLayerAllocations = 2; @@ -328,8 +335,6 @@ class FlutterPlatformViewsController { UIView* embedded_view, const SkRect& bounding_rect) __attribute__((cf_audited_transfer)); - void CompositeWithParams(int64_t view_id, const EmbeddedViewParams& params); - std::shared_ptr GetExistingLayer(); void CreateLayer(GrDirectContext* gr_context, From 6002aa0a791774489e376b0b821618780a594cd2 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Tue, 16 Jul 2024 20:55:47 -0700 Subject: [PATCH 17/49] ++ --- .../ios/framework/Source/FlutterPlatformViewsTest.mm | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm index e16dc78498eb8..362fd164c307f 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm @@ -1779,6 +1779,9 @@ - (void)testClipRect { flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + gMockPlatformView.backgroundColor = UIColor.redColor; XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; @@ -1853,6 +1856,9 @@ - (void)testClipRRect { flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + gMockPlatformView.backgroundColor = UIColor.redColor; XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; @@ -2753,6 +2759,10 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { XCTAssertTrue( flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface))); + std::shared_ptr latch = std::make_shared(1u); + thread_task_runner->PostTask([&latch]() { latch->CountDown(); }); + latch->Wait(); + // platform view is wrapped by touch interceptor, which itself is wrapped by clipping view. UIView* clippingView1 = view1.superview.superview; UIView* clippingView2 = view2.superview.superview; From e7a832ff9586e2ac61b9cf8ffa4dc15ab92c833c Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Tue, 16 Jul 2024 21:53:24 -0700 Subject: [PATCH 18/49] ++ --- .../darwin/ios/framework/Source/FlutterPlatformViewsTest.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm index 362fd164c307f..f175423dc5e2d 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm @@ -2796,7 +2796,7 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { XCTAssertTrue( flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface))); - std::shared_ptr latch = std::make_shared(1u); + latch = std::make_shared(1u); thread_task_runner->PostTask([&]() { latch->CountDown(); }); latch->Wait(); From f099d7486c5b33158162033b8a42a4cac50a67c5 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Wed, 17 Jul 2024 10:28:16 -0700 Subject: [PATCH 19/49] come on big money no whammies. --- .../Source/FlutterPlatformViewsTest.mm | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm index f175423dc5e2d..07aca2151fefb 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm @@ -338,6 +338,9 @@ - (void)testApplyBackdropFilter { flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]); ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; [mockFlutterView addSubview:childClippingView]; @@ -413,6 +416,9 @@ - (void)testApplyBackdropFilterWithCorrectFrame { flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]); ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; [mockFlutterView addSubview:childClippingView]; @@ -490,6 +496,9 @@ - (void)testApplyMultipleBackdropFilters { flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; [mockFlutterView addSubview:childClippingView]; @@ -564,6 +573,9 @@ - (void)testAddBackdropFilters { flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]); ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; [mockFlutterView addSubview:childClippingView]; @@ -601,6 +613,9 @@ - (void)testAddBackdropFilters { flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + [mockFlutterView setNeedsLayout]; [mockFlutterView layoutIfNeeded]; @@ -683,6 +698,9 @@ - (void)testRemoveBackdropFilters { flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; [mockFlutterView addSubview:childClippingView]; @@ -718,6 +736,9 @@ - (void)testRemoveBackdropFilters { flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + [mockFlutterView setNeedsLayout]; [mockFlutterView layoutIfNeeded]; @@ -757,6 +778,9 @@ - (void)testRemoveBackdropFilters { flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + [mockFlutterView setNeedsLayout]; [mockFlutterView layoutIfNeeded]; @@ -823,6 +847,9 @@ - (void)testEditBackdropFilters { flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; [mockFlutterView addSubview:childClippingView]; @@ -867,6 +894,9 @@ - (void)testEditBackdropFilters { flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + [mockFlutterView setNeedsLayout]; [mockFlutterView layoutIfNeeded]; @@ -921,6 +951,9 @@ - (void)testEditBackdropFilters { flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + [mockFlutterView setNeedsLayout]; [mockFlutterView layoutIfNeeded]; @@ -973,6 +1006,9 @@ - (void)testEditBackdropFilters { flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + [mockFlutterView setNeedsLayout]; [mockFlutterView layoutIfNeeded]; @@ -1021,6 +1057,9 @@ - (void)testEditBackdropFilters { flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + [mockFlutterView setNeedsLayout]; [mockFlutterView layoutIfNeeded]; @@ -1101,6 +1140,9 @@ - (void)testApplyBackdropFilterNotDlBlurImageFilter { flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]); ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; @@ -1141,6 +1183,9 @@ - (void)testApplyBackdropFilterNotDlBlurImageFilter { flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + [mockFlutterView setNeedsLayout]; [mockFlutterView layoutIfNeeded]; @@ -1180,6 +1225,9 @@ - (void)testApplyBackdropFilterNotDlBlurImageFilter { flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + [mockFlutterView setNeedsLayout]; [mockFlutterView layoutIfNeeded]; @@ -1219,6 +1267,9 @@ - (void)testApplyBackdropFilterNotDlBlurImageFilter { flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + [mockFlutterView setNeedsLayout]; [mockFlutterView layoutIfNeeded]; @@ -1252,6 +1303,9 @@ - (void)testApplyBackdropFilterNotDlBlurImageFilter { flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + [mockFlutterView setNeedsLayout]; [mockFlutterView layoutIfNeeded]; @@ -1412,6 +1466,9 @@ - (void)testCompositePlatformView { flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + CGRect platformViewRectInFlutterView = [gMockPlatformView convertRect:gMockPlatformView.bounds toView:mockFlutterView]; XCTAssertTrue(CGRectEqualToRect(platformViewRectInFlutterView, CGRectMake(100, 100, 300, 300))); @@ -1471,6 +1528,9 @@ - (void)testBackdropFilterCorrectlyPushedAndReset { flutterPlatformViewsController->PushFilterToVisitedPlatformViews( filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]); ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; [mockFlutterView addSubview:childClippingView]; @@ -1499,6 +1559,9 @@ - (void)testBackdropFilterCorrectlyPushedAndReset { flutterPlatformViewsController->BeginFrame(SkISize::Make(0, 0)); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams2)); flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]); [mockFlutterView setNeedsLayout]; @@ -1569,6 +1632,9 @@ - (void)testChildClippingViewShouldBeTheBoundingRectOfPlatformView { flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + CGRect platformViewRectInFlutterView = [gMockPlatformView convertRect:gMockPlatformView.bounds toView:mockFlutterView]; XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); @@ -1648,6 +1714,9 @@ - (void)testClipsDoNotInterceptWithPlatformViewShouldNotAddMaskView { flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + gMockPlatformView.backgroundColor = UIColor.redColor; XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; @@ -1716,6 +1785,9 @@ - (void)testClipRRectOnlyHasCornersInterceptWithPlatformViewShouldAddMaskView { flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); flutterPlatformViewsController->CompositeEmbeddedView(2); + flutterPlatformViewsController->CompositeWithParams( + 2, flutterPlatformViewsController->GetCompositionParams(2)); + gMockPlatformView.backgroundColor = UIColor.redColor; XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; From e2e6a2a2c3031a4bc19c1d28163dbce99e109650 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Wed, 17 Jul 2024 11:20:55 -0700 Subject: [PATCH 20/49] okay one more. --- .../framework/Source/FlutterPlatformViewsTest.mm | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm index 07aca2151fefb..bdc074f5da551 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm @@ -2699,15 +2699,11 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1)); flutterPlatformViewsController->CompositeEmbeddedView(0); - flutterPlatformViewsController->CompositeWithParams( - 0, flutterPlatformViewsController->GetCompositionParams(0)); auto embeddedViewParams2 = std::make_unique(finalMatrix, SkSize::Make(500, 500), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams2)); flutterPlatformViewsController->CompositeEmbeddedView(1); - flutterPlatformViewsController->CompositeWithParams( - 1, flutterPlatformViewsController->GetCompositionParams(1)); // SKSurface is required if the root FlutterView is present. const SkImageInfo image_info = SkImageInfo::MakeN32Premul(1000, 1000); @@ -2720,6 +2716,11 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { XCTAssertTrue( flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface))); + + std::shared_ptr latch = std::make_shared(1u); + thread_task_runner->PostTask([&latch]() { latch->CountDown(); }); + latch->Wait(); + // platform view is wrapped by touch interceptor, which itself is wrapped by clipping view. UIView* clippingView1 = view1.superview.superview; UIView* clippingView2 = view2.superview.superview; @@ -2752,6 +2753,11 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { /*frame_size=*/SkISize::Make(800, 600)); XCTAssertTrue( flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface))); + + latch = std::make_shared(1u); + thread_task_runner->PostTask([&latch]() { latch->CountDown(); }); + latch->Wait(); + XCTAssertTrue([flutterView.subviews indexOfObject:clippingView1] > [flutterView.subviews indexOfObject:clippingView2], @"The first clipping view should be added after the second clipping view."); From 44d5b5bbf576cde16fa82a70d99cebce6413336b Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Wed, 17 Jul 2024 14:46:57 -0700 Subject: [PATCH 21/49] debug printf --- .../ios/framework/Source/FlutterPlatformViewsTest.mm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm index bdc074f5da551..fc473a23cb573 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm @@ -2717,9 +2717,11 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { XCTAssertTrue( flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface))); + FML_LOG(ERROR) << "Latch 1"; std::shared_ptr latch = std::make_shared(1u); thread_task_runner->PostTask([&latch]() { latch->CountDown(); }); latch->Wait(); + FML_LOG(ERROR) << "Latch 1 End"; // platform view is wrapped by touch interceptor, which itself is wrapped by clipping view. UIView* clippingView1 = view1.superview.superview; @@ -2736,15 +2738,11 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { std::make_unique(finalMatrix, SkSize::Make(500, 500), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams2)); flutterPlatformViewsController->CompositeEmbeddedView(1); - flutterPlatformViewsController->CompositeWithParams( - 1, flutterPlatformViewsController->GetCompositionParams(1)); embeddedViewParams1 = std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1)); flutterPlatformViewsController->CompositeEmbeddedView(0); - flutterPlatformViewsController->CompositeWithParams( - 0, flutterPlatformViewsController->GetCompositionParams(0)); mock_sk_surface = SkSurfaces::Raster(image_info); mock_surface = std::make_unique( @@ -2754,9 +2752,11 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { XCTAssertTrue( flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface))); + FML_LOG(ERROR) << "Latch 2"; latch = std::make_shared(1u); thread_task_runner->PostTask([&latch]() { latch->CountDown(); }); latch->Wait(); + FML_LOG(ERROR) << "Latch 2 End"; XCTAssertTrue([flutterView.subviews indexOfObject:clippingView1] > [flutterView.subviews indexOfObject:clippingView2], From b53063a18626798679684b1e5f5447cf257fa17a Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Fri, 19 Jul 2024 12:27:23 -0700 Subject: [PATCH 22/49] ++ --- .../darwin/ios/framework/Source/FlutterPlatformViews.mm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index ed4d7cd534ae4..0a748a435910d 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -621,6 +621,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, bool FlutterPlatformViewsController::SubmitFrame(GrDirectContext* gr_context, const std::shared_ptr& ios_context, std::unique_ptr background_frame) { + FML_LOG(ERROR) << "FlutterPlatformViewsController::SubmitFrame"; TRACE_EVENT0("flutter", "FlutterPlatformViewsController::SubmitFrame"); if (flutter_view_ == nullptr || composition_order_.empty()) { @@ -778,6 +779,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, callbacks = callbacks, // composition_order = composition_order_, // unused_layers = unused_layers, views_to_dispose]() mutable { + FML_LOG(ERROR) << "Post Task For PVS"; TRACE_EVENT0("flutter", "FlutterPlatformViewsController::SubmitFrame::CATransaction"); [CATransaction begin]; From 9a8453947a20b26d51628bc2897975236f72fa5d Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Tue, 23 Jul 2024 08:51:52 -0700 Subject: [PATCH 23/49] make test work. --- .../framework/Source/FlutterPlatformViews.mm | 14 ++++---------- .../Source/FlutterPlatformViewsTest.mm | 18 ------------------ 2 files changed, 4 insertions(+), 28 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 0a748a435910d..aa5cecc541a35 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -621,7 +621,6 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, bool FlutterPlatformViewsController::SubmitFrame(GrDirectContext* gr_context, const std::shared_ptr& ios_context, std::unique_ptr background_frame) { - FML_LOG(ERROR) << "FlutterPlatformViewsController::SubmitFrame"; TRACE_EVENT0("flutter", "FlutterPlatformViewsController::SubmitFrame"); if (flutter_view_ == nullptr || composition_order_.empty()) { @@ -630,10 +629,6 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, DlCanvas* background_canvas = background_frame->Canvas(); - // Resolve all pending GPU operations before allocating a new surface. - // This does nothing on Impeller. - background_canvas->Flush(); - // Clipping the background canvas before drawing the picture recorders requires // saving and restoring the clip context. DlAutoCanvasRestore save(background_canvas, /*do_save=*/true); @@ -641,8 +636,8 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, // Maps a platform view id to a vector of `FlutterPlatformViewLayer`. LayersMap platform_view_layers; - auto did_submit = true; - auto num_platform_views = composition_order_.size(); + bool did_submit = true; + size_t num_platform_views = composition_order_.size(); size_t missing_layer_count = 0; @@ -683,7 +678,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, } } - auto allocation_size = intersection_rects.size(); + size_t allocation_size = intersection_rects.size(); // For testing purposes, the overlay id is used to find the overlay view. // This is the index of the layer for the current platform view. @@ -772,14 +767,13 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, // Dispose unused Flutter Views. auto views_to_dispose = DisposeViews(); - platform_task_runner_->PostTask([&, platform_view_layers = std::move(platform_view_layers), + fml::TaskRunner::RunNowOrPostTask(platform_task_runner_, [&, platform_view_layers = std::move(platform_view_layers), missing_layer_count, // current_composition_params = current_composition_params_, // views_to_recomposite = views_to_recomposite_, // callbacks = callbacks, // composition_order = composition_order_, // unused_layers = unused_layers, views_to_dispose]() mutable { - FML_LOG(ERROR) << "Post Task For PVS"; TRACE_EVENT0("flutter", "FlutterPlatformViewsController::SubmitFrame::CATransaction"); [CATransaction begin]; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm index fc473a23cb573..fe16f177c7ffc 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm @@ -2717,12 +2717,6 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { XCTAssertTrue( flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface))); - FML_LOG(ERROR) << "Latch 1"; - std::shared_ptr latch = std::make_shared(1u); - thread_task_runner->PostTask([&latch]() { latch->CountDown(); }); - latch->Wait(); - FML_LOG(ERROR) << "Latch 1 End"; - // platform view is wrapped by touch interceptor, which itself is wrapped by clipping view. UIView* clippingView1 = view1.superview.superview; UIView* clippingView2 = view2.superview.superview; @@ -2752,11 +2746,6 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { XCTAssertTrue( flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface))); - FML_LOG(ERROR) << "Latch 2"; - latch = std::make_shared(1u); - thread_task_runner->PostTask([&latch]() { latch->CountDown(); }); - latch->Wait(); - FML_LOG(ERROR) << "Latch 2 End"; XCTAssertTrue([flutterView.subviews indexOfObject:clippingView1] > [flutterView.subviews indexOfObject:clippingView2], @@ -2837,9 +2826,6 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { XCTAssertTrue( flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface))); - std::shared_ptr latch = std::make_shared(1u); - thread_task_runner->PostTask([&latch]() { latch->CountDown(); }); - latch->Wait(); // platform view is wrapped by touch interceptor, which itself is wrapped by clipping view. UIView* clippingView1 = view1.superview.superview; @@ -2874,10 +2860,6 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { XCTAssertTrue( flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface))); - latch = std::make_shared(1u); - thread_task_runner->PostTask([&]() { latch->CountDown(); }); - latch->Wait(); - XCTAssertTrue([flutterView.subviews indexOfObject:clippingView1] < [flutterView.subviews indexOfObject:clippingView2], @"The first clipping view should be added before the second clipping view."); From 65a5210d26af9975288df39f67100984d7a101d5 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Tue, 23 Jul 2024 08:53:43 -0700 Subject: [PATCH 24/49] ++ --- .../framework/Source/FlutterPlatformViews.mm | 94 ++++++++++--------- .../Source/FlutterPlatformViewsTest.mm | 1 - 2 files changed, 48 insertions(+), 47 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index aa5cecc541a35..b2f362be4b827 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -767,59 +767,61 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, // Dispose unused Flutter Views. auto views_to_dispose = DisposeViews(); - fml::TaskRunner::RunNowOrPostTask(platform_task_runner_, [&, platform_view_layers = std::move(platform_view_layers), - missing_layer_count, // - current_composition_params = current_composition_params_, // - views_to_recomposite = views_to_recomposite_, // - callbacks = callbacks, // - composition_order = composition_order_, // - unused_layers = unused_layers, views_to_dispose]() mutable { - TRACE_EVENT0("flutter", "FlutterPlatformViewsController::SubmitFrame::CATransaction"); - - [CATransaction begin]; - - // Configure Flutter overlay views. - for (const auto& [key, layers] : platform_view_layers) { - for (const auto& layer_data : layers) { - layer_data.layer->UpdateViewState(flutter_view_, layer_data.rect, layer_data.view_id, - layer_data.overlay_id); - } - } + fml::TaskRunner::RunNowOrPostTask( + platform_task_runner_, [&, platform_view_layers = std::move(platform_view_layers), + missing_layer_count, // + current_composition_params = current_composition_params_, // + views_to_recomposite = views_to_recomposite_, // + callbacks = callbacks, // + composition_order = composition_order_, // + unused_layers = unused_layers, views_to_dispose]() mutable { + TRACE_EVENT0("flutter", "FlutterPlatformViewsController::SubmitFrame::CATransaction"); + + [CATransaction begin]; + + // Configure Flutter overlay views. + for (const auto& [key, layers] : platform_view_layers) { + for (const auto& layer_data : layers) { + layer_data.layer->UpdateViewState(flutter_view_, layer_data.rect, layer_data.view_id, + layer_data.overlay_id); + } + } - // Dispose unused Flutter Views. - for (auto& view : views_to_dispose) { - [view removeFromSuperview]; - } + // Dispose unused Flutter Views. + for (auto& view : views_to_dispose) { + [view removeFromSuperview]; + } - // Composite Platform Views. - for (auto view_id : views_to_recomposite) { - CompositeWithParams(view_id, current_composition_params[view_id]); - } + // Composite Platform Views. + for (auto view_id : views_to_recomposite) { + CompositeWithParams(view_id, current_composition_params[view_id]); + } - for (const auto& cb : callbacks) { - cb(); - } + for (const auto& cb : callbacks) { + cb(); + } - // Create Missing Layers - for (auto i = 0u; i < missing_layer_count; i++) { - CreateLayer(gr_context, // - ios_context, // - ((FlutterView*)flutter_view_.get()).pixelFormat // - ); - } + // Create Missing Layers + for (auto i = 0u; i < missing_layer_count; i++) { + CreateLayer(gr_context, // + ios_context, // + ((FlutterView*)flutter_view_.get()).pixelFormat // + ); + } - // Organize the layers by their z indexes. - auto active_composition_order = BringLayersIntoView(platform_view_layers, composition_order); + // Organize the layers by their z indexes. + auto active_composition_order = + BringLayersIntoView(platform_view_layers, composition_order); - // If a layer was allocated in the previous frame, but it's not used in the current frame, - // then it can be removed from the scene. - RemoveUnusedLayers(unused_layers, composition_order, active_composition_order); + // If a layer was allocated in the previous frame, but it's not used in the current frame, + // then it can be removed from the scene. + RemoveUnusedLayers(unused_layers, composition_order, active_composition_order); - // If the frame is submitted with embedded platform views, - // there should be a |[CATransaction begin]| call in this frame prior to all the drawing. - // If that case, we need to commit the transaction. - [CATransaction commit]; - }); + // If the frame is submitted with embedded platform views, + // there should be a |[CATransaction begin]| call in this frame prior to all the drawing. + // If that case, we need to commit the transaction. + [CATransaction commit]; + }); return did_submit; } diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm index fe16f177c7ffc..1ff997cf7f14d 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm @@ -2746,7 +2746,6 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { 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 after the second clipping view."); From 735ce229f5efefdf417fb4a7b7ac3ffd8362527e Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Tue, 23 Jul 2024 19:00:42 -0700 Subject: [PATCH 25/49] testing fixes. --- .../ios/framework/Source/FlutterEngine.mm | 14 +- .../framework/Source/FlutterPlatformViews.mm | 113 +-- .../Source/FlutterPlatformViewsTest.mm | 820 +++++++++--------- .../Source/FlutterPlatformViews_Internal.h | 4 +- .../Source/FlutterPlatformViews_Internal.mm | 9 +- .../Source/accessibility_bridge_test.mm | 22 +- 6 files changed, 496 insertions(+), 486 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index 40c6d888dcbca..669eb9a35a39f 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -218,7 +218,7 @@ - (instancetype)initWithName:(NSString*)labelPrefix _pluginPublications = [[NSMutableDictionary alloc] init]; _registrars = [[NSMutableDictionary alloc] init]; - + [self recreatePlatformViewController]; _binaryMessenger = [[FlutterBinaryMessengerRelay alloc] initWithParent:self]; _textureRegistry = [[FlutterTextureRegistryRelay alloc] initWithParent:self]; _connections.reset(new flutter::ConnectionCollection()); @@ -269,9 +269,9 @@ - (void)setUpApplicationLifecycleNotifications:(NSNotificationCenter*)center { object:nil]; } -- (void)recreatePlatformViewController:(fml::RefPtr)platform_task_runner { +- (void)recreatePlatformViewController { _renderingApi = flutter::GetRenderingAPIForProcess(FlutterView.forceSoftwareRendering); - _platformViewsController.reset(new flutter::FlutterPlatformViewsController(platform_task_runner)); + _platformViewsController.reset(new flutter::FlutterPlatformViewsController()); } - (flutter::IOSRenderingAPI)platformViewsRenderingAPI { @@ -867,7 +867,9 @@ - (BOOL)createShell:(NSString*)entrypoint // create call is synchronous. flutter::Shell::CreateCallback on_create_platform_view = [self](flutter::Shell& shell) { - [self recreatePlatformViewController:shell.GetTaskRunners().GetPlatformTaskRunner()]; + [self recreatePlatformViewController]; + self->_platformViewsController->SetTaskRunner( + shell.GetTaskRunners().GetPlatformTaskRunner()); return std::make_unique( shell, self->_renderingApi, self->_platformViewsController, shell.GetTaskRunners(), shell.GetConcurrentWorkerTaskRunner(), shell.GetIsGpuDisabledSyncSwitch()); @@ -1466,7 +1468,9 @@ - (FlutterEngine*)spawnWithEntrypoint:(/*nullable*/ NSString*)entrypoint // create call is synchronous. flutter::Shell::CreateCallback on_create_platform_view = [result, context](flutter::Shell& shell) { - [result recreatePlatformViewController:shell.GetTaskRunners().GetPlatformTaskRunner()]; + [result recreatePlatformViewController]; + result->_platformViewsController->SetTaskRunner( + shell.GetTaskRunners().GetPlatformTaskRunner()); return std::make_unique( shell, context, result->_platformViewsController, shell.GetTaskRunners()); }; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index b2f362be4b827..ced176eabc4b9 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -422,7 +422,6 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, void FlutterPlatformViewsController::ApplyMutators(const MutatorsStack& mutators_stack, UIView* embedded_view, const SkRect& bounding_rect) { - FML_DCHECK([[NSThread currentThread] isMainThread]); if (flutter_view_ == nullptr) { return; } @@ -551,7 +550,6 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, // included in the `views_to_recomposite_`. void FlutterPlatformViewsController::CompositeWithParams(int64_t view_id, const EmbeddedViewParams& params) { - FML_DCHECK([[NSThread currentThread] isMainThread]); CGRect frame = CGRectMake(0, 0, params.sizePoints().width(), params.sizePoints().height()); FlutterTouchInterceptingView* touchInterceptor = touch_interceptors_[view_id].get(); #if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG @@ -600,11 +598,16 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, views.push_back(root_views_[view_id].get()); } - platform_task_runner_->PostTask([views = views]() { + auto task = [views = views]() { for (auto* sub_view : views) { [sub_view removeFromSuperview]; } - }); + }; + if ([[NSThread currentThread] isMainThread]) { + task(); + } else { + platform_task_runner_->PostTask(task); + } root_views_.clear(); touch_interceptors_.clear(); @@ -767,61 +770,66 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, // Dispose unused Flutter Views. auto views_to_dispose = DisposeViews(); - fml::TaskRunner::RunNowOrPostTask( - platform_task_runner_, [&, platform_view_layers = std::move(platform_view_layers), - missing_layer_count, // - current_composition_params = current_composition_params_, // - views_to_recomposite = views_to_recomposite_, // - callbacks = callbacks, // - composition_order = composition_order_, // - unused_layers = unused_layers, views_to_dispose]() mutable { - TRACE_EVENT0("flutter", "FlutterPlatformViewsController::SubmitFrame::CATransaction"); - - [CATransaction begin]; - - // Configure Flutter overlay views. - for (const auto& [key, layers] : platform_view_layers) { - for (const auto& layer_data : layers) { - layer_data.layer->UpdateViewState(flutter_view_, layer_data.rect, layer_data.view_id, - layer_data.overlay_id); - } - } + auto task = [&, platform_view_layers = std::move(platform_view_layers), + missing_layer_count, // + current_composition_params = current_composition_params_, // + views_to_recomposite = views_to_recomposite_, // + callbacks = callbacks, // + composition_order = composition_order_, // + unused_layers = unused_layers, views_to_dispose]() mutable { + TRACE_EVENT0("flutter", "FlutterPlatformViewsController::SubmitFrame::CATransaction"); + + [CATransaction begin]; + + // Configure Flutter overlay views. + for (const auto& [key, layers] : platform_view_layers) { + for (const auto& layer_data : layers) { + layer_data.layer->UpdateViewState(flutter_view_, layer_data.rect, layer_data.view_id, + layer_data.overlay_id); + } + } - // Dispose unused Flutter Views. - for (auto& view : views_to_dispose) { - [view removeFromSuperview]; - } + // Dispose unused Flutter Views. + for (auto& view : views_to_dispose) { + [view removeFromSuperview]; + } - // Composite Platform Views. - for (auto view_id : views_to_recomposite) { - CompositeWithParams(view_id, current_composition_params[view_id]); - } + // Composite Platform Views. + for (auto view_id : views_to_recomposite) { + CompositeWithParams(view_id, current_composition_params[view_id]); + } - for (const auto& cb : callbacks) { - cb(); - } + for (const auto& cb : callbacks) { + cb(); + } - // Create Missing Layers - for (auto i = 0u; i < missing_layer_count; i++) { - CreateLayer(gr_context, // - ios_context, // - ((FlutterView*)flutter_view_.get()).pixelFormat // - ); - } + // Create Missing Layers + for (auto i = 0u; i < missing_layer_count; i++) { + CreateLayer(gr_context, // + ios_context, // + ((FlutterView*)flutter_view_.get()).pixelFormat // + ); + } - // Organize the layers by their z indexes. - auto active_composition_order = - BringLayersIntoView(platform_view_layers, composition_order); + // Organize the layers by their z indexes. + auto active_composition_order = BringLayersIntoView(platform_view_layers, composition_order); - // If a layer was allocated in the previous frame, but it's not used in the current frame, - // then it can be removed from the scene. - RemoveUnusedLayers(unused_layers, composition_order, active_composition_order); + // If a layer was allocated in the previous frame, but it's not used in the current frame, + // then it can be removed from the scene. + RemoveUnusedLayers(unused_layers, composition_order, active_composition_order); + + // If the frame is submitted with embedded platform views, + // there should be a |[CATransaction begin]| call in this frame prior to all the drawing. + // If that case, we need to commit the transaction. + [CATransaction commit]; + }; + + if ([[NSThread currentThread] isMainThread]) { + task(); + } else { + platform_task_runner_->PostTask(task); + } - // If the frame is submitted with embedded platform views, - // there should be a |[CATransaction begin]| call in this frame prior to all the drawing. - // If that case, we need to commit the transaction. - [CATransaction commit]; - }); return did_submit; } @@ -851,6 +859,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, 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 diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm index 1ff997cf7f14d..2ce2f646a5b4c 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm @@ -123,14 +123,6 @@ void UpdateAssetResolverByType(std::unique_ptr updated_a } // namespace } // namespace flutter -namespace { -fml::RefPtr CreateNewThread(const std::string& name) { - auto thread = std::make_unique(name); - auto runner = thread->GetTaskRunner(); - return runner; -} -} // namespace - @interface FlutterPlatformViewsTest : XCTestCase @end @@ -138,14 +130,14 @@ @implementation FlutterPlatformViewsTest - (void)testFlutterViewOnlyCreateOnceInOneFrame { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest"); + auto thread = std::make_unique("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(thread_task_runner); + /*platform=*/thread->GetTaskRunner(), + /*raster=*/thread->GetTaskRunner(), + /*ui=*/thread->GetTaskRunner(), + /*io=*/thread->GetTaskRunner()); + auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -168,8 +160,8 @@ - (void)testFlutterViewOnlyCreateOnceInOneFrame { methodCallWithMethodName:@"create" arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], result); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; + flutterPlatformViewsController->SetFlutterView(flutterView); // Create embedded view params flutter::MutatorsStack stack; // Layer tree always pushes a screen scale factor to the stack @@ -195,14 +187,14 @@ - (void)testFlutterViewOnlyCreateOnceInOneFrame { - (void)testCanCreatePlatformViewWithoutFlutterView { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest"); + auto thread = std::make_unique("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(thread_task_runner); + /*platform=*/thread->GetTaskRunner(), + /*raster=*/thread->GetTaskRunner(), + /*ui=*/thread->GetTaskRunner(), + /*io=*/thread->GetTaskRunner()); + auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -288,14 +280,14 @@ - (void)testReleasesBackdropFilterSubviewsOnChildClippingViewDealloc { - (void)testApplyBackdropFilter { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest"); + auto thread = std::make_unique("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(thread_task_runner); + /*platform=*/thread->GetTaskRunner(), + /*raster=*/thread->GetTaskRunner(), + /*ui=*/thread->GetTaskRunner(), + /*io=*/thread->GetTaskRunner()); + auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -321,8 +313,8 @@ - (void)testApplyBackdropFilter { XCTAssertNotNil(gMockPlatformView); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; + flutterPlatformViewsController->SetFlutterView(flutterView); // Create embedded view params flutter::MutatorsStack stack; // Layer tree always pushes a screen scale factor to the stack @@ -343,10 +335,10 @@ - (void)testApplyBackdropFilter { XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]); ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; - [mockFlutterView addSubview:childClippingView]; + [flutterView addSubview:childClippingView]; - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; + [flutterView setNeedsLayout]; + [flutterView layoutIfNeeded]; // childClippingView has visual effect view with the correct configurations. NSUInteger numberOfExpectedVisualEffectView = 0; @@ -366,14 +358,14 @@ - (void)testApplyBackdropFilter { - (void)testApplyBackdropFilterWithCorrectFrame { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest"); + auto thread = std::make_unique("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(thread_task_runner); + /*platform=*/thread->GetTaskRunner(), + /*raster=*/thread->GetTaskRunner(), + /*ui=*/thread->GetTaskRunner(), + /*io=*/thread->GetTaskRunner()); + auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -399,8 +391,8 @@ - (void)testApplyBackdropFilterWithCorrectFrame { XCTAssertNotNil(gMockPlatformView); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; + flutterPlatformViewsController->SetFlutterView(flutterView); // Create embedded view params flutter::MutatorsStack stack; // Layer tree always pushes a screen scale factor to the stack @@ -421,10 +413,10 @@ - (void)testApplyBackdropFilterWithCorrectFrame { XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]); ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; - [mockFlutterView addSubview:childClippingView]; + [flutterView addSubview:childClippingView]; - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; + [flutterView setNeedsLayout]; + [flutterView layoutIfNeeded]; // childClippingView has visual effect view with the correct configurations. NSUInteger numberOfExpectedVisualEffectView = 0; @@ -444,14 +436,14 @@ - (void)testApplyBackdropFilterWithCorrectFrame { - (void)testApplyMultipleBackdropFilters { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest"); + auto thread = std::make_unique("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(thread_task_runner); + /*platform=*/thread->GetTaskRunner(), + /*raster=*/thread->GetTaskRunner(), + /*ui=*/thread->GetTaskRunner(), + /*io=*/thread->GetTaskRunner()); + auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -477,8 +469,8 @@ - (void)testApplyMultipleBackdropFilters { XCTAssertNotNil(gMockPlatformView); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; + flutterPlatformViewsController->SetFlutterView(flutterView); // Create embedded view params flutter::MutatorsStack stack; // Layer tree always pushes a screen scale factor to the stack @@ -501,10 +493,10 @@ - (void)testApplyMultipleBackdropFilters { XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; - [mockFlutterView addSubview:childClippingView]; + [flutterView addSubview:childClippingView]; - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; + [flutterView setNeedsLayout]; + [flutterView layoutIfNeeded]; NSUInteger numberOfExpectedVisualEffectView = 0; for (UIView* subview in childClippingView.subviews) { @@ -523,14 +515,14 @@ - (void)testApplyMultipleBackdropFilters { - (void)testAddBackdropFilters { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest"); + auto thread = std::make_unique("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(thread_task_runner); + /*platform=*/thread->GetTaskRunner(), + /*raster=*/thread->GetTaskRunner(), + /*ui=*/thread->GetTaskRunner(), + /*io=*/thread->GetTaskRunner()); + auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -556,8 +548,8 @@ - (void)testAddBackdropFilters { XCTAssertNotNil(gMockPlatformView); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; + flutterPlatformViewsController->SetFlutterView(flutterView); // Create embedded view params flutter::MutatorsStack stack; // Layer tree always pushes a screen scale factor to the stack @@ -578,10 +570,10 @@ - (void)testAddBackdropFilters { XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]); ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; - [mockFlutterView addSubview:childClippingView]; + [flutterView addSubview:childClippingView]; - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; + [flutterView setNeedsLayout]; + [flutterView layoutIfNeeded]; NSMutableArray* originalVisualEffectViews = [[NSMutableArray alloc] init]; for (UIView* subview in childClippingView.subviews) { @@ -616,8 +608,8 @@ - (void)testAddBackdropFilters { flutterPlatformViewsController->CompositeWithParams( 2, flutterPlatformViewsController->GetCompositionParams(2)); - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; + [flutterView setNeedsLayout]; + [flutterView layoutIfNeeded]; NSMutableArray* newVisualEffectViews = [[NSMutableArray alloc] init]; for (UIView* subview in childClippingView.subviews) { @@ -646,14 +638,14 @@ - (void)testAddBackdropFilters { - (void)testRemoveBackdropFilters { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest"); + auto thread = std::make_unique("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(thread_task_runner); + /*platform=*/thread->GetTaskRunner(), + /*raster=*/thread->GetTaskRunner(), + /*ui=*/thread->GetTaskRunner(), + /*io=*/thread->GetTaskRunner()); + auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -679,8 +671,8 @@ - (void)testRemoveBackdropFilters { XCTAssertNotNil(gMockPlatformView); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; + flutterPlatformViewsController->SetFlutterView(flutterView); // Create embedded view params flutter::MutatorsStack stack; // Layer tree always pushes a screen scale factor to the stack @@ -703,10 +695,10 @@ - (void)testRemoveBackdropFilters { XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; - [mockFlutterView addSubview:childClippingView]; + [flutterView addSubview:childClippingView]; - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; + [flutterView setNeedsLayout]; + [flutterView layoutIfNeeded]; NSMutableArray* originalVisualEffectViews = [[NSMutableArray alloc] init]; for (UIView* subview in childClippingView.subviews) { @@ -739,8 +731,8 @@ - (void)testRemoveBackdropFilters { flutterPlatformViewsController->CompositeWithParams( 2, flutterPlatformViewsController->GetCompositionParams(2)); - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; + [flutterView setNeedsLayout]; + [flutterView layoutIfNeeded]; NSMutableArray* newVisualEffectViews = [[NSMutableArray alloc] init]; for (UIView* subview in childClippingView.subviews) { @@ -781,8 +773,8 @@ - (void)testRemoveBackdropFilters { flutterPlatformViewsController->CompositeWithParams( 2, flutterPlatformViewsController->GetCompositionParams(2)); - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; + [flutterView setNeedsLayout]; + [flutterView layoutIfNeeded]; NSUInteger numberOfExpectedVisualEffectView = 0u; for (UIView* subview in childClippingView.subviews) { @@ -795,14 +787,14 @@ - (void)testRemoveBackdropFilters { - (void)testEditBackdropFilters { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest"); + auto thread = std::make_unique("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(thread_task_runner); + /*platform=*/thread->GetTaskRunner(), + /*raster=*/thread->GetTaskRunner(), + /*ui=*/thread->GetTaskRunner(), + /*io=*/thread->GetTaskRunner()); + auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -828,8 +820,8 @@ - (void)testEditBackdropFilters { XCTAssertNotNil(gMockPlatformView); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; + flutterPlatformViewsController->SetFlutterView(flutterView); // Create embedded view params flutter::MutatorsStack stack; // Layer tree always pushes a screen scale factor to the stack @@ -852,10 +844,10 @@ - (void)testEditBackdropFilters { XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; - [mockFlutterView addSubview:childClippingView]; + [flutterView addSubview:childClippingView]; - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; + [flutterView setNeedsLayout]; + [flutterView layoutIfNeeded]; NSMutableArray* originalVisualEffectViews = [[NSMutableArray alloc] init]; for (UIView* subview in childClippingView.subviews) { @@ -897,8 +889,8 @@ - (void)testEditBackdropFilters { flutterPlatformViewsController->CompositeWithParams( 2, flutterPlatformViewsController->GetCompositionParams(2)); - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; + [flutterView setNeedsLayout]; + [flutterView layoutIfNeeded]; NSMutableArray* newVisualEffectViews = [[NSMutableArray alloc] init]; for (UIView* subview in childClippingView.subviews) { @@ -954,8 +946,8 @@ - (void)testEditBackdropFilters { flutterPlatformViewsController->CompositeWithParams( 2, flutterPlatformViewsController->GetCompositionParams(2)); - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; + [flutterView setNeedsLayout]; + [flutterView layoutIfNeeded]; for (UIView* subview in childClippingView.subviews) { if (![subview isKindOfClass:[UIVisualEffectView class]]) { @@ -1009,8 +1001,8 @@ - (void)testEditBackdropFilters { flutterPlatformViewsController->CompositeWithParams( 2, flutterPlatformViewsController->GetCompositionParams(2)); - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; + [flutterView setNeedsLayout]; + [flutterView layoutIfNeeded]; for (UIView* subview in childClippingView.subviews) { if (![subview isKindOfClass:[UIVisualEffectView class]]) { @@ -1060,8 +1052,8 @@ - (void)testEditBackdropFilters { flutterPlatformViewsController->CompositeWithParams( 2, flutterPlatformViewsController->GetCompositionParams(2)); - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; + [flutterView setNeedsLayout]; + [flutterView layoutIfNeeded]; for (UIView* subview in childClippingView.subviews) { if (![subview isKindOfClass:[UIVisualEffectView class]]) { @@ -1090,14 +1082,14 @@ - (void)testEditBackdropFilters { - (void)testApplyBackdropFilterNotDlBlurImageFilter { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest"); + auto thread = std::make_unique("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(thread_task_runner); + /*platform=*/thread->GetTaskRunner(), + /*raster=*/thread->GetTaskRunner(), + /*ui=*/thread->GetTaskRunner(), + /*io=*/thread->GetTaskRunner()); + auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -1123,8 +1115,8 @@ - (void)testApplyBackdropFilterNotDlBlurImageFilter { XCTAssertNotNil(gMockPlatformView); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; + flutterPlatformViewsController->SetFlutterView(flutterView); // Create embedded view params flutter::MutatorsStack stack; // Layer tree always pushes a screen scale factor to the stack @@ -1146,10 +1138,10 @@ - (void)testApplyBackdropFilterNotDlBlurImageFilter { XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]); ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; - [mockFlutterView addSubview:childClippingView]; + [flutterView addSubview:childClippingView]; - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; + [flutterView setNeedsLayout]; + [flutterView layoutIfNeeded]; NSUInteger numberOfExpectedVisualEffectView = 0; for (UIView* subview in childClippingView.subviews) { @@ -1186,8 +1178,8 @@ - (void)testApplyBackdropFilterNotDlBlurImageFilter { flutterPlatformViewsController->CompositeWithParams( 2, flutterPlatformViewsController->GetCompositionParams(2)); - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; + [flutterView setNeedsLayout]; + [flutterView layoutIfNeeded]; numberOfExpectedVisualEffectView = 0; for (UIView* subview in childClippingView.subviews) { @@ -1228,8 +1220,8 @@ - (void)testApplyBackdropFilterNotDlBlurImageFilter { flutterPlatformViewsController->CompositeWithParams( 2, flutterPlatformViewsController->GetCompositionParams(2)); - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; + [flutterView setNeedsLayout]; + [flutterView layoutIfNeeded]; numberOfExpectedVisualEffectView = 0; for (UIView* subview in childClippingView.subviews) { @@ -1270,8 +1262,8 @@ - (void)testApplyBackdropFilterNotDlBlurImageFilter { flutterPlatformViewsController->CompositeWithParams( 2, flutterPlatformViewsController->GetCompositionParams(2)); - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; + [flutterView setNeedsLayout]; + [flutterView layoutIfNeeded]; numberOfExpectedVisualEffectView = 0; for (UIView* subview in childClippingView.subviews) { @@ -1306,8 +1298,8 @@ - (void)testApplyBackdropFilterNotDlBlurImageFilter { flutterPlatformViewsController->CompositeWithParams( 2, flutterPlatformViewsController->GetCompositionParams(2)); - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; + [flutterView setNeedsLayout]; + [flutterView layoutIfNeeded]; numberOfExpectedVisualEffectView = 0; for (UIView* subview in childClippingView.subviews) { @@ -1414,14 +1406,14 @@ - (void)testBackdropFilterVisualEffectSubviewBackgroundColor { - (void)testCompositePlatformView { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest"); + auto thread = std::make_unique("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(thread_task_runner); + /*platform=*/thread->GetTaskRunner(), + /*raster=*/thread->GetTaskRunner(), + /*ui=*/thread->GetTaskRunner(), + /*io=*/thread->GetTaskRunner()); + auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -1447,8 +1439,8 @@ - (void)testCompositePlatformView { XCTAssertNotNil(gMockPlatformView); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; + flutterPlatformViewsController->SetFlutterView(flutterView); // Create embedded view params flutter::MutatorsStack stack; // Layer tree always pushes a screen scale factor to the stack @@ -1470,20 +1462,20 @@ - (void)testCompositePlatformView { 2, flutterPlatformViewsController->GetCompositionParams(2)); CGRect platformViewRectInFlutterView = [gMockPlatformView convertRect:gMockPlatformView.bounds - toView:mockFlutterView]; + toView:flutterView]; XCTAssertTrue(CGRectEqualToRect(platformViewRectInFlutterView, CGRectMake(100, 100, 300, 300))); } - (void)testBackdropFilterCorrectlyPushedAndReset { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest"); + auto thread = std::make_unique("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(thread_task_runner); + /*platform=*/thread->GetTaskRunner(), + /*raster=*/thread->GetTaskRunner(), + /*ui=*/thread->GetTaskRunner(), + /*io=*/thread->GetTaskRunner()); + auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -1509,8 +1501,8 @@ - (void)testBackdropFilterCorrectlyPushedAndReset { XCTAssertNotNil(gMockPlatformView); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; + flutterPlatformViewsController->SetFlutterView(flutterView); // Create embedded view params flutter::MutatorsStack stack; // Layer tree always pushes a screen scale factor to the stack @@ -1533,10 +1525,10 @@ - (void)testBackdropFilterCorrectlyPushedAndReset { XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]); ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; - [mockFlutterView addSubview:childClippingView]; + [flutterView addSubview:childClippingView]; - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; + [flutterView setNeedsLayout]; + [flutterView layoutIfNeeded]; // childClippingView has visual effect view with the correct configurations. NSUInteger numberOfExpectedVisualEffectView = 0; @@ -1564,8 +1556,8 @@ - (void)testBackdropFilterCorrectlyPushedAndReset { XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]); - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; + [flutterView setNeedsLayout]; + [flutterView layoutIfNeeded]; numberOfExpectedVisualEffectView = 0; for (UIView* subview in childClippingView.subviews) { @@ -1579,14 +1571,14 @@ - (void)testBackdropFilterCorrectlyPushedAndReset { - (void)testChildClippingViewShouldBeTheBoundingRectOfPlatformView { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest"); + auto thread = std::make_unique("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(thread_task_runner); + /*platform=*/thread->GetTaskRunner(), + /*raster=*/thread->GetTaskRunner(), + /*ui=*/thread->GetTaskRunner(), + /*io=*/thread->GetTaskRunner()); + auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -1612,8 +1604,8 @@ - (void)testChildClippingViewShouldBeTheBoundingRectOfPlatformView { XCTAssertNotNil(gMockPlatformView); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; + flutterPlatformViewsController->SetFlutterView(flutterView); // Create embedded view params flutter::MutatorsStack stack; // Layer tree always pushes a screen scale factor to the stack @@ -1636,7 +1628,7 @@ - (void)testChildClippingViewShouldBeTheBoundingRectOfPlatformView { 2, flutterPlatformViewsController->GetCompositionParams(2)); CGRect platformViewRectInFlutterView = [gMockPlatformView convertRect:gMockPlatformView.bounds - toView:mockFlutterView]; + toView:flutterView]; XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; // The childclippingview's frame is set based on flow, but the platform view's frame is set based @@ -1656,14 +1648,14 @@ - (void)testChildClippingViewShouldBeTheBoundingRectOfPlatformView { - (void)testClipsDoNotInterceptWithPlatformViewShouldNotAddMaskView { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest"); + auto thread = std::make_unique("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(thread_task_runner); + /*platform=*/thread->GetTaskRunner(), + /*raster=*/thread->GetTaskRunner(), + /*ui=*/thread->GetTaskRunner(), + /*io=*/thread->GetTaskRunner()); + auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -1689,8 +1681,8 @@ - (void)testClipsDoNotInterceptWithPlatformViewShouldNotAddMaskView { XCTAssertNotNil(gMockPlatformView); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 30, 30)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 30, 30)]; + flutterPlatformViewsController->SetFlutterView(flutterView); // Create embedded view params. flutter::MutatorsStack stack; // Layer tree always pushes a screen scale factor to the stack. @@ -1720,23 +1712,23 @@ - (void)testClipsDoNotInterceptWithPlatformViewShouldNotAddMaskView { gMockPlatformView.backgroundColor = UIColor.redColor; XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; - [mockFlutterView addSubview:childClippingView]; + [flutterView addSubview:childClippingView]; - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; + [flutterView setNeedsLayout]; + [flutterView layoutIfNeeded]; XCTAssertNil(childClippingView.maskView); } - (void)testClipRRectOnlyHasCornersInterceptWithPlatformViewShouldAddMaskView { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest"); + auto thread = std::make_unique("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(thread_task_runner); + /*platform=*/thread->GetTaskRunner(), + /*raster=*/thread->GetTaskRunner(), + /*ui=*/thread->GetTaskRunner(), + /*io=*/thread->GetTaskRunner()); + auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -1762,8 +1754,8 @@ - (void)testClipRRectOnlyHasCornersInterceptWithPlatformViewShouldAddMaskView { XCTAssertNotNil(gMockPlatformView); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 30, 30)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 30, 30)]; + flutterPlatformViewsController->SetFlutterView(flutterView); // Create embedded view params flutter::MutatorsStack stack; // Layer tree always pushes a screen scale factor to the stack. @@ -1791,24 +1783,24 @@ - (void)testClipRRectOnlyHasCornersInterceptWithPlatformViewShouldAddMaskView { gMockPlatformView.backgroundColor = UIColor.redColor; XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; - [mockFlutterView addSubview:childClippingView]; + [flutterView addSubview:childClippingView]; - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; + [flutterView setNeedsLayout]; + [flutterView layoutIfNeeded]; XCTAssertNotNil(childClippingView.maskView); } - (void)testClipRect { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest"); + auto thread = std::make_unique("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(thread_task_runner); + /*platform=*/thread->GetTaskRunner(), + /*raster=*/thread->GetTaskRunner(), + /*ui=*/thread->GetTaskRunner(), + /*io=*/thread->GetTaskRunner()); + auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -1834,8 +1826,8 @@ - (void)testClipRect { XCTAssertNotNil(gMockPlatformView); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; + flutterPlatformViewsController->SetFlutterView(flutterView); // Create embedded view params flutter::MutatorsStack stack; // Layer tree always pushes a screen scale factor to the stack @@ -1857,16 +1849,16 @@ - (void)testClipRect { gMockPlatformView.backgroundColor = UIColor.redColor; XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; - [mockFlutterView addSubview:childClippingView]; + [flutterView addSubview:childClippingView]; - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; + [flutterView setNeedsLayout]; + [flutterView layoutIfNeeded]; CGRect insideClipping = CGRectMake(2, 2, 3, 3); for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; j++) { CGPoint point = CGPointMake(i, j); - int alpha = [self alphaOfPoint:CGPointMake(i, j) onView:mockFlutterView]; + int alpha = [self alphaOfPoint:CGPointMake(i, j) onView:flutterView]; if (CGRectContainsPoint(insideClipping, point)) { XCTAssertEqual(alpha, 255); } else { @@ -1878,14 +1870,14 @@ - (void)testClipRect { - (void)testClipRRect { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest"); + auto thread = std::make_unique("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(thread_task_runner); + /*platform=*/thread->GetTaskRunner(), + /*raster=*/thread->GetTaskRunner(), + /*ui=*/thread->GetTaskRunner(), + /*io=*/thread->GetTaskRunner()); + auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -1911,8 +1903,8 @@ - (void)testClipRRect { XCTAssertNotNil(gMockPlatformView); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; + flutterPlatformViewsController->SetFlutterView(flutterView); // Create embedded view params flutter::MutatorsStack stack; // Layer tree always pushes a screen scale factor to the stack @@ -1934,10 +1926,10 @@ - (void)testClipRRect { gMockPlatformView.backgroundColor = UIColor.redColor; XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; - [mockFlutterView addSubview:childClippingView]; + [flutterView addSubview:childClippingView]; - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; + [flutterView setNeedsLayout]; + [flutterView layoutIfNeeded]; /* ClippingMask outterClipping @@ -1964,7 +1956,7 @@ - (void)testClipRRect { for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; j++) { CGPoint point = CGPointMake(i, j); - int alpha = [self alphaOfPoint:CGPointMake(i, j) onView:mockFlutterView]; + int alpha = [self alphaOfPoint:CGPointMake(i, j) onView:flutterView]; if (CGRectContainsPoint(innerClipping1, point) || CGRectContainsPoint(innerClipping2, point)) { // Pixels inside either of the 2 inner clippings should be fully opaque. @@ -1982,14 +1974,14 @@ - (void)testClipRRect { - (void)testClipPath { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest"); + auto thread = std::make_unique("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(thread_task_runner); + /*platform=*/thread->GetTaskRunner(), + /*raster=*/thread->GetTaskRunner(), + /*ui=*/thread->GetTaskRunner(), + /*io=*/thread->GetTaskRunner()); + auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -2015,8 +2007,8 @@ - (void)testClipPath { XCTAssertNotNil(gMockPlatformView); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; + flutterPlatformViewsController->SetFlutterView(flutterView); // Create embedded view params flutter::MutatorsStack stack; // Layer tree always pushes a screen scale factor to the stack @@ -2039,10 +2031,10 @@ - (void)testClipPath { gMockPlatformView.backgroundColor = UIColor.redColor; XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]); ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview; - [mockFlutterView addSubview:childClippingView]; + [flutterView addSubview:childClippingView]; - [mockFlutterView setNeedsLayout]; - [mockFlutterView layoutIfNeeded]; + [flutterView setNeedsLayout]; + [flutterView layoutIfNeeded]; /* ClippingMask outterClipping @@ -2069,7 +2061,7 @@ - (void)testClipPath { for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; j++) { CGPoint point = CGPointMake(i, j); - int alpha = [self alphaOfPoint:CGPointMake(i, j) onView:mockFlutterView]; + int alpha = [self alphaOfPoint:CGPointMake(i, j) onView:flutterView]; if (CGRectContainsPoint(innerClipping1, point) || CGRectContainsPoint(innerClipping2, point)) { // Pixels inside either of the 2 inner clippings should be fully opaque. @@ -2087,14 +2079,14 @@ - (void)testClipPath { - (void)testSetFlutterViewControllerAfterCreateCanStillDispatchTouchEvents { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest"); + auto thread = std::make_unique("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(thread_task_runner); + /*platform=*/thread->GetTaskRunner(), + /*raster=*/thread->GetTaskRunner(), + /*ui=*/thread->GetTaskRunner(), + /*io=*/thread->GetTaskRunner()); + auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -2140,28 +2132,28 @@ - (void)testSetFlutterViewControllerAfterCreateCanStillDispatchTouchEvents { // Before setting flutter view controller, events are not dispatched. NSSet* touches1 = [[NSSet alloc] init]; id event1 = OCMClassMock([UIEvent class]); - id mockFlutterViewContoller = OCMClassMock([FlutterViewController class]); + id flutterViewContoller = OCMClassMock([FlutterViewController class]); [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1]; - OCMReject([mockFlutterViewContoller touchesBegan:touches1 withEvent:event1]); + OCMReject([flutterViewContoller touchesBegan:touches1 withEvent:event1]); // Set flutter view controller allows events to be dispatched. NSSet* touches2 = [[NSSet alloc] init]; id event2 = OCMClassMock([UIEvent class]); - flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller); + flutterPlatformViewsController->SetFlutterViewController(flutterViewContoller); [forwardGectureRecognizer touchesBegan:touches2 withEvent:event2]; - OCMVerify([mockFlutterViewContoller touchesBegan:touches2 withEvent:event2]); + OCMVerify([flutterViewContoller touchesBegan:touches2 withEvent:event2]); } - (void)testSetFlutterViewControllerInTheMiddleOfTouchEventShouldStillAllowGesturesToBeHandled { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest"); + auto thread = std::make_unique("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(thread_task_runner); + /*platform=*/thread->GetTaskRunner(), + /*raster=*/thread->GetTaskRunner(), + /*ui=*/thread->GetTaskRunner(), + /*io=*/thread->GetTaskRunner()); + auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -2203,15 +2195,15 @@ - (void)testSetFlutterViewControllerInTheMiddleOfTouchEventShouldStillAllowGestu break; } } - id mockFlutterViewContoller = OCMClassMock([FlutterViewController class]); + id flutterViewContoller = OCMClassMock([FlutterViewController class]); { // ***** Sequence 1, finishing touch event with touchEnded ***** // - flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller); + flutterPlatformViewsController->SetFlutterViewController(flutterViewContoller); NSSet* touches1 = [[NSSet alloc] init]; id event1 = OCMClassMock([UIEvent class]); [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1]; - OCMVerify([mockFlutterViewContoller touchesBegan:touches1 withEvent:event1]); + OCMVerify([flutterViewContoller touchesBegan:touches1 withEvent:event1]); flutterPlatformViewsController->SetFlutterViewController(nil); @@ -2219,33 +2211,33 @@ - (void)testSetFlutterViewControllerInTheMiddleOfTouchEventShouldStillAllowGestu NSSet* touches2 = [[NSSet alloc] init]; id event2 = OCMClassMock([UIEvent class]); [forwardGectureRecognizer touchesMoved:touches2 withEvent:event2]; - OCMVerify([mockFlutterViewContoller touchesMoved:touches2 withEvent:event2]); + OCMVerify([flutterViewContoller touchesMoved:touches2 withEvent:event2]); NSSet* touches3 = [[NSSet alloc] init]; id event3 = OCMClassMock([UIEvent class]); [forwardGectureRecognizer touchesEnded:touches3 withEvent:event3]; - OCMVerify([mockFlutterViewContoller touchesEnded:touches3 withEvent:event3]); + OCMVerify([flutterViewContoller touchesEnded:touches3 withEvent:event3]); // Now the 2nd touch sequence should not be allowed. NSSet* touches4 = [[NSSet alloc] init]; id event4 = OCMClassMock([UIEvent class]); [forwardGectureRecognizer touchesBegan:touches4 withEvent:event4]; - OCMReject([mockFlutterViewContoller touchesBegan:touches4 withEvent:event4]); + OCMReject([flutterViewContoller touchesBegan:touches4 withEvent:event4]); NSSet* touches5 = [[NSSet alloc] init]; id event5 = OCMClassMock([UIEvent class]); [forwardGectureRecognizer touchesEnded:touches5 withEvent:event5]; - OCMReject([mockFlutterViewContoller touchesEnded:touches5 withEvent:event5]); + OCMReject([flutterViewContoller touchesEnded:touches5 withEvent:event5]); } { // ***** Sequence 2, finishing touch event with touchCancelled ***** // - flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller); + flutterPlatformViewsController->SetFlutterViewController(flutterViewContoller); NSSet* touches1 = [[NSSet alloc] init]; id event1 = OCMClassMock([UIEvent class]); [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1]; - OCMVerify([mockFlutterViewContoller touchesBegan:touches1 withEvent:event1]); + OCMVerify([flutterViewContoller touchesBegan:touches1 withEvent:event1]); flutterPlatformViewsController->SetFlutterViewController(nil); @@ -2253,23 +2245,23 @@ - (void)testSetFlutterViewControllerInTheMiddleOfTouchEventShouldStillAllowGestu NSSet* touches2 = [[NSSet alloc] init]; id event2 = OCMClassMock([UIEvent class]); [forwardGectureRecognizer touchesMoved:touches2 withEvent:event2]; - OCMVerify([mockFlutterViewContoller touchesMoved:touches2 withEvent:event2]); + OCMVerify([flutterViewContoller touchesMoved:touches2 withEvent:event2]); NSSet* touches3 = [[NSSet alloc] init]; id event3 = OCMClassMock([UIEvent class]); [forwardGectureRecognizer touchesCancelled:touches3 withEvent:event3]; - OCMVerify([mockFlutterViewContoller forceTouchesCancelled:touches3]); + OCMVerify([flutterViewContoller forceTouchesCancelled:touches3]); // Now the 2nd touch sequence should not be allowed. NSSet* touches4 = [[NSSet alloc] init]; id event4 = OCMClassMock([UIEvent class]); [forwardGectureRecognizer touchesBegan:touches4 withEvent:event4]; - OCMReject([mockFlutterViewContoller touchesBegan:touches4 withEvent:event4]); + OCMReject([flutterViewContoller touchesBegan:touches4 withEvent:event4]); NSSet* touches5 = [[NSSet alloc] init]; id event5 = OCMClassMock([UIEvent class]); [forwardGectureRecognizer touchesEnded:touches5 withEvent:event5]; - OCMReject([mockFlutterViewContoller touchesEnded:touches5 withEvent:event5]); + OCMReject([flutterViewContoller touchesEnded:touches5 withEvent:event5]); } flutterPlatformViewsController->Reset(); @@ -2278,14 +2270,14 @@ - (void)testSetFlutterViewControllerInTheMiddleOfTouchEventShouldStillAllowGestu - (void) testSetFlutterViewControllerInTheMiddleOfTouchEventAllowsTheNewControllerToHandleSecondTouchSequence { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest"); + auto thread = std::make_unique("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(thread_task_runner); + /*platform=*/thread->GetTaskRunner(), + /*raster=*/thread->GetTaskRunner(), + /*ui=*/thread->GetTaskRunner(), + /*io=*/thread->GetTaskRunner()); + auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -2327,79 +2319,79 @@ - (void)testSetFlutterViewControllerInTheMiddleOfTouchEventShouldStillAllowGestu break; } } - id mockFlutterViewContoller = OCMClassMock([FlutterViewController class]); + id flutterViewContoller = OCMClassMock([FlutterViewController class]); - flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller); + flutterPlatformViewsController->SetFlutterViewController(flutterViewContoller); // The touches in this sequence requires 1 touch object, we always create the NSSet with one item. NSSet* touches1 = [NSSet setWithObject:@1]; id event1 = OCMClassMock([UIEvent class]); [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1]; - OCMVerify([mockFlutterViewContoller touchesBegan:touches1 withEvent:event1]); + OCMVerify([flutterViewContoller touchesBegan:touches1 withEvent:event1]); - FlutterViewController* mockFlutterViewContoller2 = OCMClassMock([FlutterViewController class]); - flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller2); + FlutterViewController* flutterViewContoller2 = OCMClassMock([FlutterViewController class]); + flutterPlatformViewsController->SetFlutterViewController(flutterViewContoller2); // Touch events should still send to the old FlutterViewController if FlutterViewController // is updated in between. NSSet* touches2 = [NSSet setWithObject:@1]; id event2 = OCMClassMock([UIEvent class]); [forwardGectureRecognizer touchesBegan:touches2 withEvent:event2]; - OCMVerify([mockFlutterViewContoller touchesBegan:touches2 withEvent:event2]); - OCMReject([mockFlutterViewContoller2 touchesBegan:touches2 withEvent:event2]); + OCMVerify([flutterViewContoller touchesBegan:touches2 withEvent:event2]); + OCMReject([flutterViewContoller2 touchesBegan:touches2 withEvent:event2]); NSSet* touches3 = [NSSet setWithObject:@1]; id event3 = OCMClassMock([UIEvent class]); [forwardGectureRecognizer touchesMoved:touches3 withEvent:event3]; - OCMVerify([mockFlutterViewContoller touchesMoved:touches3 withEvent:event3]); - OCMReject([mockFlutterViewContoller2 touchesMoved:touches3 withEvent:event3]); + OCMVerify([flutterViewContoller touchesMoved:touches3 withEvent:event3]); + OCMReject([flutterViewContoller2 touchesMoved:touches3 withEvent:event3]); NSSet* touches4 = [NSSet setWithObject:@1]; id event4 = OCMClassMock([UIEvent class]); [forwardGectureRecognizer touchesEnded:touches4 withEvent:event4]; - OCMVerify([mockFlutterViewContoller touchesEnded:touches4 withEvent:event4]); - OCMReject([mockFlutterViewContoller2 touchesEnded:touches4 withEvent:event4]); + OCMVerify([flutterViewContoller touchesEnded:touches4 withEvent:event4]); + OCMReject([flutterViewContoller2 touchesEnded:touches4 withEvent:event4]); NSSet* touches5 = [NSSet setWithObject:@1]; id event5 = OCMClassMock([UIEvent class]); [forwardGectureRecognizer touchesEnded:touches5 withEvent:event5]; - OCMVerify([mockFlutterViewContoller touchesEnded:touches5 withEvent:event5]); - OCMReject([mockFlutterViewContoller2 touchesEnded:touches5 withEvent:event5]); + OCMVerify([flutterViewContoller touchesEnded:touches5 withEvent:event5]); + OCMReject([flutterViewContoller2 touchesEnded:touches5 withEvent:event5]); // Now the 2nd touch sequence should go to the new FlutterViewController NSSet* touches6 = [NSSet setWithObject:@1]; id event6 = OCMClassMock([UIEvent class]); [forwardGectureRecognizer touchesBegan:touches6 withEvent:event6]; - OCMVerify([mockFlutterViewContoller2 touchesBegan:touches6 withEvent:event6]); - OCMReject([mockFlutterViewContoller touchesBegan:touches6 withEvent:event6]); + OCMVerify([flutterViewContoller2 touchesBegan:touches6 withEvent:event6]); + OCMReject([flutterViewContoller touchesBegan:touches6 withEvent:event6]); // Allow the touch events to finish NSSet* touches7 = [NSSet setWithObject:@1]; id event7 = OCMClassMock([UIEvent class]); [forwardGectureRecognizer touchesMoved:touches7 withEvent:event7]; - OCMVerify([mockFlutterViewContoller2 touchesMoved:touches7 withEvent:event7]); - OCMReject([mockFlutterViewContoller touchesMoved:touches7 withEvent:event7]); + OCMVerify([flutterViewContoller2 touchesMoved:touches7 withEvent:event7]); + OCMReject([flutterViewContoller touchesMoved:touches7 withEvent:event7]); NSSet* touches8 = [NSSet setWithObject:@1]; id event8 = OCMClassMock([UIEvent class]); [forwardGectureRecognizer touchesEnded:touches8 withEvent:event8]; - OCMVerify([mockFlutterViewContoller2 touchesEnded:touches8 withEvent:event8]); - OCMReject([mockFlutterViewContoller touchesEnded:touches8 withEvent:event8]); + OCMVerify([flutterViewContoller2 touchesEnded:touches8 withEvent:event8]); + OCMReject([flutterViewContoller touchesEnded:touches8 withEvent:event8]); flutterPlatformViewsController->Reset(); } - (void)testFlutterPlatformViewTouchesCancelledEventAreForcedToBeCancelled { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest"); + auto thread = std::make_unique("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(thread_task_runner); + /*platform=*/thread->GetTaskRunner(), + /*raster=*/thread->GetTaskRunner(), + /*ui=*/thread->GetTaskRunner(), + /*io=*/thread->GetTaskRunner()); + auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -2441,30 +2433,30 @@ - (void)testFlutterPlatformViewTouchesCancelledEventAreForcedToBeCancelled { break; } } - id mockFlutterViewContoller = OCMClassMock([FlutterViewController class]); + id flutterViewContoller = OCMClassMock([FlutterViewController class]); - flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller); + flutterPlatformViewsController->SetFlutterViewController(flutterViewContoller); NSSet* touches1 = [NSSet setWithObject:@1]; id event1 = OCMClassMock([UIEvent class]); [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1]; [forwardGectureRecognizer touchesCancelled:touches1 withEvent:event1]; - OCMVerify([mockFlutterViewContoller forceTouchesCancelled:touches1]); + OCMVerify([flutterViewContoller forceTouchesCancelled:touches1]); flutterPlatformViewsController->Reset(); } - (void)testFlutterPlatformViewControllerSubmitFrameWithoutFlutterViewNotCrashing { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest"); + auto thread = std::make_unique("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(thread_task_runner); + /*platform=*/thread->GetTaskRunner(), + /*raster=*/thread->GetTaskRunner(), + /*ui=*/thread->GetTaskRunner(), + /*io=*/thread->GetTaskRunner()); + auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -2528,14 +2520,14 @@ - (void)testFlutterPlatformViewControllerSubmitFrameWithoutFlutterViewNotCrashin - (void) testFlutterPlatformViewControllerResetDeallocsPlatformViewWhenRootViewsNotBindedToFlutterView { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest"); + auto thread = std::make_unique("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(thread_task_runner); + /*platform=*/thread->GetTaskRunner(), + /*raster=*/thread->GetTaskRunner(), + /*ui=*/thread->GetTaskRunner(), + /*io=*/thread->GetTaskRunner()); + auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -2546,8 +2538,8 @@ - (void)testFlutterPlatformViewControllerSubmitFrameWithoutFlutterViewNotCrashin /*worker_task_runner=*/nil, /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; + flutterPlatformViewsController->SetFlutterView(flutterView); FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; @@ -2570,8 +2562,6 @@ - (void)testFlutterPlatformViewControllerSubmitFrameWithoutFlutterViewNotCrashin std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); flutterPlatformViewsController->CompositeEmbeddedView(2); - flutterPlatformViewsController->CompositeWithParams( - 2, flutterPlatformViewsController->GetCompositionParams(2)); // Not calling |flutterPlatformViewsController::SubmitFrame| so that the platform views are not // added to flutter_view_. @@ -2584,14 +2574,14 @@ - (void)testFlutterPlatformViewControllerSubmitFrameWithoutFlutterViewNotCrashin - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest"); + auto thread = std::make_unique("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(thread_task_runner); + /*platform=*/thread->GetTaskRunner(), + /*raster=*/thread->GetTaskRunner(), + /*ui=*/thread->GetTaskRunner(), + /*io=*/thread->GetTaskRunner()); + auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -2602,8 +2592,8 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { /*worker_task_runner=*/nil, /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; + flutterPlatformViewsController->SetFlutterView(flutterView); FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; @@ -2649,14 +2639,14 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { - (void) testFlutterPlatformViewControllerSubmitFrameShouldOrderSubviewsCorrectlyWithDifferentViewHierarchy { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest"); + auto thread = std::make_unique("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(thread_task_runner); + /*platform=*/thread->GetTaskRunner(), + /*raster=*/thread->GetTaskRunner(), + /*ui=*/thread->GetTaskRunner(), + /*io=*/thread->GetTaskRunner()); + auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -2667,8 +2657,8 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { /*worker_task_runner=*/nil, /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; + flutterPlatformViewsController->SetFlutterView(flutterView); FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; @@ -2698,12 +2688,10 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { auto embeddedViewParams1 = std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1)); - flutterPlatformViewsController->CompositeEmbeddedView(0); auto embeddedViewParams2 = std::make_unique(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); @@ -2714,13 +2702,16 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); + auto latch = std::make_shared(1u); + thread->GetTaskRunner()->PostTask([&latch]() { latch->CountDown(); }); + latch->Wait(); + 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."); @@ -2731,12 +2722,10 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { embeddedViewParams2 = std::make_unique(finalMatrix, SkSize::Make(500, 500), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams2)); - flutterPlatformViewsController->CompositeEmbeddedView(1); embeddedViewParams1 = std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1)); - flutterPlatformViewsController->CompositeEmbeddedView(0); mock_sk_surface = SkSurfaces::Raster(image_info); mock_surface = std::make_unique( @@ -2746,6 +2735,10 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { XCTAssertTrue( flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface))); + latch = std::make_shared(1u); + thread->GetTaskRunner()->PostTask([&latch]() { latch->CountDown(); }); + latch->Wait(); + XCTAssertTrue([flutterView.subviews indexOfObject:clippingView1] > [flutterView.subviews indexOfObject:clippingView2], @"The first clipping view should be added after the second clipping view."); @@ -2754,14 +2747,14 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { - (void) testFlutterPlatformViewControllerSubmitFrameShouldOrderSubviewsCorrectlyWithSameViewHierarchy { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest"); + auto thread = std::make_unique("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(thread_task_runner); + /*platform=*/thread->GetTaskRunner(), + /*raster=*/thread->GetTaskRunner(), + /*ui=*/thread->GetTaskRunner(), + /*io=*/thread->GetTaskRunner()); + auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -2772,8 +2765,8 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { /*worker_task_runner=*/nil, /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; + flutterPlatformViewsController->SetFlutterView(flutterView); FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; @@ -2803,16 +2796,10 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { auto embeddedViewParams1 = std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1)); - flutterPlatformViewsController->CompositeEmbeddedView(0); - flutterPlatformViewsController->CompositeWithParams( - 0, flutterPlatformViewsController->GetCompositionParams(0)); auto embeddedViewParams2 = std::make_unique(finalMatrix, SkSize::Make(500, 500), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams2)); - flutterPlatformViewsController->CompositeEmbeddedView(1); - flutterPlatformViewsController->CompositeWithParams( - 1, flutterPlatformViewsController->GetCompositionParams(1)); // SKSurface is required if the root FlutterView is present. const SkImageInfo image_info = SkImageInfo::MakeN32Premul(1000, 1000); @@ -2825,11 +2812,13 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { XCTAssertTrue( flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface))); + auto latch = std::make_shared(1u); + thread->GetTaskRunner()->PostTask([&latch]() { latch->CountDown(); }); + latch->Wait(); // 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."); @@ -2840,16 +2829,10 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { embeddedViewParams1 = std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1)); - flutterPlatformViewsController->CompositeEmbeddedView(0); - flutterPlatformViewsController->CompositeWithParams( - 0, flutterPlatformViewsController->GetCompositionParams(0)); embeddedViewParams2 = std::make_unique(finalMatrix, SkSize::Make(500, 500), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams2)); - flutterPlatformViewsController->CompositeEmbeddedView(1); - flutterPlatformViewsController->CompositeWithParams( - 1, flutterPlatformViewsController->GetCompositionParams(1)); mock_sk_surface = SkSurfaces::Raster(image_info); mock_surface = std::make_unique( @@ -2859,6 +2842,10 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { XCTAssertTrue( flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface))); + latch = std::make_shared(1u); + thread->GetTaskRunner()->PostTask([&latch]() { latch->CountDown(); }); + latch->Wait(); + XCTAssertTrue([flutterView.subviews indexOfObject:clippingView1] < [flutterView.subviews indexOfObject:clippingView2], @"The first clipping view should be added before the second clipping view."); @@ -2952,14 +2939,14 @@ - (void)testMaskViewsReleasedWhenPoolIsReleased { - (void)testClipMaskViewIsReused { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest"); + auto thread = std::make_unique("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(thread_task_runner); + /*platform=*/thread->GetTaskRunner(), + /*raster=*/thread->GetTaskRunner(), + /*ui=*/thread->GetTaskRunner(), + /*io=*/thread->GetTaskRunner()); + auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -2984,8 +2971,8 @@ - (void)testClipMaskViewIsReused { result); XCTAssertNotNil(gMockPlatformView); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; + flutterPlatformViewsController->SetFlutterView(flutterView); // Create embedded view params flutter::MutatorsStack stack1; // Layer tree always pushes a screen scale factor to the stack @@ -3046,14 +3033,14 @@ - (void)testClipMaskViewIsReused { - (void)testDifferentClipMaskViewIsUsedForEachView { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest"); + auto thread = std::make_unique("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(thread_task_runner); + /*platform=*/thread->GetTaskRunner(), + /*raster=*/thread->GetTaskRunner(), + /*ui=*/thread->GetTaskRunner(), + /*io=*/thread->GetTaskRunner()); + auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -3088,8 +3075,8 @@ - (void)testDifferentClipMaskViewIsUsedForEachView { UIView* view2 = gMockPlatformView; XCTAssertNotNil(gMockPlatformView); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; + flutterPlatformViewsController->SetFlutterView(flutterView); // Create embedded view params flutter::MutatorsStack stack1; // Layer tree always pushes a screen scale factor to the stack @@ -3128,14 +3115,14 @@ - (void)testDifferentClipMaskViewIsUsedForEachView { - (void)testMaskViewUsesCAShapeLayerAsTheBackingLayer { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest"); + auto thread = std::make_unique("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(thread_task_runner); + /*platform=*/thread->GetTaskRunner(), + /*raster=*/thread->GetTaskRunner(), + /*ui=*/thread->GetTaskRunner(), + /*io=*/thread->GetTaskRunner()); + auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -3161,8 +3148,8 @@ - (void)testMaskViewUsesCAShapeLayerAsTheBackingLayer { result); XCTAssertNotNil(gMockPlatformView); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; + flutterPlatformViewsController->SetFlutterView(flutterView); // Create embedded view params flutter::MutatorsStack stack1; // Layer tree always pushes a screen scale factor to the stack @@ -3222,14 +3209,14 @@ - (BOOL)validateOneVisualEffectView:(UIView*)visualEffectView - (void)testDisposingViewInCompositionOrderDoNotCrash { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest"); + auto thread = std::make_unique("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(thread_task_runner); + /*platform=*/thread->GetTaskRunner(), + /*raster=*/thread->GetTaskRunner(), + /*ui=*/thread->GetTaskRunner(), + /*io=*/thread->GetTaskRunner()); + auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -3240,8 +3227,8 @@ - (void)testDisposingViewInCompositionOrderDoNotCrash { /*worker_task_runner=*/nil, /*is_gpu_disabled_jsync_switch=*/std::make_shared()); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; + flutterPlatformViewsController->SetFlutterView(flutterView); FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; @@ -3271,16 +3258,10 @@ - (void)testDisposingViewInCompositionOrderDoNotCrash { auto embeddedViewParams0 = std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams0)); - flutterPlatformViewsController->CompositeEmbeddedView(0); - flutterPlatformViewsController->CompositeWithParams( - 0, flutterPlatformViewsController->GetCompositionParams(0)); auto embeddedViewParams1 = std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1)); - flutterPlatformViewsController->CompositeEmbeddedView(1); - flutterPlatformViewsController->CompositeWithParams( - 1, flutterPlatformViewsController->GetCompositionParams(1)); XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 2UL); @@ -3303,6 +3284,10 @@ - (void)testDisposingViewInCompositionOrderDoNotCrash { XCTAssertTrue( flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface))); + auto latch = std::make_shared(1u); + thread->GetTaskRunner()->PostTask([&latch]() { latch->CountDown(); }); + latch->Wait(); + // Disposing won't remove embedded views until the view is removed from the composition_order_ XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 2UL); XCTAssertNotNil(flutterPlatformViewsController->GetPlatformViewByID(0)); @@ -3318,9 +3303,6 @@ - (void)testDisposingViewInCompositionOrderDoNotCrash { auto embeddedViewParams1 = std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1)); - flutterPlatformViewsController->CompositeEmbeddedView(1); - flutterPlatformViewsController->CompositeWithParams( - 1, flutterPlatformViewsController->GetCompositionParams(1)); const SkImageInfo image_info = SkImageInfo::MakeN32Premul(1000, 1000); sk_sp mock_sk_surface = SkSurfaces::Raster(image_info); @@ -3332,6 +3314,10 @@ - (void)testDisposingViewInCompositionOrderDoNotCrash { XCTAssertTrue( flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface))); + auto latch = std::make_shared(1u); + thread->GetTaskRunner()->PostTask([&latch]() { latch->CountDown(); }); + latch->Wait(); + // Disposing won't remove embedded views until the view is removed from the composition_order_ XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 1UL); XCTAssertNil(flutterPlatformViewsController->GetPlatformViewByID(0)); @@ -3340,14 +3326,14 @@ - (void)testDisposingViewInCompositionOrderDoNotCrash { } - (void)testOnlyPlatformViewsAreRemovedWhenReset { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest"); + auto thread = std::make_unique("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(thread_task_runner); + /*platform=*/thread->GetTaskRunner(), + /*raster=*/thread->GetTaskRunner(), + /*ui=*/thread->GetTaskRunner(), + /*io=*/thread->GetTaskRunner()); + auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -3370,8 +3356,8 @@ - (void)testOnlyPlatformViewsAreRemovedWhenReset { methodCallWithMethodName:@"create" arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], result); - UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; + flutterPlatformViewsController->SetFlutterView(flutterView); // Create embedded view params flutter::MutatorsStack stack; // Layer tree always pushes a screen scale factor to the stack @@ -3389,8 +3375,6 @@ - (void)testOnlyPlatformViewsAreRemovedWhenReset { flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); flutterPlatformViewsController->CompositeEmbeddedView(2); - flutterPlatformViewsController->CompositeWithParams( - 2, flutterPlatformViewsController->GetCompositionParams(2)); // SKSurface is required if the root FlutterView is present. const SkImageInfo image_info = SkImageInfo::MakeN32Premul(1000, 1000); @@ -3403,12 +3387,16 @@ - (void)testOnlyPlatformViewsAreRemovedWhenReset { flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface)); + auto latch = std::make_shared(1u); + thread->GetTaskRunner()->PostTask([&latch]() { latch->CountDown(); }); + latch->Wait(); + UIView* someView = [[UIView alloc] init]; - [mockFlutterView addSubview:someView]; + [flutterView addSubview:someView]; flutterPlatformViewsController->Reset(); - XCTAssertEqual(mockFlutterView.subviews.count, 1u); - XCTAssertEqual(mockFlutterView.subviews.firstObject, someView); + XCTAssertEqual(flutterView.subviews.count, 1u); + XCTAssertEqual(flutterView.subviews.firstObject, someView); } - (void)testFlutterTouchInterceptingViewLinksToAccessibilityContainer { diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index 276601e8addfa..74f375c0acdf0 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -213,12 +213,14 @@ class FlutterPlatformViewLayerPool { class FlutterPlatformViewsController { public: - explicit FlutterPlatformViewsController(const fml::RefPtr& platform_task_runner); + FlutterPlatformViewsController(); ~FlutterPlatformViewsController(); fml::WeakPtr GetWeakPtr(); + void SetTaskRunner(const fml::RefPtr& platform_task_runner); + void SetFlutterView(UIView* flutter_view) __attribute__((cf_audited_transfer)); void SetFlutterViewController(UIViewController* flutter_view_controller) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm index b3a1083048a68..83d01ec2c6901 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm @@ -27,10 +27,8 @@ FlutterPlatformViewLayer::~FlutterPlatformViewLayer() = default; -FlutterPlatformViewsController::FlutterPlatformViewsController( - const fml::RefPtr& platform_task_runner) +FlutterPlatformViewsController::FlutterPlatformViewsController() : layer_pool_(std::make_unique()), - platform_task_runner_(platform_task_runner), weak_factory_(std::make_unique>(this)) { mask_view_pool_.reset( [[FlutterClippingMaskViewPool alloc] initWithCapacity:kFlutterClippingMaskViewPoolCapacity]); @@ -38,6 +36,11 @@ FlutterPlatformViewsController::~FlutterPlatformViewsController() = default; +void FlutterPlatformViewsController::SetTaskRunner( + const fml::RefPtr& platform_task_runner) { + platform_task_runner_ = platform_task_runner; +} + fml::WeakPtr FlutterPlatformViewsController::GetWeakPtr() { return weak_factory_->GetWeakPtr(); } diff --git a/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm b/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm index b5f4e001e2802..81b79a386ea70 100644 --- a/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm +++ b/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm @@ -280,7 +280,8 @@ - (void)testSemanticsDeallocated { /*io=*/thread_task_runner); auto flutterPlatformViewsController = - std::make_shared(thread_task_runner); + std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(thread_task_runner); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -341,7 +342,8 @@ - (void)testSemanticsDeallocatedWithoutLoadingView { /*io=*/thread_task_runner); auto flutterPlatformViewsController = - std::make_shared(thread_task_runner); + std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(thread_task_runner); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -387,8 +389,8 @@ - (void)testReplacedSemanticsDoesNotCleanupChildren { /*ui=*/thread_task_runner, /*io=*/thread_task_runner); - auto flutterPlatformViewsController = - std::make_shared(thread_task_runner); + auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(thread_task_runner); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -484,8 +486,8 @@ - (void)testScrollableSemanticsDeallocated { /*ui=*/thread_task_runner, /*io=*/thread_task_runner); - auto flutterPlatformViewsController = - std::make_shared(thread_task_runner); + auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(thread_task_runner); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -559,8 +561,8 @@ - (void)testBridgeReplacesSemanticsNode { /*ui=*/thread_task_runner, /*io=*/thread_task_runner); - auto flutterPlatformViewsController = - std::make_shared(thread_task_runner); + auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(thread_task_runner); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -2180,7 +2182,9 @@ - (void)testPlatformViewDestructorDoesNotCallSemanticsAPIs { id mockFlutterViewController = OCMClassMock([FlutterViewController class]); auto flutterPlatformViewsController = - std::make_shared(thread_task_runner); + std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(thread_task_runner); + OCMStub([mockFlutterViewController platformViewsController]) .andReturn(flutterPlatformViewsController.get()); auto weakFactory = std::make_unique>( From b6c3345e608b6243cc41b2615f5f0818119f7055 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Wed, 24 Jul 2024 10:59:52 -0700 Subject: [PATCH 26/49] merge in view slicer. --- ci/licenses_golden/excluded_files | 1 + ci/licenses_golden/licenses_flutter | 4 + flow/BUILD.gn | 3 + flow/view_slicer.cc | 116 +++++++++ flow/view_slicer.h | 25 ++ flow/view_slicer_unittests.cc | 138 +++++++++++ .../external_view_embedder.cc | 74 +----- .../framework/Source/FlutterPlatformViews.mm | 227 ++++++++---------- .../Source/FlutterPlatformViewsTest.mm | 4 +- .../Source/FlutterPlatformViews_Internal.h | 9 +- .../UnobstructedPlatformViewTests.m | 24 +- 11 files changed, 402 insertions(+), 223 deletions(-) create mode 100644 flow/view_slicer.cc create mode 100644 flow/view_slicer.h create mode 100644 flow/view_slicer_unittests.cc diff --git a/ci/licenses_golden/excluded_files b/ci/licenses_golden/excluded_files index c9d5aa13b609d..37291bb3a9ae5 100644 --- a/ci/licenses_golden/excluded_files +++ b/ci/licenses_golden/excluded_files @@ -80,6 +80,7 @@ ../../../flutter/flow/surface_frame_unittests.cc ../../../flutter/flow/testing ../../../flutter/flow/texture_unittests.cc +../../../flutter/flow/view_slicer_unittests.cc ../../../flutter/flutter_frontend_server ../../../flutter/fml/ascii_trie_unittests.cc ../../../flutter/fml/backtrace_unittests.cc diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 3dcb5429fe2a0..f049bc5a73d7e 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -41734,6 +41734,8 @@ ORIGIN: ../../../flutter/flow/surface.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/flow/surface.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/flow/surface_frame.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/flow/surface_frame.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/flow/view_slicer.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/flow/view_slicer.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/flutter_vma/flutter_skia_vma.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/flutter_vma/flutter_skia_vma.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/flutter_vma/flutter_vma.cc + ../../../flutter/LICENSE @@ -44616,6 +44618,8 @@ FILE: ../../../flutter/flow/surface.cc FILE: ../../../flutter/flow/surface.h FILE: ../../../flutter/flow/surface_frame.cc FILE: ../../../flutter/flow/surface_frame.h +FILE: ../../../flutter/flow/view_slicer.cc +FILE: ../../../flutter/flow/view_slicer.h FILE: ../../../flutter/flutter_vma/flutter_skia_vma.cc FILE: ../../../flutter/flutter_vma/flutter_skia_vma.h FILE: ../../../flutter/flutter_vma/flutter_vma.cc diff --git a/flow/BUILD.gn b/flow/BUILD.gn index 9ea33535d7070..cfa3fbe1f8712 100644 --- a/flow/BUILD.gn +++ b/flow/BUILD.gn @@ -88,6 +88,8 @@ source_set("flow") { "surface.h", "surface_frame.cc", "surface_frame.h", + "view_slicer.cc", + "view_slicer.h", ] public_configs = [ "//flutter:config" ] @@ -183,6 +185,7 @@ if (enable_unittests) { "testing/mock_layer_unittests.cc", "testing/mock_texture_unittests.cc", "texture_unittests.cc", + "view_slicer_unittests.cc", ] deps = [ diff --git a/flow/view_slicer.cc b/flow/view_slicer.cc new file mode 100644 index 0000000000000..f2d7afea1f3d8 --- /dev/null +++ b/flow/view_slicer.cc @@ -0,0 +1,116 @@ +// Copyright 2013 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. + +#include "flutter/flow/view_slicer.h" + +#include +#include "flow/embedded_views.h" +#include "fml/logging.h" + +namespace flutter { + +std::unordered_map SliceViews( + DlCanvas* background_canvas, + const std::vector& composition_order, + const std::unordered_map>& + slices, + const std::unordered_map& view_rects) { + std::unordered_map overlay_layers; + + auto current_frame_view_count = composition_order.size(); + + // Restore the clip context after exiting this method since it's changed + // below. + DlAutoCanvasRestore save(background_canvas, /*do_save=*/true); + + for (size_t i = 0; i < current_frame_view_count; i++) { + int64_t view_id = composition_order[i]; + EmbedderViewSlice* slice = slices.at(view_id).get(); + if (slice->canvas() == nullptr) { + continue; + } + + slice->end_recording(); + + SkRect full_joined_rect = SkRect::MakeEmpty(); + + // Determinate if Flutter UI intersects with any of the previous + // platform views stacked by z position. + // + // This is done by querying the r-tree that holds the records for the + // picture recorder corresponding to the flow layers added after a platform + // view layer. + for (int j = i; j >= 0; j--) { + int64_t current_view_id = composition_order[j]; + auto maybe_rect = view_rects.find(current_view_id); + FML_DCHECK(maybe_rect != view_rects.end()); + if (maybe_rect == view_rects.end()) { + continue; + } + + SkRect current_view_rect = maybe_rect->second; + const SkIRect rounded_in_platform_view_rect = current_view_rect.roundIn(); + + // Each rect corresponds to a native view that renders Flutter UI. + std::vector intersection_rects = + slice->region(current_view_rect).getRects(); + + // Ignore intersections of single width/height on the edge of the platform + // view. + // This is to address the following performance issue when interleaving + // adjacent platform views and layers: Since we `roundOut` both platform + // view rects and the layer rects, as long as the coordinate is + // fractional, there will be an intersection of a single pixel width (or + // height) after rounding out, even if they do not intersect before + // rounding out. We have to round out both platform view rect and the + // layer rect. Rounding in platform view rect will result in missing pixel + // on the intersection edge. Rounding in layer rect will result in missing + // pixel on the edge of the layer on top of the platform view. + for (auto it = intersection_rects.begin(); it != intersection_rects.end(); + /*no-op*/) { + // If intersection_rect does not intersect with the *rounded in* + // platform view rect, then the intersection must be a single pixel + // width (or height) on edge. + if (!SkIRect::Intersects(*it, rounded_in_platform_view_rect)) { + it = intersection_rects.erase(it); + } else { + ++it; + } + } + + // Limit the number of native views, so it doesn't grow forever. + // + // In this case, the rects are merged into a single one that is the union + // of all the rects. + SkRect partial_joined_rect = SkRect::MakeEmpty(); + for (const SkIRect& rect : intersection_rects) { + partial_joined_rect.join(SkRect::Make(rect)); + } + + // Get the intersection rect with the `current_view_rect`, + partial_joined_rect.intersect(SkRect::Make(current_view_rect.roundOut())); + + // Join the `partial_joined_rect` into `full_joined_rect` to get the rect + // above the current `slice` + full_joined_rect.join(partial_joined_rect); + } + + if (!full_joined_rect.isEmpty()) { + overlay_layers.insert({view_id, full_joined_rect}); + + // Clip the background canvas, so it doesn't contain any of the pixels + // drawn on the overlay layer. + background_canvas->ClipRect(full_joined_rect, + DlCanvas::ClipOp::kDifference); + } + slice->render_into(background_canvas); + } + + // Manually trigger the DlAutoCanvasRestore before we submit the frame + save.Restore(); + + return overlay_layers; +} + +} // namespace flutter diff --git a/flow/view_slicer.h b/flow/view_slicer.h new file mode 100644 index 0000000000000..74f5f5eafc354 --- /dev/null +++ b/flow/view_slicer.h @@ -0,0 +1,25 @@ +// Copyright 2013 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. + +#ifndef FLUTTER_FLOW_VIEW_SLICER_H_ +#define FLUTTER_FLOW_VIEW_SLICER_H_ + +#include +#include "display_list/dl_canvas.h" +#include "flow/embedded_views.h" + +namespace flutter { + +/// @brief Compute the required overlay layers and clip the view slices +/// according to the size and position of the platform views. +std::unordered_map SliceViews( + DlCanvas* background_canvas, + const std::vector& composition_order, + const std::unordered_map>& + slices, + const std::unordered_map& view_rects); + +} // namespace flutter + +#endif // FLUTTER_FLOW_VIEW_SLICER_H_ diff --git a/flow/view_slicer_unittests.cc b/flow/view_slicer_unittests.cc new file mode 100644 index 0000000000000..12829b07fb6f6 --- /dev/null +++ b/flow/view_slicer_unittests.cc @@ -0,0 +1,138 @@ +// Copyright 2013 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. + +#include +#include "display_list/dl_builder.h" +#include "flow/embedded_views.h" +#include "flutter/flow/view_slicer.h" +#include "gtest/gtest.h" + +namespace flutter { +namespace testing { + +namespace { +void AddSliceOfSize( + std::unordered_map>& slices, + int64_t id, + SkRect rect) { + slices[id] = std::make_unique(rect); + DlPaint paint; + paint.setColor(DlColor::kBlack()); + slices[id]->canvas()->DrawRect(rect, paint); +} +} // namespace + +TEST(ViewSlicerTest, CanSlicerNonOverlappingViews) { + DisplayListBuilder builder(SkRect::MakeLTRB(0, 0, 100, 100)); + + std::vector composition_order = {1}; + std::unordered_map> slices; + AddSliceOfSize(slices, 1, SkRect::MakeLTRB(99, 99, 100, 100)); + + std::unordered_map view_rects = { + {1, SkRect::MakeLTRB(50, 50, 60, 60)}}; + + auto computed_overlays = + SliceViews(&builder, composition_order, slices, view_rects); + + EXPECT_TRUE(computed_overlays.empty()); +} + +TEST(ViewSlicerTest, IgnoresFractionalOverlaps) { + DisplayListBuilder builder(SkRect::MakeLTRB(0, 0, 100, 100)); + + std::vector composition_order = {1}; + std::unordered_map> slices; + AddSliceOfSize(slices, 1, SkRect::MakeLTRB(0, 0, 50.49, 50.49)); + + std::unordered_map view_rects = { + {1, SkRect::MakeLTRB(50.5, 50.5, 100, 100)}}; + + auto computed_overlays = + SliceViews(&builder, composition_order, slices, view_rects); + + EXPECT_TRUE(computed_overlays.empty()); +} + +TEST(ViewSlicerTest, ComputesOverlapWith1PV) { + DisplayListBuilder builder(SkRect::MakeLTRB(0, 0, 100, 100)); + + std::vector composition_order = {1}; + std::unordered_map> slices; + AddSliceOfSize(slices, 1, SkRect::MakeLTRB(0, 0, 50, 50)); + + std::unordered_map view_rects = { + {1, SkRect::MakeLTRB(0, 0, 100, 100)}}; + + auto computed_overlays = + SliceViews(&builder, composition_order, slices, view_rects); + + EXPECT_EQ(computed_overlays.size(), 1u); + auto overlay = computed_overlays.find(1); + ASSERT_NE(overlay, computed_overlays.end()); + + EXPECT_EQ(overlay->second, SkRect::MakeLTRB(0, 0, 50, 50)); +} + +TEST(ViewSlicerTest, ComputesOverlapWith2PV) { + DisplayListBuilder builder(SkRect::MakeLTRB(0, 0, 100, 100)); + + std::vector composition_order = {1, 2}; + std::unordered_map> slices; + AddSliceOfSize(slices, 1, SkRect::MakeLTRB(0, 0, 50, 50)); + AddSliceOfSize(slices, 2, SkRect::MakeLTRB(50, 50, 100, 100)); + + std::unordered_map view_rects = { + {1, SkRect::MakeLTRB(0, 0, 50, 50)}, // + {2, SkRect::MakeLTRB(50, 50, 100, 100)}, // + }; + + auto computed_overlays = + SliceViews(&builder, composition_order, slices, view_rects); + + EXPECT_EQ(computed_overlays.size(), 2u); + + auto overlay = computed_overlays.find(1); + ASSERT_NE(overlay, computed_overlays.end()); + + EXPECT_EQ(overlay->second, SkRect::MakeLTRB(0, 0, 50, 50)); + + overlay = computed_overlays.find(2); + ASSERT_NE(overlay, computed_overlays.end()); + EXPECT_EQ(overlay->second, SkRect::MakeLTRB(50, 50, 100, 100)); +} + +TEST(ViewSlicerTest, OverlappingTwoPVs) { + DisplayListBuilder builder(SkRect::MakeLTRB(0, 0, 100, 100)); + + std::vector composition_order = {1, 2}; + std::unordered_map> slices; + // This embeded view overlaps both platform views: + // + // [ A [ ]] + // [_____[ C ]] + // [ B [ ]] + // [ ] + AddSliceOfSize(slices, 1, SkRect::MakeLTRB(0, 0, 0, 0)); + AddSliceOfSize(slices, 2, SkRect::MakeLTRB(0, 0, 100, 100)); + + std::unordered_map view_rects = { + {1, SkRect::MakeLTRB(0, 0, 50, 50)}, // + {2, SkRect::MakeLTRB(50, 50, 100, 100)}, // + }; + + auto computed_overlays = + SliceViews(&builder, composition_order, slices, view_rects); + + EXPECT_EQ(computed_overlays.size(), 1u); + + auto overlay = computed_overlays.find(2); + ASSERT_NE(overlay, computed_overlays.end()); + + // We create a single overlay for both overlapping sections. + EXPECT_EQ(overlay->second, SkRect::MakeLTRB(0, 0, 100, 100)); +} + +} // namespace testing +} // namespace flutter diff --git a/shell/platform/android/external_view_embedder/external_view_embedder.cc b/shell/platform/android/external_view_embedder/external_view_embedder.cc index f6ce9eb161273..a957df9a54042 100644 --- a/shell/platform/android/external_view_embedder/external_view_embedder.cc +++ b/shell/platform/android/external_view_embedder/external_view_embedder.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "flutter/shell/platform/android/external_view_embedder/external_view_embedder.h" +#include "flow/view_slicer.h" #include "flutter/common/constants.h" #include "flutter/fml/synchronization/waitable_event.h" #include "flutter/fml/trace_event.h" @@ -78,72 +79,17 @@ void AndroidExternalViewEmbedder::SubmitFlutterView( return; } - std::unordered_map overlay_layers; - DlCanvas* background_canvas = frame->Canvas(); - auto current_frame_view_count = composition_order_.size(); - - // Restore the clip context after exiting this method since it's changed - // below. - DlAutoCanvasRestore save(background_canvas, /*do_save=*/true); - - for (size_t i = 0; i < current_frame_view_count; i++) { - int64_t view_id = composition_order_[i]; - EmbedderViewSlice* slice = slices_.at(view_id).get(); - if (slice->canvas() == nullptr) { - continue; - } - - slice->end_recording(); - - SkRect full_joined_rect = SkRect::MakeEmpty(); - - // Determinate if Flutter UI intersects with any of the previous - // platform views stacked by z position. - // - // This is done by querying the r-tree that holds the records for the - // picture recorder corresponding to the flow layers added after a platform - // view layer. - for (ssize_t j = i; j >= 0; j--) { - int64_t current_view_id = composition_order_[j]; - SkRect current_view_rect = GetViewRect(current_view_id); - // The rect above the `current_view_rect` - SkRect partial_joined_rect = SkRect::MakeEmpty(); - // Each rect corresponds to a native view that renders Flutter UI. - std::vector intersection_rects = - slice->region(current_view_rect).getRects(); - - // Limit the number of native views, so it doesn't grow forever. - // - // In this case, the rects are merged into a single one that is the union - // of all the rects. - for (const SkIRect& rect : intersection_rects) { - partial_joined_rect.join(SkRect::Make(rect)); - } - // Get the intersection rect with the `current_view_rect`, - partial_joined_rect.intersect(current_view_rect); - // Join the `partial_joined_rect` into `full_joined_rect` to get the rect - // above the current `slice` - full_joined_rect.join(partial_joined_rect); - } - if (!full_joined_rect.isEmpty()) { - // Subpixels in the platform may not align with the canvas subpixels. - // - // To workaround it, round the floating point bounds and make the rect - // slightly larger. - // - // For example, {0.3, 0.5, 3.1, 4.7} becomes {0, 0, 4, 5}. - full_joined_rect.set(full_joined_rect.roundOut()); - overlay_layers.insert({view_id, full_joined_rect}); - // Clip the background canvas, so it doesn't contain any of the pixels - // drawn on the overlay layer. - background_canvas->ClipRect(full_joined_rect, - DlCanvas::ClipOp::kDifference); - } - slice->render_into(background_canvas); + std::unordered_map view_rects; + for (auto platform_id : composition_order_) { + view_rects[platform_id] = GetViewRect(platform_id); } - // Manually trigger the DlAutoCanvasRestore before we submit the frame - save.Restore(); + std::unordered_map overlay_layers = + SliceViews(frame->Canvas(), // + composition_order_, // + slices_, // + view_rects // + ); // Submit the background canvas frame before switching the GL context to // the overlay surfaces. diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 7962892a58ac1..82586167f26a7 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -3,6 +3,7 @@ // found in the LICENSE file. #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h" +#include "fml/synchronization/count_down_latch.h" #include #include @@ -10,6 +11,7 @@ #include "flow/surface_frame.h" #include "fml/logging.h" +#include "flutter/flow/view_slicer.h" #include "flutter/fml/make_copyable.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterOverlayView.h" @@ -639,130 +641,98 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, return background_frame->Submit(); } - DlCanvas* background_canvas = background_frame->Canvas(); - - // Clipping the background canvas before drawing the picture recorders requires - // saving and restoring the clip context. - DlAutoCanvasRestore save(background_canvas, /*do_save=*/true); - - // Maps a platform view id to a vector of `FlutterPlatformViewLayer`. LayersMap platform_view_layers; - - bool did_submit = true; - size_t num_platform_views = composition_order_.size(); - - size_t missing_layer_count = 0; - - // Pending Submit Callbacks std::vector callbacks; + auto did_submit = true; + std::unordered_map view_rects; - for (size_t i = 0; i < num_platform_views; i++) { - int64_t platform_view_id = composition_order_[i]; - EmbedderViewSlice* slice = slices_[platform_view_id].get(); - slice->end_recording(); - - // Check if the current picture contains overlays that intersect with the - // current platform view or any of the previous platform views. - for (size_t j = i + 1; j > 0; j--) { - int64_t current_platform_view_id = composition_order_[j - 1]; - SkRect platform_view_rect = - current_composition_params_[current_platform_view_id].finalBoundingRect(); - - std::vector intersection_rects = slice->region(platform_view_rect).getRects(); - const SkIRect rounded_in_platform_view_rect = platform_view_rect.roundIn(); - // Ignore intersections of single width/height on the edge of the platform view. - // This is to address the following performance issue when interleaving adjacent - // platform views and layers: - // Since we `roundOut` both platform view rects and the layer rects, as long as - // the coordinate is fractional, there will be an intersection of a single pixel width - // (or height) after rounding out, even if they do not intersect before rounding out. - // We have to round out both platform view rect and the layer rect. - // Rounding in platform view rect will result in missing pixel on the intersection edge. - // Rounding in layer rect will result in missing pixel on the edge of the layer on top - // of the platform view. - for (auto it = intersection_rects.begin(); it != intersection_rects.end(); /*no-op*/) { - // If intersection_rect does not intersect with the *rounded in* platform - // view rect, then the intersection must be a single pixel width (or height) on edge. - if (!SkIRect::Intersects(*it, rounded_in_platform_view_rect)) { - it = intersection_rects.erase(it); - } else { - ++it; - } - } + for (int64_t view_id : composition_order_) { + view_rects[view_id] = current_composition_params_[view_id].finalBoundingRect(); + } - size_t allocation_size = intersection_rects.size(); + std::unordered_map overlay_layers = + SliceViews(background_frame->Canvas(), composition_order_, slices_, view_rects); - // For testing purposes, the overlay id is used to find the overlay view. - // This is the index of the layer for the current platform view. - auto overlay_id = platform_view_layers[current_platform_view_id].size(); + size_t required_overlay_layers = 0; + for (int64_t view_id : composition_order_) { + std::unordered_map::const_iterator overlay = overlay_layers.find(view_id); + if (overlay == overlay_layers.end()) { + continue; + } + required_overlay_layers++; + } - // If the max number of allocations per platform view is exceeded, - // then join all the rects into a single one. - if (allocation_size > kMaxLayerAllocations) { - SkIRect joined_rect = SkIRect::MakeEmpty(); - for (const SkIRect& rect : intersection_rects) { - joined_rect.join(rect); - } - // Replace the rects in the intersection rects list for a single rect that is - // the union of all the rects in the list. - intersection_rects.clear(); - intersection_rects.push_back(joined_rect); + // If there are not sufficient overlay layers, we must construct them on the platform + // thread, at least until we've refactored iOS surface creation to use IOSurfaces + // instead of CALayers. + if (required_overlay_layers > layer_pool_->size()) { + auto missing_layer_count = required_overlay_layers - layer_pool_->size(); + TRACE_EVENT0("flutter", "FlutterPlatformViewsController::CreateMissingLayers"); + // Workaround for FLutterPlatformViewsTest + if ([NSThread currentThread] isMainThread]) { + // Create Missing Layers + for (auto i = 0u; i < missing_layer_count; i++) { + CreateLayer(gr_context, // + ios_context, // + ((FlutterView*)flutter_view_.get()).pixelFormat // + ); } - for (SkIRect& joined_rect : intersection_rects) { - // Get the intersection rect between the current rect - // and the platform view rect. - joined_rect.intersect(platform_view_rect.roundOut()); - // Clip the background canvas, so it doesn't contain any of the pixels drawn - // on the overlay layer. - background_canvas->ClipRect(SkRect::Make(joined_rect), DlCanvas::ClipOp::kDifference); - // Get a new host layer. - std::shared_ptr layer = GetExistingLayer(); - if (!layer) { - missing_layer_count++; - continue; + } else { + auto latch = std::make_shared(1u); + platform_task_runner_->PostTask([&]() { + // Create Missing Layers + for (auto i = 0u; i < missing_layer_count; i++) { + CreateLayer(gr_context, // + ios_context, // + ((FlutterView*)flutter_view_.get()).pixelFormat // + ); } + latch->CountDown(); + }); + latch->Wait(); + } + } - { - std::unique_ptr frame = layer->surface->AcquireFrame(frame_size_); - // If frame is null, AcquireFrame already printed out an error message. - if (!frame) { - continue; - } - DlCanvas* overlay_canvas = frame->Canvas(); - int restore_count = overlay_canvas->GetSaveCount(); - overlay_canvas->Save(); - overlay_canvas->ClipRect(SkRect::Make(joined_rect)); - // overlay_canvas->Clear(DlColor::kTransparent()); - slice->render_into(overlay_canvas); - overlay_canvas->RestoreToCount(restore_count); - - // This flutter view is never the last in a frame, since we always submit the - // underlay view last. - frame->set_submit_info( - {.frame_boundary = false, - .present_with_transaction = true, - .submit_receiver = [&callbacks](const SurfaceFrame::DeferredSubmit& cb) { - callbacks.push_back(cb); - }}); - - layer->did_submit_last_frame = frame->Submit(); - } + int64_t overlay_id = 0; + for (int64_t view_id : composition_order_) { + std::unordered_map::const_iterator overlay = overlay_layers.find(view_id); + if (overlay == overlay_layers.end()) { + continue; + } + std::shared_ptr layer = GetExistingLayer(); + if (!layer) { + continue; + } - did_submit &= layer->did_submit_last_frame; - platform_view_layers[current_platform_view_id].push_back( - {.rect = joined_rect, - .view_id = current_platform_view_id, - .overlay_id = static_cast(overlay_id), - .layer = layer}); - overlay_id++; - } + std::unique_ptr frame = layer->surface->AcquireFrame(frame_size_); + // If frame is null, AcquireFrame already printed out an error message. + if (!frame) { + continue; } - slice->render_into(background_canvas); + DlCanvas* overlay_canvas = frame->Canvas(); + int restore_count = overlay_canvas->GetSaveCount(); + overlay_canvas->Save(); + overlay_canvas->ClipRect(overlay->second); + slices_[view_id]->render_into(overlay_canvas); + overlay_canvas->RestoreToCount(restore_count); + + // This flutter view is never the last in a frame, since we always submit the + // underlay view last. + frame->set_submit_info( + {.frame_boundary = false, + .present_with_transaction = true, + .submit_receiver = [&callbacks](const SurfaceFrame::DeferredSubmit& cb) { + callbacks.push_back(cb); + }}); + + layer->did_submit_last_frame = frame->Submit(); + + did_submit &= layer->did_submit_last_frame; + platform_view_layers[view_id].push_back( + {.rect = overlay->second, .view_id = view_id, .overlay_id = overlay_id, .layer = layer}); + overlay_id++; } - // Manually trigger the SkAutoCanvasRestore before we submit the frame - save.Restore(); - background_frame->set_submit_info( {.present_with_transaction = true, .submit_receiver = [&callbacks](const SurfaceFrame::DeferredSubmit& cb) { @@ -772,19 +742,17 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, // Mark all layers as available, so they can be used in the next frame. std::vector> unused_layers = - layer_pool_->GetUnusedLayers(); - FML_DCHECK(unused_layers.empty() || missing_layer_count == 0); + layer_pool_->RemoveUnusedLayers(); layer_pool_->RecycleLayers(); // Dispose unused Flutter Views. auto views_to_dispose = DisposeViews(); - auto task = [&, platform_view_layers = std::move(platform_view_layers), - missing_layer_count, // - current_composition_params = current_composition_params_, // - views_to_recomposite = views_to_recomposite_, // - callbacks = callbacks, // - composition_order = composition_order_, // + auto task = [&, platform_view_layers = std::move(platform_view_layers), // + current_composition_params = current_composition_params_, // + views_to_recomposite = views_to_recomposite_, // + callbacks = callbacks, // + composition_order = composition_order_, // unused_layers = unused_layers, views_to_dispose]() mutable { TRACE_EVENT0("flutter", "FlutterPlatformViewsController::SubmitFrame::CATransaction"); @@ -793,8 +761,11 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, // Configure Flutter overlay views. for (const auto& [key, layers] : platform_view_layers) { for (const auto& layer_data : layers) { - layer_data.layer->UpdateViewState(flutter_view_, layer_data.rect, layer_data.view_id, - layer_data.overlay_id); + layer_data.layer->UpdateViewState(flutter_view_, // + layer_data.rect, // + layer_data.view_id, // + layer_data.overlay_id // + ); } } @@ -804,22 +775,15 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, } // Composite Platform Views. - for (auto view_id : views_to_recomposite) { + for (int64_t view_id : views_to_recomposite) { CompositeWithParams(view_id, current_composition_params[view_id]); } + // Present callbacks. for (const auto& cb : callbacks) { cb(); } - // Create Missing Layers - for (auto i = 0u; i < missing_layer_count; i++) { - CreateLayer(gr_context, // - ios_context, // - ((FlutterView*)flutter_view_.get()).pixelFormat // - ); - } - // Organize the layers by their z indexes. auto active_composition_order = BringLayersIntoView(platform_view_layers, composition_order); @@ -833,6 +797,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, [CATransaction commit]; }; + // Workaround for FlutterPlatformViewsTest.mm if ([[NSThread currentThread] isMainThread]) { task(); } else { @@ -893,7 +858,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, } void FlutterPlatformViewLayer::UpdateViewState(UIView* flutter_view, - SkIRect rect, + SkRect rect, int64_t view_id, int64_t overlay_id) { UIView* overlay_view_wrapper = this->overlay_view_wrapper.get(); diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm index 68160d89783a7..bfdf5083269bb 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm @@ -3418,9 +3418,9 @@ - (void)testLayerPool { auto pool = flutter::FlutterPlatformViewLayerPool{}; // Add layers to the pool. - auto layer1 = pool.GetLayer(gr_context.get(), ios_context, MTLPixelFormatBGRA8Unorm); + pool.CreateLayer(gr_context.get(), ios_context, MTLPixelFormatBGRA8Unorm); XCTAssertEqual(pool.size(), 1u); - auto layer2 = pool.GetLayer(gr_context.get(), ios_context, MTLPixelFormatBGRA8Unorm); + pool.CreateLayer(gr_context.get(), ios_context, MTLPixelFormatBGRA8Unorm); XCTAssertEqual(pool.size(), 2u); // Mark all layers as unused. diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index ca25c1636b9f9..419876ed8fe9d 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -142,7 +142,6 @@ void ResetAnchor(CALayer* layer); CGRect GetCGRectFromSkRect(const SkRect& clipSkRect); BOOL BlurRadiusEqualToBlurRadius(CGFloat radius1, CGFloat radius2); -class IOSContextGL; class IOSSurface; struct FlutterPlatformViewLayer { @@ -166,7 +165,7 @@ struct FlutterPlatformViewLayer { // so we can update the overlay with the new context. GrDirectContext* gr_context; - void UpdateViewState(UIView* flutter_view, SkIRect rect, int64_t view_id, int64_t overlay_id); + void UpdateViewState(UIView* flutter_view, SkRect rect, int64_t view_id, int64_t overlay_id); }; // This class isn't thread safe. @@ -302,10 +301,8 @@ class FlutterPlatformViewsController { } private: - static const size_t kMaxLayerAllocations = 2; - struct LayerData { - SkIRect rect; + SkRect rect; int64_t view_id; int64_t overlay_id; std::shared_ptr layer; @@ -369,7 +366,7 @@ class FlutterPlatformViewsController { // operation until the next platform view or the end of the last leaf node in the layer tree. // // The Slices are deleted by the FlutterPlatformViewsController.reset(). - std::map> slices_; + std::unordered_map> slices_; fml::scoped_nsobject channel_; fml::scoped_nsobject flutter_view_; diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/UnobstructedPlatformViewTests.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/UnobstructedPlatformViewTests.m index 5fc45893706f8..3f6014f3b2761 100644 --- a/testing/scenario_app/ios/Scenarios/ScenariosUITests/UnobstructedPlatformViewTests.m +++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/UnobstructedPlatformViewTests.m @@ -234,21 +234,14 @@ - (void)testMultiplePlatformViewsWithOverlays { XCTAssertEqual(platform_view2.frame.size.width, 250); XCTAssertEqual(platform_view2.frame.size.height, 250); - XCUIElement* overlay1 = app.otherElements[@"platform_view[0].overlay[0]"]; + XCUIElement* overlay1 = app.otherElements[@"platform_view[1].overlay[0]"]; XCTAssertTrue(overlay1.exists); XCTAssertEqual(overlay1.frame.origin.x, 25); - XCTAssertEqual(overlay1.frame.origin.y, 300); + XCTAssertEqual(overlay1.frame.origin.y, 0); XCTAssertEqual(overlay1.frame.size.width, 225); - XCTAssertEqual(overlay1.frame.size.height, 200); - - XCUIElement* overlay2 = app.otherElements[@"platform_view[1].overlay[0]"]; - XCTAssertTrue(overlay2.exists); - XCTAssertEqual(overlay2.frame.origin.x, 25); - XCTAssertEqual(overlay2.frame.origin.y, 0); - XCTAssertEqual(overlay2.frame.size.width, 225); - XCTAssertEqual(overlay2.frame.size.height, 250); + XCTAssertEqual(overlay1.frame.size.height, 500); - XCUIElement* overlayView0 = app.otherElements[@"platform_view[0].overlay_view[0]"]; + XCUIElement* overlayView0 = app.otherElements[@"platform_view[1].overlay_view[0]"]; XCTAssertTrue(overlayView0.exists); // Overlay should always be the same frame as the app. XCTAssertEqualWithAccuracy(overlayView0.frame.origin.x, app.frame.origin.x, kCompareAccuracy); @@ -256,15 +249,6 @@ - (void)testMultiplePlatformViewsWithOverlays { XCTAssertEqualWithAccuracy(overlayView0.frame.size.width, app.frame.size.width, kCompareAccuracy); XCTAssertEqualWithAccuracy(overlayView0.frame.size.height, app.frame.size.height, kCompareAccuracy); - - XCUIElement* overlayView1 = app.otherElements[@"platform_view[1].overlay_view[0]"]; - XCTAssertTrue(overlayView1.exists); - // Overlay should always be the same frame as the app. - XCTAssertEqualWithAccuracy(overlayView1.frame.origin.x, app.frame.origin.x, kCompareAccuracy); - XCTAssertEqualWithAccuracy(overlayView1.frame.origin.y, app.frame.origin.x, kCompareAccuracy); - XCTAssertEqualWithAccuracy(overlayView1.frame.size.width, app.frame.size.width, kCompareAccuracy); - XCTAssertEqualWithAccuracy(overlayView1.frame.size.height, app.frame.size.height, - kCompareAccuracy); } // More then two overlays are merged into a single layer. From cd8e1debd47b7522ddce3efd714d5d29d9fc8df4 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Thu, 25 Jul 2024 08:57:51 -0700 Subject: [PATCH 27/49] ++ --- .../darwin/ios/framework/Source/FlutterPlatformViews.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 82586167f26a7..5c2c71824c2f0 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -669,7 +669,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, auto missing_layer_count = required_overlay_layers - layer_pool_->size(); TRACE_EVENT0("flutter", "FlutterPlatformViewsController::CreateMissingLayers"); // Workaround for FLutterPlatformViewsTest - if ([NSThread currentThread] isMainThread]) { + if ([[NSThread currentThread] isMainThread]) { // Create Missing Layers for (auto i = 0u; i < missing_layer_count; i++) { CreateLayer(gr_context, // From 6603d182e5e548e841279f743eaa888f251befc8 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Thu, 25 Jul 2024 10:02:32 -0700 Subject: [PATCH 28/49] minor cleanups. --- .../framework/Source/FlutterPlatformViews.mm | 167 ++++++++++-------- .../Source/FlutterPlatformViews_Internal.h | 18 +- 2 files changed, 114 insertions(+), 71 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 5c2c71824c2f0..8dd44d2e8929a 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -666,31 +666,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, // thread, at least until we've refactored iOS surface creation to use IOSurfaces // instead of CALayers. if (required_overlay_layers > layer_pool_->size()) { - auto missing_layer_count = required_overlay_layers - layer_pool_->size(); - TRACE_EVENT0("flutter", "FlutterPlatformViewsController::CreateMissingLayers"); - // Workaround for FLutterPlatformViewsTest - if ([[NSThread currentThread] isMainThread]) { - // Create Missing Layers - for (auto i = 0u; i < missing_layer_count; i++) { - CreateLayer(gr_context, // - ios_context, // - ((FlutterView*)flutter_view_.get()).pixelFormat // - ); - } - } else { - auto latch = std::make_shared(1u); - platform_task_runner_->PostTask([&]() { - // Create Missing Layers - for (auto i = 0u; i < missing_layer_count; i++) { - CreateLayer(gr_context, // - ios_context, // - ((FlutterView*)flutter_view_.get()).pixelFormat // - ); - } - latch->CountDown(); - }); - latch->Wait(); - } + CreateMissingOverlays(gr_context, ios_context, required_overlay_layers); } int64_t overlay_id = 0; @@ -745,66 +721,117 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, layer_pool_->RemoveUnusedLayers(); layer_pool_->RecycleLayers(); - // Dispose unused Flutter Views. - auto views_to_dispose = DisposeViews(); - auto task = [&, platform_view_layers = std::move(platform_view_layers), // current_composition_params = current_composition_params_, // views_to_recomposite = views_to_recomposite_, // - callbacks = callbacks, // + callbacks = std::move(callbacks), // composition_order = composition_order_, // - unused_layers = unused_layers, views_to_dispose]() mutable { - TRACE_EVENT0("flutter", "FlutterPlatformViewsController::SubmitFrame::CATransaction"); - - [CATransaction begin]; - - // Configure Flutter overlay views. - for (const auto& [key, layers] : platform_view_layers) { - for (const auto& layer_data : layers) { - layer_data.layer->UpdateViewState(flutter_view_, // - layer_data.rect, // - layer_data.view_id, // - layer_data.overlay_id // - ); - } - } + unused_layers = std::move(unused_layers), + views_to_dispose = DisposeViews() // + ]() mutable { + PerformSubmit(std::move(platform_view_layers), // + std::move(callbacks), // + std::move(current_composition_params), // + std::move(views_to_recomposite), // + std::move(composition_order), // + std::move(unused_layers), // + std::move(views_to_dispose) // + ); + }; - // Dispose unused Flutter Views. - for (auto& view : views_to_dispose) { - [view removeFromSuperview]; - } + // Workaround for FlutterPlatformViewsTest.mm + if ([[NSThread currentThread] isMainThread]) { + task(); + } else { + platform_task_runner_->PostTask(task); + } + + return did_submit; +} + +void FlutterPlatformViewsController::CreateMissingOverlays( + GrDirectContext* gr_context, + const std::shared_ptr& ios_context, + size_t required_overlay_layers) { + TRACE_EVENT0("flutter", "FlutterPlatformViewsController::CreateMissingLayers"); - // Composite Platform Views. - for (int64_t view_id : views_to_recomposite) { - CompositeWithParams(view_id, current_composition_params[view_id]); + auto missing_layer_count = required_overlay_layers - layer_pool_->size(); + // Workaround for FLutterPlatformViewsTest + if ([[NSThread currentThread] isMainThread]) { + // Create Missing Layers + for (auto i = 0u; i < missing_layer_count; i++) { + CreateLayer(gr_context, // + ios_context, // + ((FlutterView*)flutter_view_.get()).pixelFormat // + ); } + return; + } - // Present callbacks. - for (const auto& cb : callbacks) { - cb(); + auto latch = std::make_shared(1u); + platform_task_runner_->PostTask([&]() { + // Create Missing Layers + for (auto i = 0u; i < missing_layer_count; i++) { + CreateLayer(gr_context, // + ios_context, // + ((FlutterView*)flutter_view_.get()).pixelFormat // + ); } + latch->CountDown(); + }); + latch->Wait(); +} - // Organize the layers by their z indexes. - auto active_composition_order = BringLayersIntoView(platform_view_layers, composition_order); +/// Update the buffers and mutate the platform views in CATransaction on the platform thread. +void FlutterPlatformViewsController::PerformSubmit( + LayersMap platform_view_layers, + std::vector callbacks, + std::map current_composition_params, + std::unordered_set views_to_recomposite, + std::vector composition_order, + std::vector> unused_layers, + std::vector views_to_dispose) { + TRACE_EVENT0("flutter", "FlutterPlatformViewsController::SubmitFrame::CATransaction"); + + [CATransaction begin]; + + // Configure Flutter overlay views. + for (const auto& [key, layers] : platform_view_layers) { + for (const auto& layer_data : layers) { + layer_data.layer->UpdateViewState(flutter_view_, // + layer_data.rect, // + layer_data.view_id, // + layer_data.overlay_id // + ); + } + } - // If a layer was allocated in the previous frame, but it's not used in the current frame, - // then it can be removed from the scene. - RemoveUnusedLayers(unused_layers, composition_order, active_composition_order); + // Dispose unused Flutter Views. + for (auto& view : views_to_dispose) { + [view removeFromSuperview]; + } - // If the frame is submitted with embedded platform views, - // there should be a |[CATransaction begin]| call in this frame prior to all the drawing. - // If that case, we need to commit the transaction. - [CATransaction commit]; - }; + // Composite Platform Views. + for (int64_t view_id : views_to_recomposite) { + CompositeWithParams(view_id, current_composition_params[view_id]); + } - // Workaround for FlutterPlatformViewsTest.mm - if ([[NSThread currentThread] isMainThread]) { - task(); - } else { - platform_task_runner_->PostTask(task); + // Present callbacks. + for (const auto& cb : callbacks) { + cb(); } - return did_submit; + // Organize the layers by their z indexes. + auto active_composition_order = BringLayersIntoView(platform_view_layers, composition_order); + + // If a layer was allocated in the previous frame, but it's not used in the current frame, + // then it can be removed from the scene. + RemoveUnusedLayers(unused_layers, composition_order, active_composition_order); + + // If the frame is submitted with embedded platform views, + // there should be a |[CATransaction begin]| call in this frame prior to all the drawing. + // If that case, we need to commit the transaction. + [CATransaction commit]; } std::vector FlutterPlatformViewsController::BringLayersIntoView( diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index 419876ed8fe9d..d9c19fd1e8547 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -310,6 +310,22 @@ class FlutterPlatformViewsController { using LayersMap = std::map>; + /// Update the buffers and mutate the platform views in CATransaction on the platform thread. + void PerformSubmit(LayersMap platform_view_layers, + std::vector callbacks, + std::map current_composition_params, + std::unordered_set views_to_recomposite, + std::vector composition_order, + std::vector> unused_layers, + std::vector views_to_dispose); + + /// @brief Populate any missing overlay layers. + /// + /// This requires posting a task to the platform thread and blocking on its completion. + void CreateMissingOverlays(GrDirectContext* gr_context, + const std::shared_ptr& ios_context, + size_t required_overlay_layers); + void OnCreate(FlutterMethodCall* call, FlutterResult result) __attribute__((cf_audited_transfer)); void OnDispose(FlutterMethodCall* call, FlutterResult result) __attribute__((cf_audited_transfer)); @@ -399,7 +415,7 @@ class FlutterPlatformViewsController { // A vector of visited platform view IDs. std::vector visited_platform_views_; - // Only compoiste platform views in this set. + // Only composite platform views in this set. std::unordered_set views_to_recomposite_; // The FlutterPlatformViewGestureRecognizersBlockingPolicy for each type of platform view. From 27c0daabe056feb40682c96acc439a9383180194 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Thu, 25 Jul 2024 16:17:40 -0700 Subject: [PATCH 29/49] formatting. --- .../framework/Source/FlutterPlatformViews.mm | 28 +++++++++---------- .../Source/FlutterPlatformViews_Internal.h | 14 +++++----- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 8dd44d2e8929a..f7d044e130931 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -729,13 +729,13 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, unused_layers = std::move(unused_layers), views_to_dispose = DisposeViews() // ]() mutable { - PerformSubmit(std::move(platform_view_layers), // - std::move(callbacks), // - std::move(current_composition_params), // - std::move(views_to_recomposite), // - std::move(composition_order), // - std::move(unused_layers), // - std::move(views_to_dispose) // + PerformSubmit(platform_view_layers, // + callbacks, // + current_composition_params, // + views_to_recomposite, // + composition_order, // + unused_layers, // + views_to_dispose // ); }; @@ -784,13 +784,13 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, /// Update the buffers and mutate the platform views in CATransaction on the platform thread. void FlutterPlatformViewsController::PerformSubmit( - LayersMap platform_view_layers, - std::vector callbacks, - std::map current_composition_params, - std::unordered_set views_to_recomposite, - std::vector composition_order, - std::vector> unused_layers, - std::vector views_to_dispose) { + const LayersMap& platform_view_layers, + const std::vector& callbacks, + std::map& current_composition_params, + const std::unordered_set& views_to_recomposite, + const std::vector& composition_order, + const std::vector>& unused_layers, + const std::vector& views_to_dispose) { TRACE_EVENT0("flutter", "FlutterPlatformViewsController::SubmitFrame::CATransaction"); [CATransaction begin]; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index d9c19fd1e8547..4019a387f2d29 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -311,13 +311,13 @@ class FlutterPlatformViewsController { using LayersMap = std::map>; /// Update the buffers and mutate the platform views in CATransaction on the platform thread. - void PerformSubmit(LayersMap platform_view_layers, - std::vector callbacks, - std::map current_composition_params, - std::unordered_set views_to_recomposite, - std::vector composition_order, - std::vector> unused_layers, - std::vector views_to_dispose); + void PerformSubmit(const LayersMap& platform_view_layers, + const std::vector& callbacks, + std::map& current_composition_params, + const std::unordered_set& views_to_recomposite, + const std::vector& composition_order, + const std::vector>& unused_layers, + const std::vector& views_to_dispose); /// @brief Populate any missing overlay layers. /// From d794211c8c83c35e657778a300d32d0fd32a0342 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Thu, 25 Jul 2024 18:29:34 -0700 Subject: [PATCH 30/49] add back clear --- shell/gpu/gpu_surface_metal_delegate.h | 4 ++++ shell/gpu/gpu_surface_metal_skia.mm | 15 ++++++++++++-- .../framework/Source/FlutterPlatformViews.mm | 1 + .../darwin/ios/ios_surface_metal_skia.h | 3 +++ .../darwin/ios/ios_surface_metal_skia.mm | 20 +++++++++++-------- testing/scenario_app/ios/README.md | 4 ++-- 6 files changed, 35 insertions(+), 12 deletions(-) diff --git a/shell/gpu/gpu_surface_metal_delegate.h b/shell/gpu/gpu_surface_metal_delegate.h index f20b0507231c9..09278e3f3cbfe 100644 --- a/shell/gpu/gpu_surface_metal_delegate.h +++ b/shell/gpu/gpu_surface_metal_delegate.h @@ -77,6 +77,10 @@ class GPUSurfaceMetalDelegate { /// virtual bool PresentDrawable(GrMTLHandle drawable) const = 0; + virtual bool PreparePresent(GrMTLHandle drawable) const { + return true; + } + //------------------------------------------------------------------------------ /// @brief Returns the handle to the MTLTexture to render to. This is only /// called when the specified render target type is `kMTLTexture`. diff --git a/shell/gpu/gpu_surface_metal_skia.mm b/shell/gpu/gpu_surface_metal_skia.mm index c945c8894a70c..e02d643ed97ef 100644 --- a/shell/gpu/gpu_surface_metal_skia.mm +++ b/shell/gpu/gpu_surface_metal_skia.mm @@ -152,8 +152,10 @@ return nullptr; } - auto submit_callback = [this, drawable](const SurfaceFrame& surface_frame, + auto submit_callback = [this, drawable, mtl_layer](const SurfaceFrame& surface_frame, DlCanvas* canvas) -> bool { + mtl_layer.presentsWithTransaction = surface_frame.submit_info().present_with_transaction; + TRACE_EVENT0("flutter", "GPUSurfaceMetal::Submit"); if (canvas == nullptr) { FML_DLOG(ERROR) << "Canvas not available."; @@ -179,7 +181,16 @@ damage_[texture] = SkIRect::MakeEmpty(); } - return delegate_->PresentDrawable(drawable); + if (surface_frame.submit_info().submit_receiver) { + delegate_->PreparePresent(drawable); + surface_frame.submit_info().submit_receiver([&, drawable = drawable]() { + return delegate_->PresentDrawable(drawable); + }); + return true; + } else { + delegate_->PreparePresent(drawable); + return delegate_->PresentDrawable(drawable); + } }; SurfaceFrame::FramebufferInfo framebuffer_info; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index f7d044e130931..ab6beb669e3a1 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -689,6 +689,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, int restore_count = overlay_canvas->GetSaveCount(); overlay_canvas->Save(); overlay_canvas->ClipRect(overlay->second); + overlay_canvas->Clear(DlColor::kTransparent()); slices_[view_id]->render_into(overlay_canvas); overlay_canvas->RestoreToCount(restore_count); diff --git a/shell/platform/darwin/ios/ios_surface_metal_skia.h b/shell/platform/darwin/ios/ios_surface_metal_skia.h index 65549762c4949..e696bab8c40de 100644 --- a/shell/platform/darwin/ios/ios_surface_metal_skia.h +++ b/shell/platform/darwin/ios/ios_surface_metal_skia.h @@ -47,6 +47,9 @@ class SK_API_AVAILABLE_CA_METAL_LAYER IOSSurfaceMetalSkia final : public IOSSurf // |GPUSurfaceMetalDelegate| bool PresentDrawable(GrMTLHandle drawable) const override __attribute__((cf_audited_transfer)); + // |GPUSurfaceMetalDelegate| + bool PreparePresent(GrMTLHandle drawable) const override; + // |GPUSurfaceMetalDelegate| GPUMTLTextureInfo GetMTLTexture(const SkISize& frame_info) const override __attribute__((cf_audited_transfer)); diff --git a/shell/platform/darwin/ios/ios_surface_metal_skia.mm b/shell/platform/darwin/ios/ios_surface_metal_skia.mm index 32465e2153201..8188837853b8c 100644 --- a/shell/platform/darwin/ios/ios_surface_metal_skia.mm +++ b/shell/platform/darwin/ios/ios_surface_metal_skia.mm @@ -80,22 +80,26 @@ - (void)flutterPrepareForPresent:(nonnull id)commandBuffer; } // |GPUSurfaceMetalDelegate| -bool IOSSurfaceMetalSkia::PresentDrawable(GrMTLHandle drawable) const { - if (drawable == nullptr) { - FML_DLOG(ERROR) << "Could not acquire next Metal drawable from the SkSurface."; - return false; - } - +bool IOSSurfaceMetalSkia::PreparePresent(GrMTLHandle drawable) const { id command_buffer = [command_queue_ commandBuffer]; - id metal_drawable = (__bridge id)drawable; if ([metal_drawable conformsToProtocol:@protocol(FlutterMetalDrawable)]) { [(id)metal_drawable flutterPrepareForPresent:command_buffer]; } - [command_buffer commit]; [command_buffer waitUntilScheduled]; + return true; +} + +// |GPUSurfaceMetalDelegate| +bool IOSSurfaceMetalSkia::PresentDrawable(GrMTLHandle drawable) const { + if (drawable == nullptr) { + FML_DLOG(ERROR) << "Could not acquire next Metal drawable from the SkSurface."; + return false; + } + + id metal_drawable = (__bridge id)drawable; [metal_drawable present]; return true; } diff --git a/testing/scenario_app/ios/README.md b/testing/scenario_app/ios/README.md index 62e3e538f3df6..bec73196610a1 100644 --- a/testing/scenario_app/ios/README.md +++ b/testing/scenario_app/ios/README.md @@ -9,14 +9,14 @@ run: ```sh # From the root of the engine repository -$ ./testing/run_ios_tests.sh ios_debug_sim_unopt +$ ./testing/scenario_app/run_ios_tests.sh ios_debug_sim_unopt ``` or: ```sh # From the root of the engine repository -$ ./testing/run_ios_tests.sh ios_debug_sim_unopt_arm64 +$ ./testing/scenario_app/run_ios_tests.sh ios_debug_sim_unopt_arm64 ``` To run or debug in Xcode, open the xcodeproj file located in From 2f4314bf0c27133df4dfc49ff45d683aca70db92 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Thu, 25 Jul 2024 18:30:01 -0700 Subject: [PATCH 31/49] ++ --- shell/gpu/gpu_surface_metal_delegate.h | 4 +--- shell/gpu/gpu_surface_metal_skia.mm | 7 +++---- shell/platform/darwin/ios/ios_surface_metal_skia.mm | 1 - 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/shell/gpu/gpu_surface_metal_delegate.h b/shell/gpu/gpu_surface_metal_delegate.h index 09278e3f3cbfe..54a86c2855623 100644 --- a/shell/gpu/gpu_surface_metal_delegate.h +++ b/shell/gpu/gpu_surface_metal_delegate.h @@ -77,9 +77,7 @@ class GPUSurfaceMetalDelegate { /// virtual bool PresentDrawable(GrMTLHandle drawable) const = 0; - virtual bool PreparePresent(GrMTLHandle drawable) const { - return true; - } + virtual bool PreparePresent(GrMTLHandle drawable) const { return true; } //------------------------------------------------------------------------------ /// @brief Returns the handle to the MTLTexture to render to. This is only diff --git a/shell/gpu/gpu_surface_metal_skia.mm b/shell/gpu/gpu_surface_metal_skia.mm index e02d643ed97ef..4ab84291b53a8 100644 --- a/shell/gpu/gpu_surface_metal_skia.mm +++ b/shell/gpu/gpu_surface_metal_skia.mm @@ -153,7 +153,7 @@ } auto submit_callback = [this, drawable, mtl_layer](const SurfaceFrame& surface_frame, - DlCanvas* canvas) -> bool { + DlCanvas* canvas) -> bool { mtl_layer.presentsWithTransaction = surface_frame.submit_info().present_with_transaction; TRACE_EVENT0("flutter", "GPUSurfaceMetal::Submit"); @@ -183,9 +183,8 @@ if (surface_frame.submit_info().submit_receiver) { delegate_->PreparePresent(drawable); - surface_frame.submit_info().submit_receiver([&, drawable = drawable]() { - return delegate_->PresentDrawable(drawable); - }); + surface_frame.submit_info().submit_receiver( + [&, drawable = drawable]() { return delegate_->PresentDrawable(drawable); }); return true; } else { delegate_->PreparePresent(drawable); diff --git a/shell/platform/darwin/ios/ios_surface_metal_skia.mm b/shell/platform/darwin/ios/ios_surface_metal_skia.mm index 8188837853b8c..6c0b534d56d1a 100644 --- a/shell/platform/darwin/ios/ios_surface_metal_skia.mm +++ b/shell/platform/darwin/ios/ios_surface_metal_skia.mm @@ -91,7 +91,6 @@ - (void)flutterPrepareForPresent:(nonnull id)commandBuffer; return true; } - // |GPUSurfaceMetalDelegate| bool IOSSurfaceMetalSkia::PresentDrawable(GrMTLHandle drawable) const { if (drawable == nullptr) { From 25a4e06bbcbf532e57ce41ba685db8ee4ad36e94 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Fri, 26 Jul 2024 10:19:44 -0700 Subject: [PATCH 32/49] ++ --- .../platform/darwin/ios/framework/Source/FlutterPlatformViews.mm | 1 - 1 file changed, 1 deletion(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index ab6beb669e3a1..65766b6f88d3b 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -437,7 +437,6 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, return; } - FML_DCHECK(CATransform3DEqualToTransform(embedded_view.layer.transform, CATransform3DIdentity)); ResetAnchor(embedded_view.layer); ChildClippingView* clipView = (ChildClippingView*)embedded_view.superview; From ea2e66df0bedb3953426e1b0cfe30c6aabe3501d Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Fri, 26 Jul 2024 11:41:49 -0700 Subject: [PATCH 33/49] merge threads on SIM. --- .clangd | 2 +- .../framework/Source/FlutterPlatformViews.mm | 39 ++++++++++++++++++- .../darwin/ios/ios_external_view_embedder.mm | 4 ++ 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/.clangd b/.clangd index 5e9a80ab7c29f..c36b4792d750c 100644 --- a/.clangd +++ b/.clangd @@ -7,4 +7,4 @@ # - https://github.com/clangd/clangd/issues/662 CompileFlags: Add: -Wno-unknown-warning-option - Remove: [-m*] + Remove: [-m*, -f*] diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 65766b6f88d3b..f1686c6ae0ac0 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -20,6 +20,12 @@ FLUTTER_ASSERT_ARC +#ifdef FML_OS_IOS_SIMULATOR +// Note: this is an arbitrary number that attempts to account for cases +// where the platform view might be momentarily off the screen. +static const int kDefaultMergedLeaseDuration = 10; +#endif // FML_OS_IOS_SIMULATOR + @implementation UIView (FirstResponder) - (BOOL)flt_hasFirstResponderInViewHierarchySubtree { if (self.isFirstResponder) { @@ -339,12 +345,43 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, PostPrerollResult FlutterPlatformViewsController::PostPrerollAction( const fml::RefPtr& raster_thread_merger) { + // TODO(jonahwilliams): remove this once Software backend is removed for iOS Sim. +#ifdef FML_OS_IOS_SIMULATOR + if (composition_order_.empty()) { + return PostPrerollResult::kSuccess; + } + if (!raster_thread_merger->IsMerged()) { + // The raster thread merger may be disabled if the rasterizer is being + // created or teared down. + // + // In such cases, the current frame is dropped, and a new frame is attempted + // with the same layer tree. + // + // Eventually, the frame is submitted once this method returns `kSuccess`. + // At that point, the raster tasks are handled on the platform thread. + CancelFrame(); + return PostPrerollResult::kSkipAndRetryFrame; + } + // If the post preroll action is successful, we will display platform views in the current frame. + // In order to sync the rendering of the platform views (quartz) with skia's rendering, + // We need to begin an explicit CATransaction. This transaction needs to be submitted + // after the current frame is submitted. + raster_thread_merger->ExtendLeaseTo(kDefaultMergedLeaseDuration); + return PostPrerollResult::kSuccess; +#else return PostPrerollResult::kSuccess; +#endif // FML_OS_IOS_SIMULATOR } void FlutterPlatformViewsController::EndFrame( bool should_resubmit_frame, - const fml::RefPtr& raster_thread_merger) {} + const fml::RefPtr& raster_thread_merger) { +#if FML_OS_IOS_SIMULATOR + if (should_resubmit_frame) { + raster_thread_merger->MergeWithLease(kDefaultMergedLeaseDuration); + } +#endif // FML_OS_IOS_SIMULATOR +} void FlutterPlatformViewsController::PushFilterToVisitedPlatformViews( const std::shared_ptr& filter, diff --git a/shell/platform/darwin/ios/ios_external_view_embedder.mm b/shell/platform/darwin/ios/ios_external_view_embedder.mm index 92d1e2dd227f2..05dab3c73b60d 100644 --- a/shell/platform/darwin/ios/ios_external_view_embedder.mm +++ b/shell/platform/darwin/ios/ios_external_view_embedder.mm @@ -96,7 +96,11 @@ // |ExternalViewEmbedder| bool IOSExternalViewEmbedder::SupportsDynamicThreadMerging() { +#if FML_OS_IOS_SIMULATOR return false; +#else + return false; +#endif // FML_OS_IOS_SIMULATOR } // |ExternalViewEmbedder| From d2208859b2b878c092d091657712813405bee954 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Fri, 26 Jul 2024 11:53:06 -0700 Subject: [PATCH 34/49] ++ --- shell/platform/darwin/ios/ios_external_view_embedder.mm | 2 +- shell/platform/embedder/embedder_external_view_embedder.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/shell/platform/darwin/ios/ios_external_view_embedder.mm b/shell/platform/darwin/ios/ios_external_view_embedder.mm index 05dab3c73b60d..e125a9d385b40 100644 --- a/shell/platform/darwin/ios/ios_external_view_embedder.mm +++ b/shell/platform/darwin/ios/ios_external_view_embedder.mm @@ -97,7 +97,7 @@ // |ExternalViewEmbedder| bool IOSExternalViewEmbedder::SupportsDynamicThreadMerging() { #if FML_OS_IOS_SIMULATOR - return false; + return true; #else return false; #endif // FML_OS_IOS_SIMULATOR diff --git a/shell/platform/embedder/embedder_external_view_embedder.h b/shell/platform/embedder/embedder_external_view_embedder.h index 141d928125820..63f383f8bb4fd 100644 --- a/shell/platform/embedder/embedder_external_view_embedder.h +++ b/shell/platform/embedder/embedder_external_view_embedder.h @@ -14,7 +14,6 @@ #include "flutter/fml/macros.h" #include "flutter/shell/platform/embedder/embedder_external_view.h" #include "flutter/shell/platform/embedder/embedder_render_target_cache.h" -#include "fml/task_runner.h" namespace flutter { From 9b534cd913e38f677ffefb0ad2d3bd76e495c75a Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Fri, 26 Jul 2024 12:05:15 -0700 Subject: [PATCH 35/49] Update shell/platform/darwin/ios/ios_external_view_embedder.mm Co-authored-by: Jenn Magder --- shell/platform/darwin/ios/ios_external_view_embedder.mm | 1 + 1 file changed, 1 insertion(+) diff --git a/shell/platform/darwin/ios/ios_external_view_embedder.mm b/shell/platform/darwin/ios/ios_external_view_embedder.mm index e125a9d385b40..6cd0430481251 100644 --- a/shell/platform/darwin/ios/ios_external_view_embedder.mm +++ b/shell/platform/darwin/ios/ios_external_view_embedder.mm @@ -96,6 +96,7 @@ // |ExternalViewEmbedder| bool IOSExternalViewEmbedder::SupportsDynamicThreadMerging() { +// TODO(jonahwilliams): remove this once Software backend is removed for iOS Sim. #if FML_OS_IOS_SIMULATOR return true; #else From 970e13acb58673d2c86a6c74b1d8ec99d8042b8a Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Fri, 26 Jul 2024 13:34:15 -0700 Subject: [PATCH 36/49] ++ --- .../framework/Source/FlutterPlatformViews.mm | 27 ++++--------------- .../Source/FlutterPlatformViews_Internal.h | 27 ++++++++++++------- 2 files changed, 23 insertions(+), 31 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index f1686c6ae0ac0..c7594241cf532 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -93,16 +93,12 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, BOOL canApplyBlurBackdrop = YES; std::shared_ptr FlutterPlatformViewLayerPool::GetNextLayer() { - layers_mutex_.lock(); - std::shared_ptr result; if (available_layer_index_ < layers_.size()) { result = layers_[available_layer_index_]; available_layer_index_++; } - layers_mutex_.unlock(); - return result; } @@ -158,9 +154,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, layer->overlay_view_wrapper.get().clipsToBounds = YES; [layer->overlay_view_wrapper.get() addSubview:layer->overlay_view]; - layers_mutex_.lock(); layers_.push_back(layer); - layers_mutex_.unlock(); } void FlutterPlatformViewLayerPool::RecycleLayers() { @@ -169,13 +163,11 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, std::vector> FlutterPlatformViewLayerPool::RemoveUnusedLayers() { - layers_mutex_.lock(); std::vector> results; for (size_t i = available_layer_index_; i < layers_.size(); i++) { results.push_back(layers_[i]); } layers_.erase(layers_.begin() + available_layer_index_, layers_.end()); - layers_mutex_.unlock(); return results; } @@ -645,16 +637,11 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, views.push_back(root_views_[view_id].get()); } - auto task = [views = views]() { + fml::TaskRunner::RunNowOrPostTask(platform_task_runner_, [views = views]() { for (auto* sub_view : views) { [sub_view removeFromSuperview]; } - }; - if ([[NSThread currentThread] isMainThread]) { - task(); - } else { - platform_task_runner_->PostTask(task); - } + }); root_views_.clear(); touch_interceptors_.clear(); @@ -764,7 +751,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, callbacks = std::move(callbacks), // composition_order = composition_order_, // unused_layers = std::move(unused_layers), - views_to_dispose = DisposeViews() // + views_to_dispose = GetViewsToDispose() // ]() mutable { PerformSubmit(platform_view_layers, // callbacks, // @@ -776,12 +763,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, ); }; - // Workaround for FlutterPlatformViewsTest.mm - if ([[NSThread currentThread] isMainThread]) { - task(); - } else { - platform_task_runner_->PostTask(task); - } + fml::TaskRunner::RunNowOrPostTask(platform_task_runner_, task); return did_submit; } @@ -793,6 +775,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, TRACE_EVENT0("flutter", "FlutterPlatformViewsController::CreateMissingLayers"); auto missing_layer_count = required_overlay_layers - layer_pool_->size(); + // Workaround for FLutterPlatformViewsTest if ([[NSThread currentThread] isMainThread]) { // Create Missing Layers diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index 4019a387f2d29..d1445ffbb6b66 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -168,28 +168,37 @@ struct FlutterPlatformViewLayer { void UpdateViewState(UIView* flutter_view, SkRect rect, int64_t view_id, int64_t overlay_id); }; -// This class isn't thread safe. +/// @brief Storage for Overlay layers across frames. +/// +/// Note: this class does not synchronize access to its layers or any layer removal. As it +/// is currently used, layers must be created on the platform thread but other methods of +/// it are called on the raster thread. This is safe as overlay layers are only ever added +/// while the raster thread is latched. class FlutterPlatformViewLayerPool { public: FlutterPlatformViewLayerPool() = default; ~FlutterPlatformViewLayerPool() = default; - /// Gets a layer from the pool if available, or allocates a new one. - /// Finally, it marks the layer as used. That is, it increments `available_layer_index_`. + /// @brief Gets a layer from the pool if available. + /// + /// The layer is marked as used until [RecycleLayers] is called. std::shared_ptr GetNextLayer(); + /// @brief Create a new overlay layer. + /// + /// This method can only be called on the Platform thread. void CreateLayer(GrDirectContext* gr_context, const std::shared_ptr& ios_context, MTLPixelFormat pixel_format); - // Removes unused layers from the pool. Returns the unused layers. + /// @brief Removes unused layers from the pool. Returns the unused layers. std::vector> RemoveUnusedLayers(); - // Marks the layers in the pool as available for reuse. + /// @brief Marks the layers in the pool as available for reuse. void RecycleLayers(); - // Returns the count of layers currently in the pool. + /// @brief The count of layers currently in the pool. size_t size() const; private: @@ -206,7 +215,6 @@ class FlutterPlatformViewLayerPool { /// This indicates that entries starting from 1 can be reused meanwhile the entry at position 0 /// cannot be reused. size_t available_layer_index_ = 0; - mutable std::mutex layers_mutex_; std::vector> layers_; FML_DISALLOW_COPY_AND_ASSIGN(FlutterPlatformViewLayerPool); @@ -333,8 +341,9 @@ class FlutterPlatformViewsController { __attribute__((cf_audited_transfer)); void OnRejectGesture(FlutterMethodCall* call, FlutterResult result) __attribute__((cf_audited_transfer)); - // Dispose the views in `views_to_dispose_`. - std::vector DisposeViews(); + + /// @brief Return all views to be disposed on the platform thread. + std::vector GetViewsToDispose(); // Traverse the `mutators_stack` and return the number of clip operations. int CountClips(const MutatorsStack& mutators_stack); From e3fc731f42a64fdb3ff82241ce1ebabafc2ce71e Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Fri, 26 Jul 2024 16:18:31 -0700 Subject: [PATCH 37/49] does it blend? --- .../framework/Source/FlutterPlatformViews.mm | 27 +- .../Source/FlutterPlatformViewsTest.mm | 392 +++++++++--------- .../Source/FlutterPlatformViews_Internal.h | 16 +- 3 files changed, 224 insertions(+), 211 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index c7594241cf532..72e3835263ac7 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -660,9 +660,11 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, std::unique_ptr background_frame) { TRACE_EVENT0("flutter", "FlutterPlatformViewsController::SubmitFrame"); - if (flutter_view_ == nullptr || composition_order_.empty()) { + if (flutter_view_ == nullptr || (composition_order_.empty() && !had_platform_views_)) { + had_platform_views_ = false; return background_frame->Submit(); } + had_platform_views_ = true; LayersMap platform_view_layers; std::vector callbacks; @@ -841,12 +843,12 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, cb(); } - // Organize the layers by their z indexes. - auto active_composition_order = BringLayersIntoView(platform_view_layers, composition_order); - // If a layer was allocated in the previous frame, but it's not used in the current frame, // then it can be removed from the scene. - RemoveUnusedLayers(unused_layers, composition_order, active_composition_order); + RemoveUnusedLayers(unused_layers, composition_order); + + // Organize the layers by their z indexes. + BringLayersIntoView(platform_view_layers, composition_order); // If the frame is submitted with embedded platform views, // there should be a |[CATransaction begin]| call in this frame prior to all the drawing. @@ -854,14 +856,13 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, [CATransaction commit]; } -std::vector FlutterPlatformViewsController::BringLayersIntoView( +void FlutterPlatformViewsController::BringLayersIntoView( LayersMap layer_map, const std::vector& composition_order) { FML_DCHECK(flutter_view_); UIView* flutter_view = flutter_view_.get(); - std::vector active_composition_order; - + previous_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]; @@ -871,7 +872,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, for (const auto& layer_data : layers) { [desired_platform_subviews addObject:layer_data.layer->overlay_view_wrapper]; } - active_composition_order.push_back(platform_view_id); + previous_composition_order_.push_back(platform_view_id); } NSSet* desired_platform_subviews_set = [NSSet setWithArray:desired_platform_subviews]; @@ -891,7 +892,6 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, [flutter_view addSubview:subview]; } } - return active_composition_order; } std::shared_ptr FlutterPlatformViewsController::GetExistingLayer() { @@ -929,8 +929,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, void FlutterPlatformViewsController::RemoveUnusedLayers( const std::vector>& unused_layers, - const std::vector& composition_order, - const std::vector& active_composition_order) { + const std::vector& composition_order) { for (const std::shared_ptr& layer : unused_layers) { [layer->overlay_view_wrapper removeFromSuperview]; } @@ -940,7 +939,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, composition_order_set.insert(view_id); } // Remove unused platform views. - for (int64_t view_id : active_composition_order) { + for (int64_t view_id : previous_composition_order_) { if (composition_order_set.find(view_id) == composition_order_set.end()) { UIView* platform_view_root = root_views_[view_id].get(); [platform_view_root removeFromSuperview]; @@ -948,7 +947,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, } } -std::vector FlutterPlatformViewsController::DisposeViews() { +std::vector FlutterPlatformViewsController::GetViewsToDispose() { std::vector views; if (views_to_dispose_.empty()) { return views; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm index bfdf5083269bb..f13b3cf4968bc 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm @@ -129,16 +129,22 @@ @interface FlutterPlatformViewsTest : XCTestCase @implementation FlutterPlatformViewsTest +fml::RefPtr GetDefaultTaskRunner() { + fml::MessageLoop::EnsureInitializedForCurrentThread(); + return fml::MessageLoop::GetCurrent().GetTaskRunner(); +} + + - (void)testFlutterViewOnlyCreateOnceInOneFrame { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread = std::make_unique("FlutterPlatformViewsTest"); + flutter::TaskRunners runners(/*label=*/self.name.UTF8String, - /*platform=*/thread->GetTaskRunner(), - /*raster=*/thread->GetTaskRunner(), - /*ui=*/thread->GetTaskRunner(), - /*io=*/thread->GetTaskRunner()); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); - flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -188,14 +194,14 @@ - (void)testFlutterViewOnlyCreateOnceInOneFrame { - (void)testCanCreatePlatformViewWithoutFlutterView { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread = std::make_unique("FlutterPlatformViewsTest"); + flutter::TaskRunners runners(/*label=*/self.name.UTF8String, - /*platform=*/thread->GetTaskRunner(), - /*raster=*/thread->GetTaskRunner(), - /*ui=*/thread->GetTaskRunner(), - /*io=*/thread->GetTaskRunner()); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); - flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -281,14 +287,14 @@ - (void)testReleasesBackdropFilterSubviewsOnChildClippingViewDealloc { - (void)testApplyBackdropFilter { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread = std::make_unique("FlutterPlatformViewsTest"); + flutter::TaskRunners runners(/*label=*/self.name.UTF8String, - /*platform=*/thread->GetTaskRunner(), - /*raster=*/thread->GetTaskRunner(), - /*ui=*/thread->GetTaskRunner(), - /*io=*/thread->GetTaskRunner()); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); - flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -359,14 +365,14 @@ - (void)testApplyBackdropFilter { - (void)testApplyBackdropFilterWithCorrectFrame { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread = std::make_unique("FlutterPlatformViewsTest"); + flutter::TaskRunners runners(/*label=*/self.name.UTF8String, - /*platform=*/thread->GetTaskRunner(), - /*raster=*/thread->GetTaskRunner(), - /*ui=*/thread->GetTaskRunner(), - /*io=*/thread->GetTaskRunner()); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); - flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -437,14 +443,14 @@ - (void)testApplyBackdropFilterWithCorrectFrame { - (void)testApplyMultipleBackdropFilters { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread = std::make_unique("FlutterPlatformViewsTest"); + flutter::TaskRunners runners(/*label=*/self.name.UTF8String, - /*platform=*/thread->GetTaskRunner(), - /*raster=*/thread->GetTaskRunner(), - /*ui=*/thread->GetTaskRunner(), - /*io=*/thread->GetTaskRunner()); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); - flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -516,14 +522,14 @@ - (void)testApplyMultipleBackdropFilters { - (void)testAddBackdropFilters { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread = std::make_unique("FlutterPlatformViewsTest"); + flutter::TaskRunners runners(/*label=*/self.name.UTF8String, - /*platform=*/thread->GetTaskRunner(), - /*raster=*/thread->GetTaskRunner(), - /*ui=*/thread->GetTaskRunner(), - /*io=*/thread->GetTaskRunner()); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); - flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -639,14 +645,14 @@ - (void)testAddBackdropFilters { - (void)testRemoveBackdropFilters { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread = std::make_unique("FlutterPlatformViewsTest"); + flutter::TaskRunners runners(/*label=*/self.name.UTF8String, - /*platform=*/thread->GetTaskRunner(), - /*raster=*/thread->GetTaskRunner(), - /*ui=*/thread->GetTaskRunner(), - /*io=*/thread->GetTaskRunner()); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); - flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -788,14 +794,14 @@ - (void)testRemoveBackdropFilters { - (void)testEditBackdropFilters { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread = std::make_unique("FlutterPlatformViewsTest"); + flutter::TaskRunners runners(/*label=*/self.name.UTF8String, - /*platform=*/thread->GetTaskRunner(), - /*raster=*/thread->GetTaskRunner(), - /*ui=*/thread->GetTaskRunner(), - /*io=*/thread->GetTaskRunner()); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); - flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -1083,14 +1089,14 @@ - (void)testEditBackdropFilters { - (void)testApplyBackdropFilterNotDlBlurImageFilter { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread = std::make_unique("FlutterPlatformViewsTest"); + flutter::TaskRunners runners(/*label=*/self.name.UTF8String, - /*platform=*/thread->GetTaskRunner(), - /*raster=*/thread->GetTaskRunner(), - /*ui=*/thread->GetTaskRunner(), - /*io=*/thread->GetTaskRunner()); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); - flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -1407,14 +1413,14 @@ - (void)testBackdropFilterVisualEffectSubviewBackgroundColor { - (void)testCompositePlatformView { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread = std::make_unique("FlutterPlatformViewsTest"); + flutter::TaskRunners runners(/*label=*/self.name.UTF8String, - /*platform=*/thread->GetTaskRunner(), - /*raster=*/thread->GetTaskRunner(), - /*ui=*/thread->GetTaskRunner(), - /*io=*/thread->GetTaskRunner()); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); - flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -1469,14 +1475,14 @@ - (void)testCompositePlatformView { - (void)testBackdropFilterCorrectlyPushedAndReset { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread = std::make_unique("FlutterPlatformViewsTest"); + flutter::TaskRunners runners(/*label=*/self.name.UTF8String, - /*platform=*/thread->GetTaskRunner(), - /*raster=*/thread->GetTaskRunner(), - /*ui=*/thread->GetTaskRunner(), - /*io=*/thread->GetTaskRunner()); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); - flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -1572,14 +1578,14 @@ - (void)testBackdropFilterCorrectlyPushedAndReset { - (void)testChildClippingViewShouldBeTheBoundingRectOfPlatformView { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread = std::make_unique("FlutterPlatformViewsTest"); + flutter::TaskRunners runners(/*label=*/self.name.UTF8String, - /*platform=*/thread->GetTaskRunner(), - /*raster=*/thread->GetTaskRunner(), - /*ui=*/thread->GetTaskRunner(), - /*io=*/thread->GetTaskRunner()); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); - flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -1649,14 +1655,14 @@ - (void)testChildClippingViewShouldBeTheBoundingRectOfPlatformView { - (void)testClipsDoNotInterceptWithPlatformViewShouldNotAddMaskView { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread = std::make_unique("FlutterPlatformViewsTest"); + flutter::TaskRunners runners(/*label=*/self.name.UTF8String, - /*platform=*/thread->GetTaskRunner(), - /*raster=*/thread->GetTaskRunner(), - /*ui=*/thread->GetTaskRunner(), - /*io=*/thread->GetTaskRunner()); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); - flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -1722,14 +1728,14 @@ - (void)testClipsDoNotInterceptWithPlatformViewShouldNotAddMaskView { - (void)testClipRRectOnlyHasCornersInterceptWithPlatformViewShouldAddMaskView { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread = std::make_unique("FlutterPlatformViewsTest"); + flutter::TaskRunners runners(/*label=*/self.name.UTF8String, - /*platform=*/thread->GetTaskRunner(), - /*raster=*/thread->GetTaskRunner(), - /*ui=*/thread->GetTaskRunner(), - /*io=*/thread->GetTaskRunner()); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); - flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -1794,14 +1800,14 @@ - (void)testClipRRectOnlyHasCornersInterceptWithPlatformViewShouldAddMaskView { - (void)testClipRect { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread = std::make_unique("FlutterPlatformViewsTest"); + flutter::TaskRunners runners(/*label=*/self.name.UTF8String, - /*platform=*/thread->GetTaskRunner(), - /*raster=*/thread->GetTaskRunner(), - /*ui=*/thread->GetTaskRunner(), - /*io=*/thread->GetTaskRunner()); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); - flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -1871,14 +1877,14 @@ - (void)testClipRect { - (void)testClipRRect { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread = std::make_unique("FlutterPlatformViewsTest"); + flutter::TaskRunners runners(/*label=*/self.name.UTF8String, - /*platform=*/thread->GetTaskRunner(), - /*raster=*/thread->GetTaskRunner(), - /*ui=*/thread->GetTaskRunner(), - /*io=*/thread->GetTaskRunner()); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); - flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -1975,14 +1981,14 @@ - (void)testClipRRect { - (void)testClipPath { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread = std::make_unique("FlutterPlatformViewsTest"); + flutter::TaskRunners runners(/*label=*/self.name.UTF8String, - /*platform=*/thread->GetTaskRunner(), - /*raster=*/thread->GetTaskRunner(), - /*ui=*/thread->GetTaskRunner(), - /*io=*/thread->GetTaskRunner()); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); - flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -2080,14 +2086,14 @@ - (void)testClipPath { - (void)testSetFlutterViewControllerAfterCreateCanStillDispatchTouchEvents { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread = std::make_unique("FlutterPlatformViewsTest"); + flutter::TaskRunners runners(/*label=*/self.name.UTF8String, - /*platform=*/thread->GetTaskRunner(), - /*raster=*/thread->GetTaskRunner(), - /*ui=*/thread->GetTaskRunner(), - /*io=*/thread->GetTaskRunner()); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); - flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -2147,14 +2153,14 @@ - (void)testSetFlutterViewControllerAfterCreateCanStillDispatchTouchEvents { - (void)testSetFlutterViewControllerInTheMiddleOfTouchEventShouldStillAllowGesturesToBeHandled { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread = std::make_unique("FlutterPlatformViewsTest"); + flutter::TaskRunners runners(/*label=*/self.name.UTF8String, - /*platform=*/thread->GetTaskRunner(), - /*raster=*/thread->GetTaskRunner(), - /*ui=*/thread->GetTaskRunner(), - /*io=*/thread->GetTaskRunner()); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); - flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -2271,14 +2277,14 @@ - (void)testSetFlutterViewControllerInTheMiddleOfTouchEventShouldStillAllowGestu - (void) testSetFlutterViewControllerInTheMiddleOfTouchEventAllowsTheNewControllerToHandleSecondTouchSequence { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread = std::make_unique("FlutterPlatformViewsTest"); + flutter::TaskRunners runners(/*label=*/self.name.UTF8String, - /*platform=*/thread->GetTaskRunner(), - /*raster=*/thread->GetTaskRunner(), - /*ui=*/thread->GetTaskRunner(), - /*io=*/thread->GetTaskRunner()); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); - flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -2385,14 +2391,14 @@ - (void)testSetFlutterViewControllerInTheMiddleOfTouchEventShouldStillAllowGestu - (void)testFlutterPlatformViewTouchesCancelledEventAreForcedToBeCancelled { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread = std::make_unique("FlutterPlatformViewsTest"); + flutter::TaskRunners runners(/*label=*/self.name.UTF8String, - /*platform=*/thread->GetTaskRunner(), - /*raster=*/thread->GetTaskRunner(), - /*ui=*/thread->GetTaskRunner(), - /*io=*/thread->GetTaskRunner()); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); - flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -2450,14 +2456,14 @@ - (void)testFlutterPlatformViewTouchesCancelledEventAreForcedToBeCancelled { - (void)testFlutterPlatformViewControllerSubmitFrameWithoutFlutterViewNotCrashing { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread = std::make_unique("FlutterPlatformViewsTest"); + flutter::TaskRunners runners(/*label=*/self.name.UTF8String, - /*platform=*/thread->GetTaskRunner(), - /*raster=*/thread->GetTaskRunner(), - /*ui=*/thread->GetTaskRunner(), - /*io=*/thread->GetTaskRunner()); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); - flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -2521,14 +2527,14 @@ - (void)testFlutterPlatformViewControllerSubmitFrameWithoutFlutterViewNotCrashin - (void) testFlutterPlatformViewControllerResetDeallocsPlatformViewWhenRootViewsNotBindedToFlutterView { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread = std::make_unique("FlutterPlatformViewsTest"); + flutter::TaskRunners runners(/*label=*/self.name.UTF8String, - /*platform=*/thread->GetTaskRunner(), - /*raster=*/thread->GetTaskRunner(), - /*ui=*/thread->GetTaskRunner(), - /*io=*/thread->GetTaskRunner()); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); - flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -2575,14 +2581,14 @@ - (void)testFlutterPlatformViewControllerSubmitFrameWithoutFlutterViewNotCrashin - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread = std::make_unique("FlutterPlatformViewsTest"); + flutter::TaskRunners runners(/*label=*/self.name.UTF8String, - /*platform=*/thread->GetTaskRunner(), - /*raster=*/thread->GetTaskRunner(), - /*ui=*/thread->GetTaskRunner(), - /*io=*/thread->GetTaskRunner()); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); - flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -2640,14 +2646,14 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { - (void) testFlutterPlatformViewControllerSubmitFrameShouldOrderSubviewsCorrectlyWithDifferentViewHierarchy { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread = std::make_unique("FlutterPlatformViewsTest"); + flutter::TaskRunners runners(/*label=*/self.name.UTF8String, - /*platform=*/thread->GetTaskRunner(), - /*raster=*/thread->GetTaskRunner(), - /*ui=*/thread->GetTaskRunner(), - /*io=*/thread->GetTaskRunner()); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); - flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -2704,7 +2710,7 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { /*frame_size=*/SkISize::Make(800, 600)); auto latch = std::make_shared(1u); - thread->GetTaskRunner()->PostTask([&latch]() { latch->CountDown(); }); + GetDefaultTaskRunner()->PostTask([&latch]() { latch->CountDown(); }); latch->Wait(); XCTAssertTrue( @@ -2737,7 +2743,7 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface))); latch = std::make_shared(1u); - thread->GetTaskRunner()->PostTask([&latch]() { latch->CountDown(); }); + GetDefaultTaskRunner()->PostTask([&latch]() { latch->CountDown(); }); latch->Wait(); XCTAssertTrue([flutterView.subviews indexOfObject:clippingView1] > @@ -2748,14 +2754,14 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { - (void) testFlutterPlatformViewControllerSubmitFrameShouldOrderSubviewsCorrectlyWithSameViewHierarchy { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread = std::make_unique("FlutterPlatformViewsTest"); + flutter::TaskRunners runners(/*label=*/self.name.UTF8String, - /*platform=*/thread->GetTaskRunner(), - /*raster=*/thread->GetTaskRunner(), - /*ui=*/thread->GetTaskRunner(), - /*io=*/thread->GetTaskRunner()); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); - flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -2814,7 +2820,7 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { XCTAssertTrue( flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface))); auto latch = std::make_shared(1u); - thread->GetTaskRunner()->PostTask([&latch]() { latch->CountDown(); }); + GetDefaultTaskRunner()->PostTask([&latch]() { latch->CountDown(); }); latch->Wait(); // platform view is wrapped by touch interceptor, which itself is wrapped by clipping view. @@ -2844,7 +2850,7 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface))); latch = std::make_shared(1u); - thread->GetTaskRunner()->PostTask([&latch]() { latch->CountDown(); }); + GetDefaultTaskRunner()->PostTask([&latch]() { latch->CountDown(); }); latch->Wait(); XCTAssertTrue([flutterView.subviews indexOfObject:clippingView1] < @@ -2940,14 +2946,14 @@ - (void)testMaskViewsReleasedWhenPoolIsReleased { - (void)testClipMaskViewIsReused { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread = std::make_unique("FlutterPlatformViewsTest"); + flutter::TaskRunners runners(/*label=*/self.name.UTF8String, - /*platform=*/thread->GetTaskRunner(), - /*raster=*/thread->GetTaskRunner(), - /*ui=*/thread->GetTaskRunner(), - /*io=*/thread->GetTaskRunner()); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); - flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -3034,14 +3040,14 @@ - (void)testClipMaskViewIsReused { - (void)testDifferentClipMaskViewIsUsedForEachView { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread = std::make_unique("FlutterPlatformViewsTest"); + flutter::TaskRunners runners(/*label=*/self.name.UTF8String, - /*platform=*/thread->GetTaskRunner(), - /*raster=*/thread->GetTaskRunner(), - /*ui=*/thread->GetTaskRunner(), - /*io=*/thread->GetTaskRunner()); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); - flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -3116,14 +3122,14 @@ - (void)testDifferentClipMaskViewIsUsedForEachView { - (void)testMaskViewUsesCAShapeLayerAsTheBackingLayer { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread = std::make_unique("FlutterPlatformViewsTest"); + flutter::TaskRunners runners(/*label=*/self.name.UTF8String, - /*platform=*/thread->GetTaskRunner(), - /*raster=*/thread->GetTaskRunner(), - /*ui=*/thread->GetTaskRunner(), - /*io=*/thread->GetTaskRunner()); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); - flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -3210,14 +3216,14 @@ - (BOOL)validateOneVisualEffectView:(UIView*)visualEffectView - (void)testDisposingViewInCompositionOrderDoNotCrash { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread = std::make_unique("FlutterPlatformViewsTest"); + flutter::TaskRunners runners(/*label=*/self.name.UTF8String, - /*platform=*/thread->GetTaskRunner(), - /*raster=*/thread->GetTaskRunner(), - /*ui=*/thread->GetTaskRunner(), - /*io=*/thread->GetTaskRunner()); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); - flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -3286,7 +3292,7 @@ - (void)testDisposingViewInCompositionOrderDoNotCrash { flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface))); auto latch = std::make_shared(1u); - thread->GetTaskRunner()->PostTask([&latch]() { latch->CountDown(); }); + GetDefaultTaskRunner()->PostTask([&latch]() { latch->CountDown(); }); latch->Wait(); // Disposing won't remove embedded views until the view is removed from the composition_order_ @@ -3316,7 +3322,7 @@ - (void)testDisposingViewInCompositionOrderDoNotCrash { flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface))); auto latch = std::make_shared(1u); - thread->GetTaskRunner()->PostTask([&latch]() { latch->CountDown(); }); + GetDefaultTaskRunner()->PostTask([&latch]() { latch->CountDown(); }); latch->Wait(); // Disposing won't remove embedded views until the view is removed from the composition_order_ @@ -3327,14 +3333,14 @@ - (void)testDisposingViewInCompositionOrderDoNotCrash { } - (void)testOnlyPlatformViewsAreRemovedWhenReset { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread = std::make_unique("FlutterPlatformViewsTest"); + flutter::TaskRunners runners(/*label=*/self.name.UTF8String, - /*platform=*/thread->GetTaskRunner(), - /*raster=*/thread->GetTaskRunner(), - /*ui=*/thread->GetTaskRunner(), - /*io=*/thread->GetTaskRunner()); + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); auto flutterPlatformViewsController = std::make_shared(); - flutterPlatformViewsController->SetTaskRunner(thread->GetTaskRunner()); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/mock_delegate.settings_.enable_impeller @@ -3389,7 +3395,7 @@ - (void)testOnlyPlatformViewsAreRemovedWhenReset { flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface)); auto latch = std::make_shared(1u); - thread->GetTaskRunner()->PostTask([&latch]() { latch->CountDown(); }); + GetDefaultTaskRunner()->PostTask([&latch]() { latch->CountDown(); }); latch->Wait(); UIView* someView = [[UIView alloc] init]; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index d1445ffbb6b66..be0f90f45ce4a 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -373,13 +373,11 @@ class FlutterPlatformViewsController { // Must run on the platform thread. void RemoveUnusedLayers( const std::vector>& unused_layers, - const std::vector& composition_order, - const std::vector& active_composition_order); + const std::vector& composition_order); // Appends the overlay views and platform view and sets their z index based on the composition // order. - std::vector BringLayersIntoView(LayersMap layer_map, - const std::vector& composition_order); + void BringLayersIntoView(LayersMap layer_map, const std::vector& composition_order); // Resets the state of the frame. void ResetFrameState(); @@ -440,6 +438,16 @@ class FlutterPlatformViewsController { fml::RefPtr platform_task_runner_; + /// @brief The composition order from the previous thread. + /// + /// Only accessed from the platform thread. + std::vector previous_composition_order_; + + /// Whether the previous frame had an active composition order. + /// + /// Only accessed from the raster thread. + bool had_platform_views_ = false; + // WeakPtrFactory must be the last member. std::unique_ptr> weak_factory_; From c0fab3b6b9f0dce623a8c7c778046fca58ce7d02 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Fri, 26 Jul 2024 16:20:04 -0700 Subject: [PATCH 38/49] ++ --- .../darwin/ios/framework/Source/FlutterPlatformViewsTest.mm | 1 - 1 file changed, 1 deletion(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm index f13b3cf4968bc..23e2e324446f3 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm @@ -134,7 +134,6 @@ @implementation FlutterPlatformViewsTest return fml::MessageLoop::GetCurrent().GetTaskRunner(); } - - (void)testFlutterViewOnlyCreateOnceInOneFrame { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; From d73671ee593be84d8a9a297bca71e48834690895 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Fri, 26 Jul 2024 18:52:35 -0700 Subject: [PATCH 39/49] test fixes and clang tidy. --- .../Source/FlutterPlatformViewsTest.mm | 29 ++----------------- 1 file changed, 2 insertions(+), 27 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm index 23e2e324446f3..a819b7c945769 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm @@ -129,10 +129,12 @@ @interface FlutterPlatformViewsTest : XCTestCase @implementation FlutterPlatformViewsTest +namespace { fml::RefPtr GetDefaultTaskRunner() { fml::MessageLoop::EnsureInitializedForCurrentThread(); return fml::MessageLoop::GetCurrent().GetTaskRunner(); } +} // namespace - (void)testFlutterViewOnlyCreateOnceInOneFrame { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; @@ -2708,10 +2710,6 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); - auto latch = std::make_shared(1u); - GetDefaultTaskRunner()->PostTask([&latch]() { latch->CountDown(); }); - latch->Wait(); - XCTAssertTrue( flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface))); @@ -2741,10 +2739,6 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { XCTAssertTrue( flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface))); - latch = std::make_shared(1u); - GetDefaultTaskRunner()->PostTask([&latch]() { latch->CountDown(); }); - latch->Wait(); - XCTAssertTrue([flutterView.subviews indexOfObject:clippingView1] > [flutterView.subviews indexOfObject:clippingView2], @"The first clipping view should be added after the second clipping view."); @@ -2818,9 +2812,6 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { XCTAssertTrue( flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface))); - auto latch = std::make_shared(1u); - GetDefaultTaskRunner()->PostTask([&latch]() { latch->CountDown(); }); - latch->Wait(); // platform view is wrapped by touch interceptor, which itself is wrapped by clipping view. UIView* clippingView1 = view1.superview.superview; @@ -2848,10 +2839,6 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { XCTAssertTrue( flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface))); - latch = std::make_shared(1u); - GetDefaultTaskRunner()->PostTask([&latch]() { latch->CountDown(); }); - latch->Wait(); - XCTAssertTrue([flutterView.subviews indexOfObject:clippingView1] < [flutterView.subviews indexOfObject:clippingView2], @"The first clipping view should be added before the second clipping view."); @@ -3290,10 +3277,6 @@ - (void)testDisposingViewInCompositionOrderDoNotCrash { XCTAssertTrue( flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface))); - auto latch = std::make_shared(1u); - GetDefaultTaskRunner()->PostTask([&latch]() { latch->CountDown(); }); - latch->Wait(); - // Disposing won't remove embedded views until the view is removed from the composition_order_ XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 2UL); XCTAssertNotNil(flutterPlatformViewsController->GetPlatformViewByID(0)); @@ -3320,10 +3303,6 @@ - (void)testDisposingViewInCompositionOrderDoNotCrash { XCTAssertTrue( flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface))); - auto latch = std::make_shared(1u); - GetDefaultTaskRunner()->PostTask([&latch]() { latch->CountDown(); }); - latch->Wait(); - // Disposing won't remove embedded views until the view is removed from the composition_order_ XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 1UL); XCTAssertNil(flutterPlatformViewsController->GetPlatformViewByID(0)); @@ -3393,10 +3372,6 @@ - (void)testOnlyPlatformViewsAreRemovedWhenReset { flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface)); - auto latch = std::make_shared(1u); - GetDefaultTaskRunner()->PostTask([&latch]() { latch->CountDown(); }); - latch->Wait(); - UIView* someView = [[UIView alloc] init]; [flutterView addSubview:someView]; From 72859079d326ec3d87b0c522c3dc021cd2555324 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Sat, 27 Jul 2024 11:22:03 -0700 Subject: [PATCH 40/49] simplify and clean up thread access for UIViews. --- .../framework/Source/FlutterPlatformViews.mm | 123 +++++++++--------- .../Source/FlutterPlatformViews_Internal.h | 88 ++++++++----- 2 files changed, 114 insertions(+), 97 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 72e3835263ac7..f13cec87a7790 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -209,10 +209,11 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, NSString* viewTypeString = args[@"viewType"]; std::string viewType(viewTypeString.UTF8String); - if (views_.count(viewId) != 0) { + if (platform_views_.count(viewId) != 0) { result([FlutterError errorWithCode:@"recreating_view" message:@"trying to create an already created view" details:[NSString stringWithFormat:@"view id: '%lld'", viewId]]); + return; } NSObject* factory = factories_[viewType].get(); @@ -248,19 +249,21 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, // Set a unique view identifier, so the platform view can be identified in unit tests. platform_view.accessibilityIdentifier = [NSString stringWithFormat:@"platform_view[%lld]", viewId]; - views_[viewId] = fml::scoped_nsobject>(embedded_view); FlutterTouchInterceptingView* touch_interceptor = [[FlutterTouchInterceptingView alloc] initWithEmbeddedView:platform_view platformViewsController:GetWeakPtr() gestureRecognizersBlockingPolicy:gesture_recognizers_blocking_policies_[viewType]]; - touch_interceptors_[viewId] = - fml::scoped_nsobject(touch_interceptor); - ChildClippingView* clipping_view = [[ChildClippingView alloc] initWithFrame:CGRectZero]; [clipping_view addSubview:touch_interceptor]; - root_views_[viewId] = fml::scoped_nsobject(clipping_view); + + platform_views_[viewId] = PlatformViewData{ + .view = fml::scoped_nsobject>(embedded_view), // + .touch_interceptor = + fml::scoped_nsobject(touch_interceptor), // + .root_view = fml::scoped_nsobject(clipping_view) // + }; result(nil); } @@ -269,7 +272,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, NSNumber* arg = [call arguments]; int64_t viewId = [arg longLongValue]; - if (views_.count(viewId) == 0) { + if (platform_views_.count(viewId) == 0) { result([FlutterError errorWithCode:@"unknown_view" message:@"trying to dispose an unknown" details:[NSString stringWithFormat:@"view id: '%lld'", viewId]]); @@ -285,14 +288,14 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, NSDictionary* args = [call arguments]; int64_t viewId = [args[@"id"] longLongValue]; - if (views_.count(viewId) == 0) { + if (platform_views_.count(viewId) == 0) { result([FlutterError errorWithCode:@"unknown_view" message:@"trying to set gesture state for an unknown view" details:[NSString stringWithFormat:@"view id: '%lld'", viewId]]); return; } - FlutterTouchInterceptingView* view = touch_interceptors_[viewId].get(); + FlutterTouchInterceptingView* view = platform_views_[viewId].touch_interceptor.get(); [view releaseGesture]; result(nil); @@ -303,14 +306,14 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, NSDictionary* args = [call arguments]; int64_t viewId = [args[@"id"] longLongValue]; - if (views_.count(viewId) == 0) { + if (platform_views_.count(viewId) == 0) { result([FlutterError errorWithCode:@"unknown_view" message:@"trying to set gesture state for an unknown view" details:[NSString stringWithFormat:@"view id: '%lld'", viewId]]); return; } - FlutterTouchInterceptingView* view = touch_interceptors_[viewId].get(); + FlutterTouchInterceptingView* view = platform_views_[viewId].touch_interceptor.get(); [view blockGesture]; result(nil); @@ -418,15 +421,16 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, FlutterTouchInterceptingView* FlutterPlatformViewsController::GetFlutterTouchInterceptingViewByID( int64_t view_id) { - if (views_.empty()) { + if (platform_views_.empty()) { return nil; } - return touch_interceptors_[view_id].get(); + return platform_views_[view_id].touch_interceptor.get(); } long FlutterPlatformViewsController::FindFirstResponderPlatformViewId() { - for (auto const& [id, root_view] : root_views_) { - if (((UIView*)root_view.get()).flt_hasFirstResponderInViewHierarchySubtree) { + for (auto const& [id, platform_view_data] : platform_views_) { + UIView* root_view = (UIView*)platform_view_data.root_view.get(); + if (root_view.flt_hasFirstResponderInViewHierarchySubtree) { return id; } } @@ -590,7 +594,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, void FlutterPlatformViewsController::CompositeWithParams(int64_t view_id, const EmbeddedViewParams& params) { CGRect frame = CGRectMake(0, 0, params.sizePoints().width(), params.sizePoints().height()); - FlutterTouchInterceptingView* touchInterceptor = touch_interceptors_[view_id].get(); + FlutterTouchInterceptingView* touchInterceptor = platform_views_[view_id].touch_interceptor.get(); #if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG FML_DCHECK(CGPointEqualToPoint([touchInterceptor embeddedView].frame.origin, CGPointZero)); if (non_zero_origin_views_.find(view_id) == non_zero_origin_views_.end() && @@ -614,7 +618,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, touchInterceptor.alpha = 1; const MutatorsStack& mutatorStack = params.mutatorsStack(); - UIView* clippingView = root_views_[view_id].get(); + UIView* clippingView = platform_views_[view_id].root_view.get(); // The frame of the clipping view should be the final bounding rect. // Because the translate matrix in the Mutator Stack also includes the offset, // when we apply the transforms matrix in |ApplyMutators|, we need @@ -631,28 +635,20 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, } void FlutterPlatformViewsController::Reset() { - std::vector views; - views.reserve(composition_order_.size()); - for (int64_t view_id : composition_order_) { - views.push_back(root_views_[view_id].get()); - } - - fml::TaskRunner::RunNowOrPostTask(platform_task_runner_, [views = views]() { - for (auto* sub_view : views) { - [sub_view removeFromSuperview]; - } - }); - - root_views_.clear(); - touch_interceptors_.clear(); - views_.clear(); composition_order_.clear(); slices_.clear(); current_composition_params_.clear(); - clip_count_.clear(); views_to_recomposite_.clear(); layer_pool_->RecycleLayers(); visited_platform_views_.clear(); + + fml::TaskRunner::RunNowOrPostTask( + platform_task_runner_, [&, composition_order = composition_order_]() { + for (int64_t view_id : composition_order_) { + [platform_views_[view_id].root_view.get() removeFromSuperview]; + } + platform_views_.clear(); + }); } bool FlutterPlatformViewsController::SubmitFrame(GrDirectContext* gr_context, @@ -668,7 +664,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, LayersMap platform_view_layers; std::vector callbacks; - auto did_submit = true; + bool did_submit = true; std::unordered_map view_rects; for (int64_t view_id : composition_order_) { @@ -730,8 +726,12 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, layer->did_submit_last_frame = frame->Submit(); did_submit &= layer->did_submit_last_frame; - platform_view_layers[view_id].push_back( - {.rect = overlay->second, .view_id = view_id, .overlay_id = overlay_id, .layer = layer}); + platform_view_layers[view_id] = LayerData{ + .rect = overlay->second, // + .view_id = view_id, // + .overlay_id = overlay_id, // + .layer = layer // + }; overlay_id++; } @@ -752,16 +752,14 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, views_to_recomposite = views_to_recomposite_, // callbacks = std::move(callbacks), // composition_order = composition_order_, // - unused_layers = std::move(unused_layers), - views_to_dispose = GetViewsToDispose() // + unused_layers = std::move(unused_layers) // ]() mutable { PerformSubmit(platform_view_layers, // callbacks, // current_composition_params, // views_to_recomposite, // composition_order, // - unused_layers, // - views_to_dispose // + unused_layers // ); }; @@ -778,7 +776,8 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, auto missing_layer_count = required_overlay_layers - layer_pool_->size(); - // Workaround for FLutterPlatformViewsTest + // If raster thread is merged because we're in a unit test or running on simulator, then + // we don't need to post a task to create an overlay layer. if ([[NSThread currentThread] isMainThread]) { // Create Missing Layers for (auto i = 0u; i < missing_layer_count; i++) { @@ -811,25 +810,23 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, std::map& current_composition_params, const std::unordered_set& views_to_recomposite, const std::vector& composition_order, - const std::vector>& unused_layers, - const std::vector& views_to_dispose) { - TRACE_EVENT0("flutter", "FlutterPlatformViewsController::SubmitFrame::CATransaction"); + const std::vector>& unused_layers) { + TRACE_EVENT0("flutter", "FlutterPlatformViewsController::PerformSubmit"); + FML_DCHECK([[NSThread currentThread] isMainThread]); [CATransaction begin]; // Configure Flutter overlay views. - for (const auto& [key, layers] : platform_view_layers) { - for (const auto& layer_data : layers) { - layer_data.layer->UpdateViewState(flutter_view_, // - layer_data.rect, // - layer_data.view_id, // - layer_data.overlay_id // - ); - } + for (const auto& [key, layer_data] : platform_view_layers) { + layer_data.layer->UpdateViewState(flutter_view_, // + layer_data.rect, // + layer_data.view_id, // + layer_data.overlay_id // + ); } // Dispose unused Flutter Views. - for (auto& view : views_to_dispose) { + for (auto& view : GetViewsToDispose()) { [view removeFromSuperview]; } @@ -857,7 +854,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, } void FlutterPlatformViewsController::BringLayersIntoView( - LayersMap layer_map, + const LayersMap& layer_map, const std::vector& composition_order) { FML_DCHECK(flutter_view_); UIView* flutter_view = flutter_view_.get(); @@ -866,11 +863,12 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, 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 layers = layer_map[platform_view_id]; - UIView* platform_view_root = root_views_[platform_view_id].get(); + UIView* platform_view_root = platform_views_[platform_view_id].root_view.get(); [desired_platform_subviews addObject:platform_view_root]; - for (const auto& layer_data : layers) { - [desired_platform_subviews addObject:layer_data.layer->overlay_view_wrapper]; + + auto maybe_layer_data = layer_map.find(platform_view_id); + if (maybe_layer_data != layer_map.end()) { + [desired_platform_subviews addObject:maybe_layer_data->second.layer->overlay_view_wrapper]; } previous_composition_order_.push_back(platform_view_id); } @@ -941,7 +939,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, // Remove unused platform views. for (int64_t view_id : previous_composition_order_) { if (composition_order_set.find(view_id) == composition_order_set.end()) { - UIView* platform_view_root = root_views_[view_id].get(); + UIView* platform_view_root = platform_views_[view_id].root_view.get(); [platform_view_root removeFromSuperview]; } } @@ -961,14 +959,11 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, views_to_delay_dispose.insert(viewId); continue; } - UIView* root_view = root_views_[viewId].get(); + UIView* root_view = platform_views_[viewId].root_view.get(); views.push_back(root_view); - views_.erase(viewId); - touch_interceptors_.erase(viewId); - root_views_.erase(viewId); current_composition_params_.erase(viewId); - clip_count_.erase(viewId); views_to_recomposite_.erase(viewId); + platform_views_.erase(viewId); } views_to_dispose_ = std::move(views_to_delay_dispose); return views; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index be0f90f45ce4a..7132321153ac1 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -316,7 +316,7 @@ class FlutterPlatformViewsController { std::shared_ptr layer; }; - using LayersMap = std::map>; + using LayersMap = std::map; /// Update the buffers and mutate the platform views in CATransaction on the platform thread. void PerformSubmit(const LayersMap& platform_view_layers, @@ -324,8 +324,7 @@ class FlutterPlatformViewsController { std::map& current_composition_params, const std::unordered_set& views_to_recomposite, const std::vector& composition_order, - const std::vector>& unused_layers, - const std::vector& views_to_dispose); + const std::vector>& unused_layers); /// @brief Populate any missing overlay layers. /// @@ -377,7 +376,8 @@ class FlutterPlatformViewsController { // Appends the overlay views and platform view and sets their z index based on the composition // order. - void BringLayersIntoView(LayersMap layer_map, const std::vector& composition_order); + void BringLayersIntoView(const LayersMap& layer_map, + const std::vector& composition_order); // Resets the state of the frame. void ResetFrameState(); @@ -396,54 +396,76 @@ class FlutterPlatformViewsController { fml::scoped_nsobject> flutter_view_controller_; fml::scoped_nsobject mask_view_pool_; std::map>> factories_; - std::map>> views_; - std::map> touch_interceptors_; - // Mapping a platform view ID to the top most parent view (root_view) of a platform view. In - // |SubmitFrame|, root_views_ are added to flutter_view_ as child views. - // - // The platform view with the view ID is a child of the root view; If the platform view is not - // clipped, and no clipping view is added, the root view will be the intercepting view. - std::map> root_views_; - // Mapping a platform view ID to its latest composition params. - std::map current_composition_params_; - // Mapping a platform view ID to the count of the clipping operations that were applied to the - // platform view last time it was composited. - std::map clip_count_; + + // The FlutterPlatformViewGestureRecognizersBlockingPolicy for each type of platform view. + std::map + gesture_recognizers_blocking_policies_; + + /// The size of the current onscreen surface in physical pixels. SkISize frame_size_; - // Method channel `OnDispose` calls adds the views to be disposed to this set to be disposed on - // the next frame. + /// The task runner for posting tasks to the platform thread. + fml::RefPtr platform_task_runner_; + + /// Each of the following maps stores part of the platform view hierarchy according to its + /// ID. + /// + /// The views_ map contains the platform view itself. + /// The touch_interceptors_ map contains the touch intercepting views to accept gestures. + /// Finally, the root views contains either the touch_interceptor or the child clipping + /// view. + /// + /// These maps must only be accessed on the platform thread. + struct PlatformViewData { + fml::scoped_nsobject> view; + fml::scoped_nsobject touch_interceptor; + fml::scoped_nsobject root_view; + }; + + std::map platform_views_; + /// The composition parameters for each platform view. + /// + /// This state is only modified on the raster thread. + std::map current_composition_params_; + + /// Method channel `OnDispose` calls adds the views to be disposed to this set to be disposed on + /// the next frame. + /// + /// This state is modified on both the platform and raster thread. std::unordered_set views_to_dispose_; - // A vector of embedded view IDs according to their composition order. - // The last ID in this vector belond to the that is composited on top of all others. + /// view IDs in composition order. + /// + /// This state is only modified on the raster thread. std::vector composition_order_; - // A vector of visited platform view IDs. + /// platform view IDs visited during layer tree composition. + /// + /// This state is only modified on the raster thread. std::vector visited_platform_views_; - // Only composite platform views in this set. + /// Only composite platform views in this set. + /// + /// This state is only modified on the raster thread. std::unordered_set views_to_recomposite_; - // The FlutterPlatformViewGestureRecognizersBlockingPolicy for each type of platform view. - std::map - gesture_recognizers_blocking_policies_; - #if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG - // A set to keep track of embedded views that does not have (0, 0) origin. - // An insertion triggers a warning message about non-zero origin logged on the debug console. - // See https://github.com/flutter/flutter/issues/109700 for details. + /// A set to keep track of embedded views that do not have (0, 0) origin. + /// An insertion triggers a warning message about non-zero origin logged on the debug console. + /// See https://github.com/flutter/flutter/issues/109700 for details. std::unordered_set non_zero_origin_views_; #endif - fml::RefPtr platform_task_runner_; - /// @brief The composition order from the previous thread. /// /// Only accessed from the platform thread. std::vector previous_composition_order_; - /// Whether the previous frame had an active composition order. + /// Whether the previous frame had any platform views in active composition order. + /// + /// This state is tracked so that the first frame after removing the last platform view + /// runs through the platform view rendering code path, giving us a chance to remove the + /// platform view from the UIView hierarchy. /// /// Only accessed from the raster thread. bool had_platform_views_ = false; From bcd851952646d4b54942a0746c5cf7e91f5d0778 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Sat, 27 Jul 2024 12:10:30 -0700 Subject: [PATCH 41/49] reset before clearning composition order. --- .../ios/framework/Source/FlutterPlatformViews.mm | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index f13cec87a7790..c3bc984339d38 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -635,13 +635,6 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, } void FlutterPlatformViewsController::Reset() { - composition_order_.clear(); - slices_.clear(); - current_composition_params_.clear(); - views_to_recomposite_.clear(); - layer_pool_->RecycleLayers(); - visited_platform_views_.clear(); - fml::TaskRunner::RunNowOrPostTask( platform_task_runner_, [&, composition_order = composition_order_]() { for (int64_t view_id : composition_order_) { @@ -649,6 +642,13 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, } platform_views_.clear(); }); + + composition_order_.clear(); + slices_.clear(); + current_composition_params_.clear(); + views_to_recomposite_.clear(); + layer_pool_->RecycleLayers(); + visited_platform_views_.clear(); } bool FlutterPlatformViewsController::SubmitFrame(GrDirectContext* gr_context, From 5f6a8d4552dbdf39da16ec8a0d5f6a8128edd69a Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Tue, 30 Jul 2024 14:05:24 -0700 Subject: [PATCH 42/49] switch to SurfaceFrame API. --- flow/surface_frame.h | 5 -- impeller/renderer/backend/metal/surface_mtl.h | 10 +--- .../renderer/backend/metal/surface_mtl.mm | 11 +--- shell/gpu/gpu_surface_metal_impeller.mm | 4 +- shell/gpu/gpu_surface_metal_skia.mm | 7 ++- .../framework/Source/FlutterPlatformViews.mm | 55 ++++++++----------- .../Source/FlutterPlatformViews_Internal.h | 4 +- 7 files changed, 37 insertions(+), 59 deletions(-) diff --git a/flow/surface_frame.h b/flow/surface_frame.h index 3fa64c3bd27e9..651c8812d956c 100644 --- a/flow/surface_frame.h +++ b/flow/surface_frame.h @@ -31,9 +31,6 @@ class SurfaceFrame { std::function; using SubmitCallback = std::function; - using DeferredSubmit = std::function; - using SubmitReciever = std::function; - // Information about the underlying framebuffer struct FramebufferInfo { // Indicates whether or not the surface supports pixel readback as used in @@ -98,8 +95,6 @@ class SurfaceFrame { bool frame_boundary = true; bool present_with_transaction = false; - - SubmitReciever submit_receiver; }; bool Encode(); diff --git a/impeller/renderer/backend/metal/surface_mtl.h b/impeller/renderer/backend/metal/surface_mtl.h index 1f93bde937045..2bb4c3aaa8c45 100644 --- a/impeller/renderer/backend/metal/surface_mtl.h +++ b/impeller/renderer/backend/metal/surface_mtl.h @@ -58,11 +58,8 @@ class SurfaceMTL final : public Surface { // Returns a Rect defining the area of the surface in device pixels IRect coverage() const; - using DeferredSubmit = std::function; - using SubmitReciever = std::function; - - void SetSubmitInfo(const SubmitReciever& submit_reciever) { - submit_reciever_ = submit_reciever; + void PresentWithTransaction(bool present_with_transaction) { + present_with_transaction_ = present_with_transaction; } // |Surface| @@ -81,8 +78,7 @@ class SurfaceMTL final : public Surface { bool requires_blit_ = false; std::optional clip_rect_; bool frame_boundary_ = false; - - SubmitReciever submit_reciever_; + bool present_with_transaction_ = false; static bool ShouldPerformPartialRepaint(std::optional damage_rect); diff --git a/impeller/renderer/backend/metal/surface_mtl.mm b/impeller/renderer/backend/metal/surface_mtl.mm index 74e23c64cf6bc..2d257c7958365 100644 --- a/impeller/renderer/backend/metal/surface_mtl.mm +++ b/impeller/renderer/backend/metal/surface_mtl.mm @@ -286,7 +286,8 @@ - (void)flutterPrepareForPresent:(nonnull id)commandBuffer; // If the threads have been merged, or there is a pending frame capture, // then block on cmd buffer scheduling to ensure that the // transaction/capture work correctly. - if ([[NSThread currentThread] isMainThread] || + if (present_with_transaction_ || + [[NSThread currentThread] isMainThread] || [[MTLCaptureManager sharedCaptureManager] isCapturing] || alwaysWaitForScheduling) { TRACE_EVENT0("flutter", "waitUntilScheduled"); @@ -297,14 +298,6 @@ - (void)flutterPrepareForPresent:(nonnull id)commandBuffer; [command_buffer waitUntilScheduled]; #endif // defined(FML_OS_IOS_SIMULATOR) && defined(FML_ARCH_CPU_X86_64) [drawable_ present]; - } else if (submit_reciever_) { - auto drawable = drawable_; - [command_buffer commit]; - [command_buffer waitUntilScheduled]; - submit_reciever_([drawable]() -> bool { - [drawable present]; - return true; - }); } else { // The drawable may come from a FlutterMetalLayer, so it can't be // presented through the command buffer. diff --git a/shell/gpu/gpu_surface_metal_impeller.mm b/shell/gpu/gpu_surface_metal_impeller.mm index 7afac78ff5896..09e7f5d3519da 100644 --- a/shell/gpu/gpu_surface_metal_impeller.mm +++ b/shell/gpu/gpu_surface_metal_impeller.mm @@ -161,7 +161,7 @@ if (!surface) { return false; } - surface->SetSubmitInfo(surface_frame.submit_info().submit_receiver); + surface->PresentWithTransaction(surface_frame.submit_info().present_with_transaction); if (clip_rect && clip_rect->IsEmpty()) { surface_frame.set_user_data(std::move(surface)); @@ -282,7 +282,7 @@ auto surface = impeller::SurfaceMTL::MakeFromTexture(aiks_context->GetContext(), mtl_texture, clip_rect); - surface->SetSubmitInfo(surface_frame.submit_info().submit_receiver); + surface->PresentWithTransaction(surface_frame.submit_info().present_with_transaction); if (clip_rect && clip_rect->IsEmpty()) { return surface->Present(); diff --git a/shell/gpu/gpu_surface_metal_skia.mm b/shell/gpu/gpu_surface_metal_skia.mm index bc0ed9d60c160..9b7e255345a60 100644 --- a/shell/gpu/gpu_surface_metal_skia.mm +++ b/shell/gpu/gpu_surface_metal_skia.mm @@ -157,8 +157,8 @@ return nullptr; } - auto encode_callback = [this, drawable, mtl_layer](const SurfaceFrame& surface_frame, - DlCanvas* canvas) -> bool { + SurfaceFrame::EncodeCallback encode_callback = + [this, drawable, mtl_layer](const SurfaceFrame& surface_frame, DlCanvas* canvas) -> bool { mtl_layer.presentsWithTransaction = surface_frame.submit_info().present_with_transaction; if (canvas == nullptr) { FML_DLOG(ERROR) << "Canvas not available."; @@ -187,7 +187,8 @@ return true; }; - auto submit_callback = [this, drawable](const SurfaceFrame& surface_frame) -> bool { + SurfaceFrame::SubmitCallback submit_callback = + [this, drawable](const SurfaceFrame& surface_frame) -> bool { TRACE_EVENT0("flutter", "GPUSurfaceMetal::Submit"); return delegate_->PresentDrawable(drawable); }; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 848f8a10d15f0..edad174b01c0e 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -670,9 +670,9 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, } had_platform_views_ = true; + bool did_encode = true; LayersMap platform_view_layers; - std::vector callbacks; - bool did_submit = true; + std::vector> surface_frames; std::unordered_map view_rects; for (int64_t view_id : composition_order_) { @@ -724,56 +724,49 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, // This flutter view is never the last in a frame, since we always submit the // underlay view last. - frame->set_submit_info( - {.frame_boundary = false, - .present_with_transaction = true, - .submit_receiver = [&callbacks](const SurfaceFrame::DeferredSubmit& cb) { - callbacks.push_back(cb); - }}); + frame->set_submit_info({.frame_boundary = false, .present_with_transaction = true}); + layer->did_submit_last_frame = frame->Encode(); - layer->did_submit_last_frame = frame->Submit(); - - did_submit &= layer->did_submit_last_frame; + did_encode &= layer->did_submit_last_frame; platform_view_layers[view_id] = LayerData{ .rect = overlay->second, // .view_id = view_id, // .overlay_id = overlay_id, // .layer = layer // }; + surface_frames.push_back(std::move(frame)); overlay_id++; } - background_frame->set_submit_info( - {.present_with_transaction = true, - .submit_receiver = [&callbacks](const SurfaceFrame::DeferredSubmit& cb) { - callbacks.push_back(cb); - }}); - did_submit &= background_frame->Submit(); + background_frame->set_submit_info({.present_with_transaction = true}); + background_frame->Encode(); + surface_frames.push_back(std::move(background_frame)); // Mark all layers as available, so they can be used in the next frame. std::vector> unused_layers = layer_pool_->RemoveUnusedLayers(); layer_pool_->RecycleLayers(); - auto task = [&, platform_view_layers = std::move(platform_view_layers), // - current_composition_params = current_composition_params_, // - views_to_recomposite = views_to_recomposite_, // - callbacks = std::move(callbacks), // - composition_order = composition_order_, // - unused_layers = std::move(unused_layers) // + auto task = [&, // + platform_view_layers = std::move(platform_view_layers), // + current_composition_params = current_composition_params_, // + views_to_recomposite = views_to_recomposite_, // + composition_order = composition_order_, // + unused_layers = std::move(unused_layers), // + surface_frames = std::move(surface_frames) // ]() mutable { PerformSubmit(platform_view_layers, // - callbacks, // current_composition_params, // views_to_recomposite, // composition_order, // - unused_layers // + unused_layers, // + std::move(surface_frames) // ); }; - fml::TaskRunner::RunNowOrPostTask(platform_task_runner_, task); + fml::TaskRunner::RunNowOrPostTask(platform_task_runner_, fml::MakeCopyable(std::move(task))); - return did_submit; + return did_encode; } void FlutterPlatformViewsController::CreateMissingOverlays( @@ -814,11 +807,11 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, /// Update the buffers and mutate the platform views in CATransaction on the platform thread. void FlutterPlatformViewsController::PerformSubmit( const LayersMap& platform_view_layers, - const std::vector& callbacks, std::map& current_composition_params, const std::unordered_set& views_to_recomposite, const std::vector& composition_order, - const std::vector>& unused_layers) { + const std::vector>& unused_layers, + std::vector> surface_frames) { TRACE_EVENT0("flutter", "FlutterPlatformViewsController::PerformSubmit"); FML_DCHECK([[NSThread currentThread] isMainThread]); @@ -844,8 +837,8 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, } // Present callbacks. - for (const auto& cb : callbacks) { - cb(); + for (const auto& frame : surface_frames) { + frame->Submit(); } // If a layer was allocated in the previous frame, but it's not used in the current frame, diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index 7132321153ac1..d37db3251d754 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -320,11 +320,11 @@ class FlutterPlatformViewsController { /// Update the buffers and mutate the platform views in CATransaction on the platform thread. void PerformSubmit(const LayersMap& platform_view_layers, - const std::vector& callbacks, std::map& current_composition_params, const std::unordered_set& views_to_recomposite, const std::vector& composition_order, - const std::vector>& unused_layers); + const std::vector>& unused_layers, + std::vector> surface_frames); /// @brief Populate any missing overlay layers. /// From be7853d61e299be29af572c4660d3c840b61e9ac Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Tue, 30 Jul 2024 14:05:46 -0700 Subject: [PATCH 43/49] ++ --- impeller/renderer/backend/metal/surface_mtl.mm | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/impeller/renderer/backend/metal/surface_mtl.mm b/impeller/renderer/backend/metal/surface_mtl.mm index 2d257c7958365..c94ad6cd4c0da 100644 --- a/impeller/renderer/backend/metal/surface_mtl.mm +++ b/impeller/renderer/backend/metal/surface_mtl.mm @@ -286,8 +286,7 @@ - (void)flutterPrepareForPresent:(nonnull id)commandBuffer; // If the threads have been merged, or there is a pending frame capture, // then block on cmd buffer scheduling to ensure that the // transaction/capture work correctly. - if (present_with_transaction_ || - [[NSThread currentThread] isMainThread] || + if (present_with_transaction_ || [[NSThread currentThread] isMainThread] || [[MTLCaptureManager sharedCaptureManager] isCapturing] || alwaysWaitForScheduling) { TRACE_EVENT0("flutter", "waitUntilScheduled"); From b19b4d878ed393e93d8d0c5205c9ddae29bce46a Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Tue, 30 Jul 2024 14:07:03 -0700 Subject: [PATCH 44/49] Remove extra include. --- .../darwin/ios/framework/Source/FlutterPlatformViews_Internal.h | 1 - 1 file changed, 1 deletion(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index d37db3251d754..3e2bb927ee1f7 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -11,7 +11,6 @@ #include "third_party/skia/include/core/SkRect.h" #include -#include #include "flutter/flow/surface.h" #include "flutter/fml/memory/weak_ptr.h" From ab087398440ee62991d891513d2dc9a98161de82 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Tue, 30 Jul 2024 14:56:59 -0700 Subject: [PATCH 45/49] clang tidy --- .../darwin/ios/framework/Source/FlutterPlatformViews.mm | 4 ++-- .../ios/framework/Source/FlutterPlatformViews_Internal.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index edad174b01c0e..9bab722e0d338 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -760,7 +760,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, views_to_recomposite, // composition_order, // unused_layers, // - std::move(surface_frames) // + surface_frames // ); }; @@ -811,7 +811,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, const std::unordered_set& views_to_recomposite, const std::vector& composition_order, const std::vector>& unused_layers, - std::vector> surface_frames) { + const std::vector>& surface_frames) { TRACE_EVENT0("flutter", "FlutterPlatformViewsController::PerformSubmit"); FML_DCHECK([[NSThread currentThread] isMainThread]); diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index 3e2bb927ee1f7..ef6b00e9b6a16 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -323,7 +323,7 @@ class FlutterPlatformViewsController { const std::unordered_set& views_to_recomposite, const std::vector& composition_order, const std::vector>& unused_layers, - std::vector> surface_frames); + const std::vector>& surface_frames); /// @brief Populate any missing overlay layers. /// From 6e6e5a2b23658d81502f11b98c7bf7b0d0fd1bb7 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Tue, 30 Jul 2024 16:45:38 -0700 Subject: [PATCH 46/49] bracken feedback --- flow/surface_frame.h | 11 +++++++++++ impeller/renderer/backend/metal/surface_mtl.h | 3 +++ shell/gpu/gpu_surface_metal_impeller.mm | 7 ------- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/flow/surface_frame.h b/flow/surface_frame.h index 651c8812d956c..4e66be913cfa4 100644 --- a/flow/surface_frame.h +++ b/flow/surface_frame.h @@ -94,6 +94,17 @@ class SurfaceFrame { // Defaults to true, which is generally a safe value. bool frame_boundary = true; + // Whether this surface presents with a CATransaction on Apple platforms. + // + // When there are platform views in the scene, the drawable needs to be + // presented in the same CATransaction as the one created for platform view + // mutations. + // + // If the drawables are being presented from the raster thread, we cannot + // use a transaction as it will dirty the UIViews being presented. If there + // is a non-Flutter UIView active, such as in add2app or a + // presentViewController page transition, then this will cause CoreAnimation + // assertion errors and exit the app. bool present_with_transaction = false; }; diff --git a/impeller/renderer/backend/metal/surface_mtl.h b/impeller/renderer/backend/metal/surface_mtl.h index 2bb4c3aaa8c45..c5dd5a61eeff6 100644 --- a/impeller/renderer/backend/metal/surface_mtl.h +++ b/impeller/renderer/backend/metal/surface_mtl.h @@ -58,6 +58,9 @@ class SurfaceMTL final : public Surface { // Returns a Rect defining the area of the surface in device pixels IRect coverage() const; + /// Mark this surface as presenting with a transaction. + /// + /// If true, [Present] will block on the scheduling of a command buffer. void PresentWithTransaction(bool present_with_transaction) { present_with_transaction_ = present_with_transaction; } diff --git a/shell/gpu/gpu_surface_metal_impeller.mm b/shell/gpu/gpu_surface_metal_impeller.mm index 09e7f5d3519da..5041fc4092c11 100644 --- a/shell/gpu/gpu_surface_metal_impeller.mm +++ b/shell/gpu/gpu_surface_metal_impeller.mm @@ -112,13 +112,6 @@ last_texture, // mtl_layer // ](SurfaceFrame& surface_frame, DlCanvas* canvas) mutable -> bool { - // When there are platform views in the scene, the drawable needs to be presented in the - // same transaction as the one created for platform views. When the drawable are being - // presented from the raster thread, we may not be able to use a transaction as it will - // dirty the UIViews being presented. If there is a non-Flutter UIView active, such as in - // add2app or a presentViewController page transition, then this will cause CoreAnimation - // assertion errors and - // exit the app. mtl_layer.presentsWithTransaction = surface_frame.submit_info().present_with_transaction; if (!aiks_context) { From 355ab30a68c4fb35c2d2ac8bfacaa0a11a7b81bc Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Wed, 31 Jul 2024 20:05:02 -0700 Subject: [PATCH 47/49] more bracken review. --- .../framework/Source/FlutterPlatformViews.mm | 58 +++++++++---------- 1 file changed, 27 insertions(+), 31 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 9bab722e0d338..539acbe22581f 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -21,8 +21,10 @@ FLUTTER_ASSERT_ARC #ifdef FML_OS_IOS_SIMULATOR -// Note: this is an arbitrary number that attempts to account for cases -// where the platform view might be momentarily off the screen. +// The number of frames the rasterizer task runner will continue +// to run on the platform thread after no platform view is rendered. +// +// Note: this is an arbitrary number. static const int kDefaultMergedLeaseDuration = 10; #endif // FML_OS_IOS_SIMULATOR @@ -105,6 +107,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, void FlutterPlatformViewLayerPool::CreateLayer(GrDirectContext* gr_context, const std::shared_ptr& ios_context, MTLPixelFormat pixel_format) { + FML_DCHECK([[NSThread currentThread] isMainThread]); std::shared_ptr layer; fml::scoped_nsobject overlay_view; fml::scoped_nsobject overlay_view_wrapper; @@ -266,12 +269,13 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, ChildClippingView* clipping_view = [[ChildClippingView alloc] initWithFrame:CGRectZero]; [clipping_view addSubview:touch_interceptor]; - platform_views_[viewId] = PlatformViewData{ - .view = fml::scoped_nsobject>(embedded_view), // - .touch_interceptor = - fml::scoped_nsobject(touch_interceptor), // - .root_view = fml::scoped_nsobject(clipping_view) // - }; + platform_views_.emplace( + viewId, PlatformViewData{ + .view = fml::scoped_nsobject>(embedded_view), // + .touch_interceptor = + fml::scoped_nsobject(touch_interceptor), // + .root_view = fml::scoped_nsobject(clipping_view) // + }); result(nil); } @@ -643,6 +647,9 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, } void FlutterPlatformViewsController::Reset() { + // Reset will only be called from the raster thread or a merged raster/platform thread. + // platform_views_ must only be modified on the platform thread, and any operations that + // read or modify platform views should occur there. fml::TaskRunner::RunNowOrPostTask( platform_task_runner_, [&, composition_order = composition_order_]() { for (int64_t view_id : composition_order_) { @@ -664,6 +671,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, std::unique_ptr background_frame) { TRACE_EVENT0("flutter", "FlutterPlatformViewsController::SubmitFrame"); + // No platform views to render; we're done. if (flutter_view_ == nullptr || (composition_order_.empty() && !had_platform_views_)) { had_platform_views_ = false; return background_frame->Submit(); @@ -673,6 +681,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, bool did_encode = true; LayersMap platform_view_layers; std::vector> surface_frames; + surface_frames.reserve(composition_order_.size()); std::unordered_map view_rects; for (int64_t view_id : composition_order_) { @@ -694,9 +703,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, // If there are not sufficient overlay layers, we must construct them on the platform // thread, at least until we've refactored iOS surface creation to use IOSurfaces // instead of CALayers. - if (required_overlay_layers > layer_pool_->size()) { - CreateMissingOverlays(gr_context, ios_context, required_overlay_layers); - } + CreateMissingOverlays(gr_context, ios_context, required_overlay_layers); int64_t overlay_id = 0; for (int64_t view_id : composition_order_) { @@ -775,24 +782,15 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, size_t required_overlay_layers) { TRACE_EVENT0("flutter", "FlutterPlatformViewsController::CreateMissingLayers"); - auto missing_layer_count = required_overlay_layers - layer_pool_->size(); - - // If raster thread is merged because we're in a unit test or running on simulator, then - // we don't need to post a task to create an overlay layer. - if ([[NSThread currentThread] isMainThread]) { - // Create Missing Layers - for (auto i = 0u; i < missing_layer_count; i++) { - CreateLayer(gr_context, // - ios_context, // - ((FlutterView*)flutter_view_.get()).pixelFormat // - ); - } + if (required_overlay_layers <= layer_pool_->size()) { return; } + auto missing_layer_count = required_overlay_layers - layer_pool_->size(); + // If the raster thread isn't merged, create layers on the platform thread and block until + // complete. auto latch = std::make_shared(1u); - platform_task_runner_->PostTask([&]() { - // Create Missing Layers + fml::TaskRunner::RunNowOrPostTask(platform_task_runner_, [&]() { for (auto i = 0u; i < missing_layer_count; i++) { CreateLayer(gr_context, // ios_context, // @@ -801,7 +799,9 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, } latch->CountDown(); }); - latch->Wait(); + if (![[NSThread currentThread] isMainThread]) { + latch->Wait(); + } } /// Update the buffers and mutate the platform views in CATransaction on the platform thread. @@ -848,9 +848,6 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, // Organize the layers by their z indexes. BringLayersIntoView(platform_view_layers, composition_order); - // If the frame is submitted with embedded platform views, - // there should be a |[CATransaction begin]| call in this frame prior to all the drawing. - // If that case, we need to commit the transaction. [CATransaction commit]; } @@ -862,8 +859,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, previous_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]; + for (int64_t platform_view_id : composition_order) { UIView* platform_view_root = platform_views_[platform_view_id].root_view.get(); [desired_platform_subviews addObject:platform_view_root]; From 8cd1c7402fd63b52f8f4bd6465e6cc04734b21eb Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Thu, 1 Aug 2024 11:48:40 -0700 Subject: [PATCH 48/49] ++ --- .../ios/framework/Source/FlutterPlatformViews.mm | 9 ++++----- .../Source/FlutterPlatformViews_Internal.h | 13 +++++++------ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 539acbe22581f..59a0f5eab99f1 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -3,20 +3,19 @@ // found in the LICENSE file. #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h" -#include "fml/synchronization/count_down_latch.h" #include #include #include "flow/surface_frame.h" -#include "fml/logging.h" - #include "flutter/flow/view_slicer.h" #include "flutter/fml/make_copyable.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterOverlayView.h" #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterView.h" #import "flutter/shell/platform/darwin/ios/ios_surface.h" +#include "fml/logging.h" +#include "fml/synchronization/count_down_latch.h" FLUTTER_ASSERT_ARC @@ -807,7 +806,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, /// Update the buffers and mutate the platform views in CATransaction on the platform thread. void FlutterPlatformViewsController::PerformSubmit( const LayersMap& platform_view_layers, - std::map& current_composition_params, + std::unordered_map& current_composition_params, const std::unordered_set& views_to_recomposite, const std::vector& composition_order, const std::vector>& unused_layers, @@ -818,7 +817,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, [CATransaction begin]; // Configure Flutter overlay views. - for (const auto& [key, layer_data] : platform_view_layers) { + for (const auto& [view_id, layer_data] : platform_view_layers) { layer_data.layer->UpdateViewState(flutter_view_, // layer_data.rect, // layer_data.view_id, // diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index ef6b00e9b6a16..35192be0f50c3 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -315,11 +315,11 @@ class FlutterPlatformViewsController { std::shared_ptr layer; }; - using LayersMap = std::map; + using LayersMap = std::unordered_map; /// Update the buffers and mutate the platform views in CATransaction on the platform thread. void PerformSubmit(const LayersMap& platform_view_layers, - std::map& current_composition_params, + std::unordered_map& current_composition_params, const std::unordered_set& views_to_recomposite, const std::vector& composition_order, const std::vector>& unused_layers, @@ -394,10 +394,11 @@ class FlutterPlatformViewsController { fml::scoped_nsobject flutter_view_; fml::scoped_nsobject> flutter_view_controller_; fml::scoped_nsobject mask_view_pool_; - std::map>> factories_; + std::unordered_map>> + factories_; // The FlutterPlatformViewGestureRecognizersBlockingPolicy for each type of platform view. - std::map + std::unordered_map gesture_recognizers_blocking_policies_; /// The size of the current onscreen surface in physical pixels. @@ -421,11 +422,11 @@ class FlutterPlatformViewsController { fml::scoped_nsobject root_view; }; - std::map platform_views_; + std::unordered_map platform_views_; /// The composition parameters for each platform view. /// /// This state is only modified on the raster thread. - std::map current_composition_params_; + std::unordered_map current_composition_params_; /// Method channel `OnDispose` calls adds the views to be disposed to this set to be disposed on /// the next frame. From 30e7b97c5e4e8bd53cffdd443ad37696d6e40a0a Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Thu, 1 Aug 2024 12:36:29 -0700 Subject: [PATCH 49/49] review comments and remove dead code. --- .../framework/Source/FlutterPlatformViews.mm | 12 ------ .../Source/FlutterPlatformViewsTest.mm | 40 ------------------- .../Source/FlutterPlatformViews_Internal.h | 30 +++++++++----- 3 files changed, 19 insertions(+), 63 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 59a0f5eab99f1..5509c2245fdf6 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -448,18 +448,6 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, return -1; } -int FlutterPlatformViewsController::CountClips(const MutatorsStack& mutators_stack) { - std::vector>::const_reverse_iterator iter = mutators_stack.Bottom(); - int clipCount = 0; - while (iter != mutators_stack.Top()) { - if ((*iter)->IsClipType()) { - clipCount++; - } - ++iter; - } - return clipCount; -} - void FlutterPlatformViewsController::ClipViewSetMaskView(UIView* clipView) { FML_DCHECK([[NSThread currentThread] isMainThread]); if (clipView.maskView) { diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm index 69779033ac01c..16e51ff0cd1d0 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm @@ -186,7 +186,6 @@ - (void)testFlutterViewOnlyCreateOnceInOneFrame { std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); XCTAssertNotNil(gMockPlatformView); @@ -337,7 +336,6 @@ - (void)testApplyBackdropFilter { std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); flutterPlatformViewsController->CompositeWithParams( 2, flutterPlatformViewsController->GetCompositionParams(2)); @@ -415,7 +413,6 @@ - (void)testApplyBackdropFilterWithCorrectFrame { std::make_unique(screenScaleMatrix, SkSize::Make(5, 10), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); flutterPlatformViewsController->CompositeWithParams( 2, flutterPlatformViewsController->GetCompositionParams(2)); @@ -495,7 +492,6 @@ - (void)testApplyMultipleBackdropFilters { std::make_unique(screenScaleMatrix, SkSize::Make(20, 20), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); flutterPlatformViewsController->CompositeWithParams( 2, flutterPlatformViewsController->GetCompositionParams(2)); @@ -572,7 +568,6 @@ - (void)testAddBackdropFilters { std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); flutterPlatformViewsController->CompositeWithParams( 2, flutterPlatformViewsController->GetCompositionParams(2)); @@ -612,7 +607,6 @@ - (void)testAddBackdropFilters { SkSize::Make(10, 10), stack2); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); flutterPlatformViewsController->CompositeWithParams( 2, flutterPlatformViewsController->GetCompositionParams(2)); @@ -697,7 +691,6 @@ - (void)testRemoveBackdropFilters { std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); flutterPlatformViewsController->CompositeWithParams( 2, flutterPlatformViewsController->GetCompositionParams(2)); @@ -735,7 +728,6 @@ - (void)testRemoveBackdropFilters { SkSize::Make(10, 10), stack2); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); flutterPlatformViewsController->CompositeWithParams( 2, flutterPlatformViewsController->GetCompositionParams(2)); @@ -777,7 +769,6 @@ - (void)testRemoveBackdropFilters { SkSize::Make(10, 10), stack2); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); flutterPlatformViewsController->CompositeWithParams( 2, flutterPlatformViewsController->GetCompositionParams(2)); @@ -846,7 +837,6 @@ - (void)testEditBackdropFilters { std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); flutterPlatformViewsController->CompositeWithParams( 2, flutterPlatformViewsController->GetCompositionParams(2)); @@ -893,7 +883,6 @@ - (void)testEditBackdropFilters { SkSize::Make(10, 10), stack2); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); flutterPlatformViewsController->CompositeWithParams( 2, flutterPlatformViewsController->GetCompositionParams(2)); @@ -950,7 +939,6 @@ - (void)testEditBackdropFilters { SkSize::Make(10, 10), stack2); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); flutterPlatformViewsController->CompositeWithParams( 2, flutterPlatformViewsController->GetCompositionParams(2)); @@ -1005,7 +993,6 @@ - (void)testEditBackdropFilters { SkSize::Make(10, 10), stack2); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); flutterPlatformViewsController->CompositeWithParams( 2, flutterPlatformViewsController->GetCompositionParams(2)); @@ -1056,7 +1043,6 @@ - (void)testEditBackdropFilters { SkSize::Make(10, 10), stack2); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); flutterPlatformViewsController->CompositeWithParams( 2, flutterPlatformViewsController->GetCompositionParams(2)); @@ -1139,7 +1125,6 @@ - (void)testApplyBackdropFilterNotDlBlurImageFilter { std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); flutterPlatformViewsController->CompositeWithParams( 2, flutterPlatformViewsController->GetCompositionParams(2)); @@ -1182,7 +1167,6 @@ - (void)testApplyBackdropFilterNotDlBlurImageFilter { SkSize::Make(10, 10), stack2); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); flutterPlatformViewsController->CompositeWithParams( 2, flutterPlatformViewsController->GetCompositionParams(2)); @@ -1224,7 +1208,6 @@ - (void)testApplyBackdropFilterNotDlBlurImageFilter { SkSize::Make(10, 10), stack2); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); flutterPlatformViewsController->CompositeWithParams( 2, flutterPlatformViewsController->GetCompositionParams(2)); @@ -1266,7 +1249,6 @@ - (void)testApplyBackdropFilterNotDlBlurImageFilter { SkSize::Make(10, 10), stack2); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); flutterPlatformViewsController->CompositeWithParams( 2, flutterPlatformViewsController->GetCompositionParams(2)); @@ -1302,7 +1284,6 @@ - (void)testApplyBackdropFilterNotDlBlurImageFilter { SkSize::Make(10, 10), stack2); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); flutterPlatformViewsController->CompositeWithParams( 2, flutterPlatformViewsController->GetCompositionParams(2)); @@ -1465,7 +1446,6 @@ - (void)testCompositePlatformView { std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); flutterPlatformViewsController->CompositeWithParams( 2, flutterPlatformViewsController->GetCompositionParams(2)); @@ -1527,7 +1507,6 @@ - (void)testBackdropFilterCorrectlyPushedAndReset { auto filter = std::make_shared(5, 2, flutter::DlTileMode::kClamp); flutterPlatformViewsController->PushFilterToVisitedPlatformViews( filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)); - flutterPlatformViewsController->CompositeEmbeddedView(2); flutterPlatformViewsController->CompositeWithParams( 2, flutterPlatformViewsController->GetCompositionParams(2)); @@ -1558,7 +1537,6 @@ - (void)testBackdropFilterCorrectlyPushedAndReset { std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); flutterPlatformViewsController->BeginFrame(SkISize::Make(0, 0)); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams2)); - flutterPlatformViewsController->CompositeEmbeddedView(2); flutterPlatformViewsController->CompositeWithParams( 2, flutterPlatformViewsController->GetCompositionParams(2)); @@ -1631,7 +1609,6 @@ - (void)testChildClippingViewShouldBeTheBoundingRectOfPlatformView { std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); flutterPlatformViewsController->CompositeWithParams( 2, flutterPlatformViewsController->GetCompositionParams(2)); @@ -1713,7 +1690,6 @@ - (void)testClipsDoNotInterceptWithPlatformViewShouldNotAddMaskView { SkMatrix::Concat(screenScaleMatrix, translateMatrix), SkSize::Make(5, 5), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); flutterPlatformViewsController->CompositeWithParams( 2, flutterPlatformViewsController->GetCompositionParams(2)); @@ -1784,7 +1760,6 @@ - (void)testClipRRectOnlyHasCornersInterceptWithPlatformViewShouldAddMaskView { SkMatrix::Concat(screenScaleMatrix, translateMatrix), SkSize::Make(5, 5), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); flutterPlatformViewsController->CompositeWithParams( 2, flutterPlatformViewsController->GetCompositionParams(2)); @@ -1850,7 +1825,6 @@ - (void)testClipRect { std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); flutterPlatformViewsController->CompositeWithParams( 2, flutterPlatformViewsController->GetCompositionParams(2)); @@ -1927,7 +1901,6 @@ - (void)testClipRRect { std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); flutterPlatformViewsController->CompositeWithParams( 2, flutterPlatformViewsController->GetCompositionParams(2)); @@ -2032,7 +2005,6 @@ - (void)testClipPath { std::make_unique(screenScaleMatrix, SkSize::Make(10, 10), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); flutterPlatformViewsController->CompositeWithParams( 2, flutterPlatformViewsController->GetCompositionParams(2)); @@ -2498,7 +2470,6 @@ - (void)testFlutterPlatformViewControllerSubmitFrameWithoutFlutterViewNotCrashin std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams_1)); - flutterPlatformViewsController->CompositeEmbeddedView(2); flutterPlatformViewsController->CompositeWithParams( 2, flutterPlatformViewsController->GetCompositionParams(2)); @@ -2514,7 +2485,6 @@ - (void)testFlutterPlatformViewControllerSubmitFrameWithoutFlutterViewNotCrashin auto embeddedViewParams_2 = std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams_2)); - flutterPlatformViewsController->CompositeEmbeddedView(2); flutterPlatformViewsController->CompositeWithParams( 2, flutterPlatformViewsController->GetCompositionParams(2)); @@ -2571,7 +2541,6 @@ - (void)testFlutterPlatformViewControllerSubmitFrameWithoutFlutterViewNotCrashin auto embeddedViewParams = std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); // Not calling |flutterPlatformViewsController::SubmitFrame| so that the platform views are not // added to flutter_view_. @@ -2626,7 +2595,6 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { auto embeddedViewParams1 = std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1)); - flutterPlatformViewsController->CompositeEmbeddedView(0); flutterPlatformViewsController->CompositeWithParams( 0, flutterPlatformViewsController->GetCompositionParams(0)); @@ -2639,7 +2607,6 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { auto embeddedViewParams2 = std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams2)); - flutterPlatformViewsController->CompositeEmbeddedView(0); flutterPlatformViewsController->CompositeWithParams( 0, flutterPlatformViewsController->GetCompositionParams(0)); @@ -2986,7 +2953,6 @@ - (void)testClipMaskViewIsReused { screenScaleMatrix, SkSize::Make(10, 10), stack1); flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1)); - flutterPlatformViewsController->CompositeEmbeddedView(1); flutterPlatformViewsController->CompositeWithParams( 1, flutterPlatformViewsController->GetCompositionParams(1)); @@ -3002,7 +2968,6 @@ - (void)testClipMaskViewIsReused { auto embeddedViewParams3 = std::make_unique( screenScaleMatrix, SkSize::Make(10, 10), stack2); flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams3)); - flutterPlatformViewsController->CompositeEmbeddedView(1); flutterPlatformViewsController->CompositeWithParams( 1, flutterPlatformViewsController->GetCompositionParams(1)); @@ -3018,7 +2983,6 @@ - (void)testClipMaskViewIsReused { auto embeddedViewParams4 = std::make_unique( screenScaleMatrix, SkSize::Make(10, 10), stack1); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams4)); - flutterPlatformViewsController->CompositeEmbeddedView(2); flutterPlatformViewsController->CompositeWithParams( 2, flutterPlatformViewsController->GetCompositionParams(2)); @@ -3095,14 +3059,12 @@ - (void)testDifferentClipMaskViewIsUsedForEachView { screenScaleMatrix, SkSize::Make(10, 10), stack2); flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1)); - flutterPlatformViewsController->CompositeEmbeddedView(1); flutterPlatformViewsController->CompositeWithParams( 1, flutterPlatformViewsController->GetCompositionParams(1)); UIView* childClippingView1 = view1.superview.superview; flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams2)); - flutterPlatformViewsController->CompositeEmbeddedView(2); flutterPlatformViewsController->CompositeWithParams( 2, flutterPlatformViewsController->GetCompositionParams(2)); @@ -3168,7 +3130,6 @@ - (void)testMaskViewUsesCAShapeLayerAsTheBackingLayer { screenScaleMatrix, SkSize::Make(10, 10), stack2); flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1)); - flutterPlatformViewsController->CompositeEmbeddedView(1); flutterPlatformViewsController->CompositeWithParams( 1, flutterPlatformViewsController->GetCompositionParams(1)); @@ -3367,7 +3328,6 @@ - (void)testOnlyPlatformViewsAreRemovedWhenReset { std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); - flutterPlatformViewsController->CompositeEmbeddedView(2); // SKSurface is required if the root FlutterView is present. const SkImageInfo image_info = SkImageInfo::MakeN32Premul(1000, 1000); diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index 35192be0f50c3..e87d1cbb15cfb 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -250,6 +250,7 @@ class FlutterPlatformViewsController { // Also reverts the composition_order_ to its original state at the beginning of the frame. void CancelFrame(); + // Runs on the raster thread. void PrerollCompositeEmbeddedView(int64_t view_id, std::unique_ptr params); @@ -271,17 +272,27 @@ class FlutterPlatformViewsController { // returns nil. FlutterTouchInterceptingView* GetFlutterTouchInterceptingViewByID(int64_t view_id); + // Runs on the raster thread. PostPrerollResult PostPrerollAction( const fml::RefPtr& raster_thread_merger); + // Runs on the raster thread. void EndFrame(bool should_resubmit_frame, const fml::RefPtr& raster_thread_merger); + // Return the Canvas for the overlay slice for the given platform view. + // + // Runs on the raster thread. DlCanvas* CompositeEmbeddedView(int64_t view_id); // Discards all platform views instances and auxiliary resources. + // + // Runs on the raster thread. void Reset(); + // Encode rendering for the Flutter overlay views and queue up perform platform view mutations. + // + // Runs on the raster thread. bool SubmitFrame(GrDirectContext* gr_context, const std::shared_ptr& ios_context, std::unique_ptr frame); @@ -317,7 +328,9 @@ class FlutterPlatformViewsController { using LayersMap = std::unordered_map; - /// Update the buffers and mutate the platform views in CATransaction on the platform thread. + // Update the buffers and mutate the platform views in CATransaction. + // + // Runs on the platform thread. void PerformSubmit(const LayersMap& platform_view_layers, std::unordered_map& current_composition_params, const std::unordered_set& views_to_recomposite, @@ -343,9 +356,6 @@ class FlutterPlatformViewsController { /// @brief Return all views to be disposed on the platform thread. std::vector GetViewsToDispose(); - // Traverse the `mutators_stack` and return the number of clip operations. - int CountClips(const MutatorsStack& mutators_stack); - void ClipViewSetMaskView(UIView* clipView) __attribute__((cf_audited_transfer)); // Applies the mutators in the mutators_stack to the UIView chain that was constructed by @@ -363,6 +373,7 @@ class FlutterPlatformViewsController { std::shared_ptr GetExistingLayer(); + // Runs on the platform thread. void CreateLayer(GrDirectContext* gr_context, const std::shared_ptr& ios_context, MTLPixelFormat pixel_format); @@ -407,22 +418,19 @@ class FlutterPlatformViewsController { /// The task runner for posting tasks to the platform thread. fml::RefPtr platform_task_runner_; - /// Each of the following maps stores part of the platform view hierarchy according to its + /// Each of the following structs stores part of the platform view hierarchy according to its /// ID. /// - /// The views_ map contains the platform view itself. - /// The touch_interceptors_ map contains the touch intercepting views to accept gestures. - /// Finally, the root views contains either the touch_interceptor or the child clipping - /// view. - /// - /// These maps must only be accessed on the platform thread. + /// This data must only be accessed on the platform thread. struct PlatformViewData { fml::scoped_nsobject> view; fml::scoped_nsobject touch_interceptor; fml::scoped_nsobject root_view; }; + /// This data must only be accessed on the platform thread. std::unordered_map platform_views_; + /// The composition parameters for each platform view. /// /// This state is only modified on the raster thread.