Reland: iOS: Migrate PlatformViewsController to Objective-C (flutter/engine#56828)

This migrates PlatformViewController from C++ to Objective-C. Generally, we try to keep the embedder interfaces and components written in Objective-C except for the few places where C++ interfaces are requried to interface with engine APIs such as Shell and PlatformView (e.g. the PlatformViewIOS subclass). Now that the implementation is Objective-C, the class and file are renamed to match Objective-C naming conventions.

This allows us to take advantage of ARC and weak references, which eliminates the need for std::shared_ptr, fml::WeakPtr etc. Further, this eliminates some particularly unintuitive behaviour wherein this class was owned via a std::shared_ptr held by FlutterEngine, and injected into many other classes (e.g. AccessibilityBridge) via a std::shared_ptr& reference -- such that only one instance of the std::shared_ptr actually ever existed, presumably to avoid std::shared_ptr refcounting overhead. Given that this overhead was only incurred a single time at engine initialisation, this seems like overkill. One might ask why it wasn't therefore held in a `std::unique_ptr` and a `std::unique_ptr&` reference passed around. Likely, this was because we wanted to take a `fml::WeakPtr` reference on it.

Regardless, none of this is necessary any longer now that we can inject `__weak FlutterPlatformViewsController*` instances to classes that use it.

To be clear, this patch makes no attempt whatsoever to simplify or clean up the interface or implementation of this class. This class ties together far too many concepts and is injected into far too many places, and we should break it up and simplify it. However, the goal of this patch was simply to port to an Objective-C interface that plays nicely with the rest of the iOS embedder. This does include a couple minor cleanups in `#include`/`#import` order and usage to match our style guide.

This is a reland with a one-line fix of a lambda-capture block to ensure `self` and any local variables are captured by value rather than by reference:
* In the case where this method is called on the platform thread (i.e. where the UI and platform thread are merged), we use the latch to pause the calling thread until the lambda completes, in which case all locals could be passed by reference since the locals are guaranteed to hang around until the lambda completes and signals the latch.
* In the case where this method is called from the UI thread (i.e. where UI and platform thread are not merged), locals may have gone out of scope by the time the lambda executes, leading to undefined behaviour if passed by reference; thus we always pass by value to be sure; since `latch` must be shared between threads, it's passed held in a `std::shared_ptr` so the underlying latch/mutex is shared but it's kept live until it goes out of scope in both threads.

[C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style

[C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
This commit is contained in:
Chris Bracken 2024-11-26 20:11:25 -08:00 committed by GitHub
parent 90320619b8
commit 2afb790cad
24 changed files with 2293 additions and 1973 deletions

View File

@ -44571,6 +44571,8 @@ ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatf
ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.mm + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformPluginTest.mm + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsController.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsController.mm + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPluginAppLifeCycleDelegate.mm + ../../../flutter/LICENSE
@ -44632,8 +44634,6 @@ ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/overlay_laye
ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/overlay_layer_pool.mm + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.mm + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/platform_views_controller.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/platform_views_controller.mm + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/profiler_metrics_ios.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/profiler_metrics_ios.mm + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h + ../../../flutter/LICENSE
@ -47514,6 +47514,8 @@ FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatfor
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.mm
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformPluginTest.mm
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsController.h
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsController.mm
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPluginAppLifeCycleDelegate.mm
@ -47575,8 +47577,6 @@ FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/overlay_layer_
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/overlay_layer_pool.mm
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.h
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.mm
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/platform_views_controller.h
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/platform_views_controller.mm
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/profiler_metrics_ios.h
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/profiler_metrics_ios.mm
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h

View File

@ -80,6 +80,8 @@ source_set("flutter_framework_source") {
"framework/Source/FlutterPlatformPlugin.h",
"framework/Source/FlutterPlatformPlugin.mm",
"framework/Source/FlutterPlatformViews.mm",
"framework/Source/FlutterPlatformViewsController.h",
"framework/Source/FlutterPlatformViewsController.mm",
"framework/Source/FlutterPlatformViews_Internal.h",
"framework/Source/FlutterPluginAppLifeCycleDelegate.mm",
"framework/Source/FlutterRestorationPlugin.h",
@ -120,8 +122,6 @@ source_set("flutter_framework_source") {
"framework/Source/overlay_layer_pool.mm",
"framework/Source/platform_message_response_darwin.h",
"framework/Source/platform_message_response_darwin.mm",
"framework/Source/platform_views_controller.h",
"framework/Source/platform_views_controller.mm",
"framework/Source/profiler_metrics_ios.h",
"framework/Source/profiler_metrics_ios.mm",
"framework/Source/vsync_waiter_ios.h",

View File

@ -105,6 +105,8 @@ static constexpr int kNumProfilerSamplesPerSec = 5;
@property(nonatomic, readonly, assign) BOOL allowHeadlessExecution;
@property(nonatomic, readonly, assign) BOOL restorationEnabled;
@property(nonatomic, strong) FlutterPlatformViewsController* platformViewsController;
// Maintains a dictionary of plugin names that have registered with the engine. Used by
// FlutterEngineRegistrar to implement a FlutterPluginRegistrar.
@property(nonatomic, readonly) NSMutableDictionary* pluginPublications;
@ -150,7 +152,6 @@ static constexpr int kNumProfilerSamplesPerSec = 5;
std::shared_ptr<flutter::ThreadHost> _threadHost;
std::unique_ptr<flutter::Shell> _shell;
std::shared_ptr<flutter::PlatformViewsController> _platformViewsController;
flutter::IOSRenderingAPI _renderingApi;
std::shared_ptr<flutter::SamplingProfiler> _profiler;
@ -211,7 +212,7 @@ static constexpr int kNumProfilerSamplesPerSec = 5;
_pluginPublications = [[NSMutableDictionary alloc] init];
_registrars = [[NSMutableDictionary alloc] init];
[self recreatePlatformViewController];
[self recreatePlatformViewsController];
_binaryMessenger = [[FlutterBinaryMessengerRelay alloc] initWithParent:self];
_textureRegistry = [[FlutterTextureRegistryRelay alloc] initWithParent:self];
_connections.reset(new flutter::ConnectionCollection());
@ -262,9 +263,9 @@ static constexpr int kNumProfilerSamplesPerSec = 5;
object:nil];
}
- (void)recreatePlatformViewController {
- (void)recreatePlatformViewsController {
_renderingApi = flutter::GetRenderingAPIForProcess(FlutterView.forceSoftwareRendering);
_platformViewsController.reset(new flutter::PlatformViewsController());
_platformViewsController = [[FlutterPlatformViewsController alloc] init];
}
- (flutter::IOSRenderingAPI)platformViewsRenderingAPI {
@ -452,11 +453,7 @@ static constexpr int kNumProfilerSamplesPerSec = 5;
_shell.reset();
_profiler.reset();
_threadHost.reset();
_platformViewsController.reset();
}
- (std::shared_ptr<flutter::PlatformViewsController>&)platformViewsController {
return _platformViewsController;
_platformViewsController = nil;
}
- (NSURL*)observatoryUrl {
@ -635,7 +632,7 @@ static constexpr int kNumProfilerSamplesPerSec = 5;
[self.platformViewsChannel
setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
if (weakSelf) {
weakSelf.platformViewsController->OnMethodCall(call, result);
[weakSelf.platformViewsController onMethodCall:call result:result];
}
}];
@ -777,11 +774,11 @@ static void SetEntryPoint(flutter::Settings* settings, NSString* entrypoint, NSS
if (!strongSelf) {
return std::unique_ptr<flutter::PlatformViewIOS>();
}
[strongSelf recreatePlatformViewController];
strongSelf->_platformViewsController->SetTaskRunner(
shell.GetTaskRunners().GetPlatformTaskRunner());
[strongSelf recreatePlatformViewsController];
strongSelf.platformViewsController.taskRunner =
shell.GetTaskRunners().GetPlatformTaskRunner();
return std::make_unique<flutter::PlatformViewIOS>(
shell, strongSelf->_renderingApi, strongSelf->_platformViewsController,
shell, strongSelf->_renderingApi, strongSelf.platformViewsController,
shell.GetTaskRunners(), shell.GetConcurrentWorkerTaskRunner(),
shell.GetIsGpuDisabledSyncSwitch());
};
@ -1103,7 +1100,7 @@ static void SetEntryPoint(flutter::Settings* settings, NSString* entrypoint, NSS
// Have to check in the next run loop, because iOS requests the previous first responder to
// resign before requesting the next view to become first responder.
dispatch_async(dispatch_get_main_queue(), ^(void) {
long platform_view_id = self.platformViewsController->FindFirstResponderPlatformViewId();
long platform_view_id = [self.platformViewsController firstResponderPlatformViewId];
if (platform_view_id == -1) {
return;
}
@ -1400,11 +1397,10 @@ static void SetEntryPoint(flutter::Settings* settings, NSString* entrypoint, NSS
// create call is synchronous.
flutter::Shell::CreateCallback<flutter::PlatformView> on_create_platform_view =
[result, context](flutter::Shell& shell) {
[result recreatePlatformViewController];
result->_platformViewsController->SetTaskRunner(
shell.GetTaskRunners().GetPlatformTaskRunner());
[result recreatePlatformViewsController];
result.platformViewsController.taskRunner = shell.GetTaskRunners().GetPlatformTaskRunner();
return std::make_unique<flutter::PlatformViewIOS>(
shell, context, result->_platformViewsController, shell.GetTaskRunners());
shell, context, result.platformViewsController, shell.GetTaskRunners());
};
flutter::Shell::CreateCallback<flutter::Rasterizer> on_create_rasterizer =
@ -1499,8 +1495,9 @@ static void SetEntryPoint(flutter::Settings* settings, NSString* entrypoint, NSS
withId:(NSString*)factoryId
gestureRecognizersBlockingPolicy:
(FlutterPlatformViewGestureRecognizersBlockingPolicy)gestureRecognizersBlockingPolicy {
[_flutterEngine platformViewsController]->RegisterViewFactory(factory, factoryId,
gestureRecognizersBlockingPolicy);
[_flutterEngine.platformViewsController registerViewFactory:factory
withId:factoryId
gestureRecognizersBlockingPolicy:gestureRecognizersBlockingPolicy];
}
@end

View File

@ -46,7 +46,6 @@ NS_ASSUME_NONNULL_BEGIN
base64Encode:(bool)base64Encode;
- (FlutterPlatformPlugin*)platformPlugin;
- (std::shared_ptr<flutter::PlatformViewsController>&)platformViewsController;
- (FlutterTextInputPlugin*)textInputPlugin;
- (FlutterRestorationPlugin*)restorationPlugin;
- (void)launchEngine:(nullable NSString*)entrypoint
@ -81,6 +80,7 @@ NS_ASSUME_NONNULL_BEGIN
userData:(nullable void*)userData;
@property(nonatomic, readonly) FlutterDartProject* project;
@end
NS_ASSUME_NONNULL_END

View File

@ -518,8 +518,7 @@ static BOOL _preparedOnce = NO;
@implementation FlutterTouchInterceptingView
- (instancetype)initWithEmbeddedView:(UIView*)embeddedView
platformViewsController:
(fml::WeakPtr<flutter::PlatformViewsController>)platformViewsController
platformViewsController:(FlutterPlatformViewsController*)platformViewsController
gestureRecognizersBlockingPolicy:
(FlutterPlatformViewGestureRecognizersBlockingPolicy)blockingPolicy {
self = [super initWithFrame:embeddedView.frame];
@ -667,7 +666,7 @@ static BOOL _preparedOnce = NO;
// outlives the FlutterViewController. And ForwardingGestureRecognizer is owned by a subview of
// FlutterView, so the ForwardingGestureRecognizer never out lives FlutterViewController.
// Therefore, `_platformViewsController` should never be nullptr.
fml::WeakPtr<flutter::PlatformViewsController> _platformViewsController;
__weak FlutterPlatformViewsController* _platformViewsController;
// Counting the pointers that has started in one touch sequence.
NSInteger _currentTouchPointersCount;
// We can't dispatch events to the framework without this back pointer.
@ -678,13 +677,12 @@ static BOOL _preparedOnce = NO;
}
- (instancetype)initWithTarget:(id)target
platformViewsController:
(fml::WeakPtr<flutter::PlatformViewsController>)platformViewsController {
platformViewsController:(FlutterPlatformViewsController*)platformViewsController {
self = [super initWithTarget:target action:nil];
if (self) {
self.delegate = self;
FML_DCHECK(platformViewsController.get() != nullptr);
_platformViewsController = std::move(platformViewsController);
FML_DCHECK(platformViewsController);
_platformViewsController = platformViewsController;
_currentTouchPointersCount = 0;
}
return self;
@ -692,7 +690,7 @@ static BOOL _preparedOnce = NO;
- (ForwardingGestureRecognizer*)recreateRecognizerWithTarget:(id)target {
return [[ForwardingGestureRecognizer alloc] initWithTarget:target
platformViewsController:std::move(_platformViewsController)];
platformViewsController:_platformViewsController];
}
- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
@ -701,7 +699,7 @@ static BOOL _preparedOnce = NO;
// At the start of each gesture sequence, we reset the `_flutterViewController`,
// so that all the touch events in the same sequence are forwarded to the same
// `_flutterViewController`.
_flutterViewController = _platformViewsController->GetFlutterViewController();
_flutterViewController = _platformViewsController.flutterViewController;
}
[_flutterViewController touchesBegan:touches withEvent:event];
_currentTouchPointersCount += touches.count;

View File

@ -0,0 +1,152 @@
// 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_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERPLATFORMVIEWSCONTROLLER_H_
#define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERPLATFORMVIEWSCONTROLLER_H_
#include <Metal/Metal.h>
#include <memory>
#include <unordered_map>
#include <unordered_set>
#include "flutter/flow/surface.h"
#include "flutter/fml/task_runner.h"
#include "flutter/fml/trace_event.h"
#include "impeller/base/thread_safety.h"
#include "third_party/skia/include/core/SkRect.h"
#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h"
#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlatformViews.h"
#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewResponder.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/overlay_layer_pool.h"
#import "flutter/shell/platform/darwin/ios/ios_context.h"
@class FlutterTouchInterceptingView;
@class FlutterClippingMaskViewPool;
@interface FlutterPlatformViewsController : NSObject
- (id)init NS_DESIGNATED_INITIALIZER;
/// The task runner used to post rendering tasks to the platform thread.
@property(nonatomic, assign) const fml::RefPtr<fml::TaskRunner>& taskRunner;
/// The flutter view.
@property(nonatomic, weak) UIView* flutterView;
/// @brief The flutter view controller.
@property(nonatomic, weak) UIViewController<FlutterViewResponder>* flutterViewController;
/// @brief Retrieve the view controller.
- (UIViewController<FlutterViewResponder>*)flutterViewController;
/// @brief set the factory used to construct embedded UI Views.
- (void)registerViewFactory:(NSObject<FlutterPlatformViewFactory>*)factory
withId:(NSString*)factoryId
gestureRecognizersBlockingPolicy:
(FlutterPlatformViewGestureRecognizersBlockingPolicy)gestureRecognizerBlockingPolicy;
/// @brief Mark the beginning of a frame and record the size of the onscreen.
- (void)beginFrameWithSize:(SkISize)frameSize;
/// @brief Cancel the current frame, indicating that no platform views are composited.
///
/// Additionally, reverts the composition order to its original state at the beginning of the
/// frame.
- (void)cancelFrame;
/// @brief Record a platform view in the layer tree to be rendered, along with the positioning and
/// mutator parameters.
///
/// Called from the raster thread.
- (void)prerollCompositeEmbeddedView:(int64_t)viewId
withParams:(std::unique_ptr<flutter::EmbeddedViewParams>)params;
/// @brief Returns the`FlutterTouchInterceptingView` with the provided view_id.
///
/// Returns nil if there is no platform view with the provided id. Called
/// from the platform thread.
- (FlutterTouchInterceptingView*)flutterTouchInterceptingViewForId:(int64_t)viewId;
/// @brief Determine if thread merging is required after prerolling platform views.
///
/// Called from the raster thread.
- (flutter::PostPrerollResult)postPrerollActionWithThreadMerger:
(const fml::RefPtr<fml::RasterThreadMerger>&)rasterThreadMerger
impellerEnabled:(BOOL)impellerEnabled;
/// @brief Mark the end of a compositor frame.
///
/// May determine changes are required to the thread merging state.
/// Called from the raster thread.
- (void)endFrameWithResubmit:(BOOL)shouldResubmitFrame
threadMerger:(const fml::RefPtr<fml::RasterThreadMerger>&)rasterThreadMerger
impellerEnabled:(BOOL)impellerEnabled;
/// @brief Returns the Canvas for the overlay slice for the given platform view.
///
/// Called from the raster thread.
- (flutter::DlCanvas*)compositeEmbeddedViewWithId:(int64_t)viewId;
/// @brief Discards all platform views instances and auxiliary resources.
///
/// Called from the raster thread.
- (void)reset;
/// @brief Encode rendering for the Flutter overlay views and queue up perform platform view
/// mutations.
///
/// Called from the raster thread.
- (BOOL)submitFrame:(std::unique_ptr<flutter::SurfaceFrame>)frame
withIosContext:(const std::shared_ptr<flutter::IOSContext>&)iosContext
grContext:(GrDirectContext*)grContext;
/// @brief Handler for platform view message channels.
- (void)onMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result;
/// @brief Returns the platform view id if the platform view (or any of its descendant view) is
/// the first responder.
///
/// Returns -1 if no such platform view is found.
- (long)firstResponderPlatformViewId;
/// @brief Pushes backdrop filter mutation to the mutator stack of each visited platform view.
- (void)pushFilterToVisitedPlatformViews:(const std::shared_ptr<flutter::DlImageFilter>&)filter
withRect:(const SkRect&)filterRect;
/// @brief Pushes the view id of a visted platform view to the list of visied platform views.
- (void)pushVisitedPlatformViewId:(int64_t)viewId;
@end
@interface FlutterPlatformViewsController (Testing)
- (size_t)embeddedViewCount;
// TODO(cbracken): Delete. This is unused.
- (size_t)layerPoolSize;
// Returns the `FlutterPlatformView`'s `view` object associated with the view_id.
//
// If the `PlatformViewsController` does not contain any `FlutterPlatformView` object or
// a `FlutterPlatformView` object associated with the view_id cannot be found, the method
// returns nil.
- (UIView*)platformViewForId:(int64_t)viewId;
// Composite the PlatformView with `viewId`.
//
// Every frame, during the paint traversal of the layer tree, this method is called for all
// the PlatformViews in `_viewsToRecomposite`.
//
// Note that `_viewsToRecomposite` does not represent all the views in the view hierarchy,
// if a PlatformView does not change its composition parameter from last frame, it is not
// included in the `views_to_recomposite_`.
- (void)compositeView:(int64_t)viewId withParams:(const flutter::EmbeddedViewParams&)params;
- (const flutter::EmbeddedViewParams&)compositionParamsForView:(int64_t)viewId;
@end
#endif // FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERPLATFORMVIEWSCONTROLLER_H_

View File

@ -6,20 +6,20 @@
#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"
#include <Metal/Metal.h>
#include "flutter/flow/surface.h"
#include "flutter/fml/memory/weak_ptr.h"
#include "flutter/fml/task_runner.h"
#include "flutter/fml/trace_event.h"
#include "flutter/impeller/base/thread_safety.h"
#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h"
#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsController.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewResponder.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/platform_views_controller.h"
#import "flutter/shell/platform/darwin/ios/ios_context.h"
#include "flutter/shell/platform/darwin/ios/ios_context.h"
#include "third_party/skia/include/core/SkRect.h"
// A UIView that acts as a clipping mask for the |ChildClippingView|.
//
@ -134,8 +134,7 @@
// 2. Dispatching all events that are hittested to the embedded view to the FlutterView.
@interface FlutterTouchInterceptingView : UIView
- (instancetype)initWithEmbeddedView:(UIView*)embeddedView
platformViewsController:
(fml::WeakPtr<flutter::PlatformViewsController>)platformViewsController
platformViewsController:(FlutterPlatformViewsController*)platformViewsController
gestureRecognizersBlockingPolicy:
(FlutterPlatformViewGestureRecognizersBlockingPolicy)blockingPolicy;
@ -192,8 +191,7 @@
// directly to the FlutterView.
@interface ForwardingGestureRecognizer : UIGestureRecognizer <UIGestureRecognizerDelegate>
- (instancetype)initWithTarget:(id)target
platformViewsController:
(fml::WeakPtr<flutter::PlatformViewsController>)platformViewsController;
platformViewsController:(FlutterPlatformViewsController*)platformViewsController;
- (ForwardingGestureRecognizer*)recreateRecognizerWithTarget:(id)target;
@end

View File

@ -14,12 +14,11 @@
@protocol FlutterViewEngineDelegate <NSObject>
@property(nonatomic, readonly) BOOL isUsingImpeller;
@property(nonatomic, readonly) FlutterPlatformViewsController* platformViewsController;
- (flutter::Rasterizer::Screenshot)takeScreenshot:(flutter::Rasterizer::ScreenshotType)type
asBase64Encoded:(BOOL)base64Encode;
- (std::shared_ptr<flutter::PlatformViewsController>&)platformViewsController;
/**
* A callback that is called when iOS queries accessibility information of the Flutter view.
*

View File

@ -745,14 +745,14 @@ static void SendFakeTouchEvent(UIScreen* screen,
// thread.
if (appeared) {
[self installFirstFrameCallback];
[self.engine platformViewsController]->SetFlutterView(self.flutterView);
[self.engine platformViewsController]->SetFlutterViewController(self);
self.platformViewsController.flutterView = self.flutterView;
self.platformViewsController.flutterViewController = self;
[self.engine iosPlatformView]->NotifyCreated();
} else {
self.displayingFlutterUI = NO;
[self.engine iosPlatformView]->NotifyDestroyed();
[self.engine platformViewsController]->SetFlutterView(nullptr);
[self.engine platformViewsController]->SetFlutterViewController(nullptr);
self.platformViewsController.flutterView = nil;
self.platformViewsController.flutterViewController = nil;
}
}
@ -2321,7 +2321,7 @@ static flutter::PointerData::DeviceKind DeviceKindFromTouchType(UITouch* touch)
#pragma mark - Platform views
- (std::shared_ptr<flutter::PlatformViewsController>&)platformViewsController {
- (FlutterPlatformViewsController*)platformViewsController {
return self.engine.platformViewsController;
}

View File

@ -5,11 +5,12 @@
#ifndef FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERVIEWCONTROLLER_INTERNAL_H_
#define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERVIEWCONTROLLER_INTERNAL_H_
#include "flutter/fml/time/time_point.h"
#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h"
#include "flutter/fml/time/time_point.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterKeySecondaryResponder.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterKeyboardManager.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsController.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterRestorationPlugin.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterUIPressProxy.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewResponder.h"
@ -56,7 +57,8 @@ typedef void (^FlutterKeyboardAnimationCallback)(fml::TimePoint);
*/
@property(nonatomic, assign, readwrite) BOOL prefersStatusBarHidden;
- (std::shared_ptr<flutter::PlatformViewsController>&)platformViewsController;
@property(nonatomic, readonly) FlutterPlatformViewsController* platformViewsController;
- (FlutterRestorationPlugin*)restorationPlugin;
// Accepts keypress events, and then calls |nextAction| if the event was not

View File

@ -13,13 +13,12 @@ FLUTTER_ASSERT_ARC
@property(nonatomic, assign) BOOL isUsingImpeller;
@end
@implementation FakeDelegate {
std::shared_ptr<flutter::PlatformViewsController> _platformViewsController;
}
@implementation FakeDelegate
@synthesize platformViewsController = _platformViewsController;
- (instancetype)init {
_callbackCalled = NO;
_platformViewsController = std::shared_ptr<flutter::PlatformViewsController>(nullptr);
return self;
}
@ -28,10 +27,6 @@ FLUTTER_ASSERT_ARC
return {};
}
- (std::shared_ptr<flutter::PlatformViewsController>&)platformViewsController {
return _platformViewsController;
}
- (void)flutterViewAccessibilityDidCall {
_callbackCalled = YES;
}

View File

@ -43,9 +43,7 @@ class MockAccessibilityBridge : public AccessibilityBridgeIos {
}
void AccessibilityObjectDidBecomeFocused(int32_t id) override {}
void AccessibilityObjectDidLoseFocus(int32_t id) override {}
std::shared_ptr<PlatformViewsController> GetPlatformViewsController() const override {
return nil;
}
FlutterPlatformViewsController* GetPlatformViewsController() const override { return nil; }
std::vector<SemanticsActionObservation> observations;
bool isVoiceOverRunningValue;
@ -74,9 +72,7 @@ class MockAccessibilityBridgeNoWindow : public AccessibilityBridgeIos {
}
void AccessibilityObjectDidBecomeFocused(int32_t id) override {}
void AccessibilityObjectDidLoseFocus(int32_t id) override {}
std::shared_ptr<PlatformViewsController> GetPlatformViewsController() const override {
return nil;
}
FlutterPlatformViewsController* GetPlatformViewsController() const override { return nil; }
std::vector<SemanticsActionObservation> observations;
bool isVoiceOverRunningValue;

View File

@ -50,7 +50,7 @@ class AccessibilityBridge final : public AccessibilityBridgeIos {
AccessibilityBridge(FlutterViewController* view_controller,
PlatformViewIOS* platform_view,
std::shared_ptr<PlatformViewsController> platform_views_controller,
__weak FlutterPlatformViewsController* platform_views_controller,
std::unique_ptr<IosDelegate> ios_delegate = nullptr);
~AccessibilityBridge();
@ -72,7 +72,7 @@ class AccessibilityBridge final : public AccessibilityBridgeIos {
fml::WeakPtr<AccessibilityBridge> GetWeakPtr();
std::shared_ptr<PlatformViewsController> GetPlatformViewsController() const override {
FlutterPlatformViewsController* GetPlatformViewsController() const override {
return platform_views_controller_;
};
@ -91,7 +91,7 @@ class AccessibilityBridge final : public AccessibilityBridgeIos {
FlutterViewController* view_controller_;
PlatformViewIOS* platform_view_;
const std::shared_ptr<PlatformViewsController> platform_views_controller_;
__weak FlutterPlatformViewsController* platform_views_controller_;
// If the this id is kSemanticObjectIdInvalid, it means either nothing has
// been focused or the focus is currently outside of the flutter application
// (i.e. the status bar or keyboard)

View File

@ -42,11 +42,11 @@ class DefaultIosDelegate : public AccessibilityBridge::IosDelegate {
AccessibilityBridge::AccessibilityBridge(
FlutterViewController* view_controller,
PlatformViewIOS* platform_view,
std::shared_ptr<PlatformViewsController> platform_views_controller,
__weak FlutterPlatformViewsController* platform_views_controller,
std::unique_ptr<IosDelegate> ios_delegate)
: view_controller_(view_controller),
platform_view_(platform_view),
platform_views_controller_(std::move(platform_views_controller)),
platform_views_controller_(platform_views_controller),
last_focused_semantics_object_id_(kSemanticObjectIdInvalid),
objects_([[NSMutableDictionary alloc] init]),
previous_routes_({}),
@ -271,11 +271,13 @@ static SemanticsObject* CreateObject(const flutter::SemanticsNode& node,
} else if (node.HasFlag(flutter::SemanticsFlags::kHasImplicitScrolling)) {
return [[FlutterScrollableSemanticsObject alloc] initWithBridge:weak_ptr uid:node.id];
} else if (node.IsPlatformViewNode()) {
return [[FlutterPlatformViewSemanticsContainer alloc]
initWithBridge:weak_ptr
uid:node.id
platformView:weak_ptr->GetPlatformViewsController()->GetFlutterTouchInterceptingViewByID(
node.platformViewId)];
FlutterPlatformViewsController* platformViewsController =
weak_ptr->GetPlatformViewsController();
FlutterTouchInterceptingView* touchInterceptingView =
[platformViewsController flutterTouchInterceptingViewForId:node.platformViewId];
return [[FlutterPlatformViewSemanticsContainer alloc] initWithBridge:weak_ptr
uid:node.id
platformView:touchInterceptingView];
} else {
return [[FlutterSemanticsObject alloc] initWithBridge:weak_ptr uid:node.id];
}

View File

@ -12,9 +12,9 @@
#include "flutter/lib/ui/semantics/semantics_node.h"
@class UIView;
@class FlutterPlatformViewsController;
namespace flutter {
class PlatformViewsController;
/// Interface that represents an accessibility bridge for iOS.
class AccessibilityBridgeIos {
@ -39,7 +39,7 @@ class AccessibilityBridgeIos {
* The input id is the uid of the newly focused SemanticObject.
*/
virtual void AccessibilityObjectDidLoseFocus(int32_t id) = 0;
virtual std::shared_ptr<PlatformViewsController> GetPlatformViewsController() const = 0;
virtual FlutterPlatformViewsController* GetPlatformViewsController() const = 0;
};
} // namespace flutter

View File

@ -279,8 +279,9 @@ fml::RefPtr<fml::TaskRunner> CreateNewThread(const std::string& name) {
/*ui=*/thread_task_runner,
/*io=*/thread_task_runner);
auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
flutterPlatformViewsController->SetTaskRunner(thread_task_runner);
FlutterPlatformViewsController* flutterPlatformViewsController =
[[FlutterPlatformViewsController alloc] init];
flutterPlatformViewsController.taskRunner = thread_task_runner;
auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
/*delegate=*/mock_delegate,
/*rendering_api=*/mock_delegate.settings_.enable_impeller
@ -294,19 +295,22 @@ fml::RefPtr<fml::TaskRunner> CreateNewThread(const std::string& name) {
id mockFlutterViewController = OCMClassMock([FlutterViewController class]);
OCMStub([mockFlutterViewController view]).andReturn(mockFlutterView);
std::string label = "some label";
flutterPlatformViewsController->SetFlutterView(mockFlutterView);
flutterPlatformViewsController.flutterView = mockFlutterView;
MockFlutterPlatformFactory* factory = [[MockFlutterPlatformFactory alloc] init];
flutterPlatformViewsController->RegisterViewFactory(
factory, @"MockFlutterPlatformView",
FlutterPlatformViewGestureRecognizersBlockingPolicyEager);
[flutterPlatformViewsController
registerViewFactory:factory
withId:@"MockFlutterPlatformView"
gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
FlutterResult result = ^(id result) {
};
flutterPlatformViewsController->OnMethodCall(
[FlutterMethodCall
methodCallWithMethodName:@"create"
arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
result);
[flutterPlatformViewsController
onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
arguments:@{
@"id" : @2,
@"viewType" : @"MockFlutterPlatformView"
}]
result:result];
auto bridge = std::make_unique<flutter::AccessibilityBridge>(
/*view_controller=*/mockFlutterViewController,
@ -322,7 +326,7 @@ fml::RefPtr<fml::TaskRunner> CreateNewThread(const std::string& name) {
flutter::CustomAccessibilityActionUpdates actions;
bridge->UpdateSemantics(/*nodes=*/nodes, /*actions=*/actions);
XCTAssertNotNil(gMockPlatformView);
flutterPlatformViewsController->Reset();
[flutterPlatformViewsController reset];
}
XCTAssertNil(gMockPlatformView);
}
@ -340,8 +344,9 @@ fml::RefPtr<fml::TaskRunner> CreateNewThread(const std::string& name) {
/*ui=*/thread_task_runner,
/*io=*/thread_task_runner);
auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
flutterPlatformViewsController->SetTaskRunner(thread_task_runner);
FlutterPlatformViewsController* flutterPlatformViewsController =
[[FlutterPlatformViewsController alloc] init];
flutterPlatformViewsController.taskRunner = thread_task_runner;
auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
/*delegate=*/mock_delegate,
/*rendering_api=*/mock_delegate.settings_.enable_impeller
@ -353,16 +358,19 @@ fml::RefPtr<fml::TaskRunner> CreateNewThread(const std::string& name) {
/*is_gpu_disabled_sync_switch=*/std::make_shared<fml::SyncSwitch>());
MockFlutterPlatformFactory* factory = [[MockFlutterPlatformFactory alloc] init];
flutterPlatformViewsController->RegisterViewFactory(
factory, @"MockFlutterPlatformView",
FlutterPlatformViewGestureRecognizersBlockingPolicyEager);
[flutterPlatformViewsController
registerViewFactory:factory
withId:@"MockFlutterPlatformView"
gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
FlutterResult result = ^(id result) {
};
flutterPlatformViewsController->OnMethodCall(
[FlutterMethodCall
methodCallWithMethodName:@"create"
arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
result);
[flutterPlatformViewsController
onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
arguments:@{
@"id" : @2,
@"viewType" : @"MockFlutterPlatformView"
}]
result:result];
auto bridge = std::make_unique<flutter::AccessibilityBridge>(
/*view_controller=*/flutterViewController,
@ -370,7 +378,7 @@ fml::RefPtr<fml::TaskRunner> CreateNewThread(const std::string& name) {
/*platform_views_controller=*/flutterPlatformViewsController);
XCTAssertNotNil(gMockPlatformView);
flutterPlatformViewsController->Reset();
[flutterPlatformViewsController reset];
platform_view->NotifyDestroyed();
}
XCTAssertNil(gMockPlatformView);
@ -387,8 +395,9 @@ fml::RefPtr<fml::TaskRunner> CreateNewThread(const std::string& name) {
/*ui=*/thread_task_runner,
/*io=*/thread_task_runner);
auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
flutterPlatformViewsController->SetTaskRunner(thread_task_runner);
FlutterPlatformViewsController* flutterPlatformViewsController =
[[FlutterPlatformViewsController alloc] init];
flutterPlatformViewsController.taskRunner = thread_task_runner;
auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
/*delegate=*/mock_delegate,
/*rendering_api=*/mock_delegate.settings_.enable_impeller
@ -484,8 +493,9 @@ fml::RefPtr<fml::TaskRunner> CreateNewThread(const std::string& name) {
/*ui=*/thread_task_runner,
/*io=*/thread_task_runner);
auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
flutterPlatformViewsController->SetTaskRunner(thread_task_runner);
FlutterPlatformViewsController* flutterPlatformViewsController =
[[FlutterPlatformViewsController alloc] init];
flutterPlatformViewsController.taskRunner = thread_task_runner;
auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
/*delegate=*/mock_delegate,
/*rendering_api=*/mock_delegate.settings_.enable_impeller
@ -559,8 +569,8 @@ fml::RefPtr<fml::TaskRunner> CreateNewThread(const std::string& name) {
/*ui=*/thread_task_runner,
/*io=*/thread_task_runner);
auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
flutterPlatformViewsController->SetTaskRunner(thread_task_runner);
FlutterPlatformViewsController* flutterPlatformViewsController =
[[FlutterPlatformViewsController alloc] init];
auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
/*delegate=*/mock_delegate,
/*rendering_api=*/mock_delegate.settings_.enable_impeller
@ -2175,11 +2185,12 @@ fml::RefPtr<fml::TaskRunner> CreateNewThread(const std::string& name) {
/*is_gpu_disabled_sync_switch=*/std::make_shared<fml::SyncSwitch>());
id mockFlutterViewController = OCMClassMock([FlutterViewController class]);
auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
flutterPlatformViewsController->SetTaskRunner(thread_task_runner);
FlutterPlatformViewsController* flutterPlatformViewsController =
[[FlutterPlatformViewsController alloc] init];
flutterPlatformViewsController.taskRunner = thread_task_runner;
OCMStub([mockFlutterViewController platformViewsController])
.andReturn(flutterPlatformViewsController.get());
.andReturn(flutterPlatformViewsController);
platform_view->SetOwnerViewController(mockFlutterViewController);
platform_view->SetSemanticsEnabled(true);

View File

@ -1,318 +0,0 @@
// 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_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_PLATFORM_VIEWS_CONTROLLER_H_
#define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_PLATFORM_VIEWS_CONTROLLER_H_
#include <Metal/Metal.h>
#include <unordered_map>
#include <unordered_set>
#include "flutter/flow/surface.h"
#include "flutter/fml/memory/weak_ptr.h"
#include "flutter/fml/task_runner.h"
#include "flutter/fml/trace_event.h"
#include "impeller/base/thread_safety.h"
#include "third_party/skia/include/core/SkRect.h"
#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h"
#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlatformViews.h"
#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewResponder.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/overlay_layer_pool.h"
#import "flutter/shell/platform/darwin/ios/ios_context.h"
@class FlutterTouchInterceptingView;
@class FlutterClippingMaskViewPool;
namespace flutter {
/// @brief Composites Flutter UI and overlay layers alongside embedded UIViews.
class PlatformViewsController {
public:
PlatformViewsController();
~PlatformViewsController() = default;
/// @brief Retrieve a weak pointer to this controller.
fml::WeakPtr<flutter::PlatformViewsController> GetWeakPtr();
/// @brief Set the platform task runner used to post rendering tasks.
void SetTaskRunner(const fml::RefPtr<fml::TaskRunner>& platform_task_runner);
/// @brief Set the flutter view.
void SetFlutterView(UIView* flutter_view) __attribute__((cf_audited_transfer));
/// @brief Set the flutter view controller.
void SetFlutterViewController(UIViewController<FlutterViewResponder>* flutter_view_controller)
__attribute__((cf_audited_transfer));
/// @brief Retrieve the view controller.
UIViewController<FlutterViewResponder>* GetFlutterViewController()
__attribute__((cf_audited_transfer));
/// @brief set the factory used to construct embedded UI Views.
void RegisterViewFactory(
NSObject<FlutterPlatformViewFactory>* factory,
NSString* factoryId,
FlutterPlatformViewGestureRecognizersBlockingPolicy gestureRecognizerBlockingPolicy)
__attribute__((cf_audited_transfer));
/// @brief Mark the beginning of a frame and record the size of the onscreen.
void BeginFrame(SkISize frame_size);
/// @brief Cancel the current frame, indicating that no platform views are composited.
///
/// Additionally, reverts the composition order to its original state at the beginning of the
/// frame.
void CancelFrame();
/// @brief Record a platform view in the layer tree to be rendered, along with the positioning and
/// mutator parameters.
///
/// Called from the raster thread.
void PrerollCompositeEmbeddedView(int64_t view_id,
std::unique_ptr<flutter::EmbeddedViewParams> params);
/// @brief Returns the`FlutterTouchInterceptingView` with the provided view_id.
///
/// Returns nil if there is no platform view with the provided id. Called
/// from the platform thread.
FlutterTouchInterceptingView* GetFlutterTouchInterceptingViewByID(int64_t view_id);
/// @brief Determine if thread merging is required after prerolling platform views.
///
/// Called from the raster thread.
PostPrerollResult PostPrerollAction(
const fml::RefPtr<fml::RasterThreadMerger>& raster_thread_merger,
bool impeller_enabled);
/// @brief Mark the end of a compositor frame.
///
/// May determine changes are required to the thread merging state.
/// Called from the raster thread.
void EndFrame(bool should_resubmit_frame,
const fml::RefPtr<fml::RasterThreadMerger>& raster_thread_merger,
bool impeller_enabled);
/// @brief Returns the Canvas for the overlay slice for the given platform view.
///
/// Called from the raster thread.
DlCanvas* CompositeEmbeddedView(int64_t view_id);
/// @brief Discards all platform views instances and auxiliary resources.
///
/// Called from the raster thread.
void Reset();
/// @brief Encode rendering for the Flutter overlay views and queue up perform platform view
/// mutations.
///
/// Called from the raster thread.
bool SubmitFrame(GrDirectContext* gr_context,
const std::shared_ptr<IOSContext>& ios_context,
std::unique_ptr<SurfaceFrame> frame);
/// @brief Handler for platform view message channels.
void OnMethodCall(FlutterMethodCall* call, FlutterResult result)
__attribute__((cf_audited_transfer));
/// @brief Returns the platform view id if the platform view (or any of its descendant view) is
/// the first responder.
///
/// Returns -1 if no such platform view is found.
long FindFirstResponderPlatformViewId();
/// @brief Pushes backdrop filter mutation to the mutator stack of each visited platform view.
void PushFilterToVisitedPlatformViews(const std::shared_ptr<DlImageFilter>& filter,
const SkRect& filter_rect);
/// @brief 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.
size_t EmbeddedViewCount() const;
// visible for testing.
size_t LayerPoolSize() const;
// visible for testing.
// Returns the `FlutterPlatformView`'s `view` object associated with the view_id.
//
// If the `PlatformViewsController` does not contain any `FlutterPlatformView` object or
// a `FlutterPlatformView` object associated with the view_id cannot be found, the method
// returns nil.
UIView* GetPlatformViewByID(int64_t view_id);
// Visible for testing.
void CompositeWithParams(int64_t view_id, const EmbeddedViewParams& params);
// Visible for testing.
const EmbeddedViewParams& GetCompositionParams(int64_t view_id) const {
return current_composition_params_.find(view_id)->second;
}
private:
PlatformViewsController(const PlatformViewsController&) = delete;
PlatformViewsController& operator=(const PlatformViewsController&) = delete;
struct LayerData {
SkRect rect;
int64_t view_id;
int64_t overlay_id;
std::shared_ptr<OverlayLayer> layer;
};
using LayersMap = std::unordered_map<int64_t, LayerData>;
// 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<int64_t, EmbeddedViewParams>& current_composition_params,
const std::unordered_set<int64_t>& views_to_recomposite,
const std::vector<int64_t>& composition_order,
const std::vector<std::shared_ptr<OverlayLayer>>& unused_layers,
const std::vector<std::unique_ptr<SurfaceFrame>>& surface_frames);
/// @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<IOSContext>& 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));
void OnAcceptGesture(FlutterMethodCall* call, FlutterResult result)
__attribute__((cf_audited_transfer));
void OnRejectGesture(FlutterMethodCall* call, FlutterResult result)
__attribute__((cf_audited_transfer));
/// @brief Return all views to be disposed on the platform thread.
std::vector<UIView*> GetViewsToDispose();
void ClipViewSetMaskView(UIView* clipView) __attribute__((cf_audited_transfer));
// Applies the mutators in the mutators_stack to the UIView chain that was constructed by
// `ReconstructClipViewsChain`
//
// Clips are applied to the `embedded_view`'s super view(|ChildClippingView|) using a
// |FlutterClippingMaskView|. Transforms are applied to `embedded_view`
//
// The `bounding_rect` is the final bounding rect of the PlatformView
// (EmbeddedViewParams::finalBoundingRect). If a clip mutator's rect contains the final bounding
// rect of the PlatformView, the clip mutator is not applied for performance optimization.
void ApplyMutators(const MutatorsStack& mutators_stack,
UIView* embedded_view,
const SkRect& bounding_rect) __attribute__((cf_audited_transfer));
std::shared_ptr<OverlayLayer> GetExistingLayer();
// Runs on the platform thread.
void CreateLayer(GrDirectContext* gr_context,
const std::shared_ptr<IOSContext>& 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(const std::vector<std::shared_ptr<OverlayLayer>>& unused_layers,
const std::vector<int64_t>& composition_order);
// Appends the overlay views and platform view and sets their z index based on the composition
// order.
void BringLayersIntoView(const LayersMap& layer_map,
const std::vector<int64_t>& composition_order);
// Resets the state of the frame.
void ResetFrameState();
// The pool of reusable view layers. The pool allows to recycle layer in each frame.
std::unique_ptr<OverlayLayerPool> layer_pool_;
// The platform view's |EmbedderViewSlice| keyed off the view id, which contains any subsequent
// operation until the next platform view or the end of the last leaf node in the layer tree.
//
// The Slices are deleted by the PlatformViewsController.reset().
std::unordered_map<int64_t, std::unique_ptr<EmbedderViewSlice>> slices_;
UIView* flutter_view_;
UIViewController<FlutterViewResponder>* flutter_view_controller_;
FlutterClippingMaskViewPool* mask_view_pool_;
std::unordered_map<std::string, NSObject<FlutterPlatformViewFactory>*> factories_;
// The FlutterPlatformViewGestureRecognizersBlockingPolicy for each type of platform view.
std::unordered_map<std::string, FlutterPlatformViewGestureRecognizersBlockingPolicy>
gesture_recognizers_blocking_policies_;
/// The size of the current onscreen surface in physical pixels.
SkISize frame_size_;
/// The task runner for posting tasks to the platform thread.
fml::RefPtr<fml::TaskRunner> platform_task_runner_;
/// Each of the following structs stores part of the platform view hierarchy according to its
/// ID.
///
/// This data must only be accessed on the platform thread.
struct PlatformViewData {
NSObject<FlutterPlatformView>* view;
FlutterTouchInterceptingView* touch_interceptor;
UIView* root_view;
};
/// This data must only be accessed on the platform thread.
std::unordered_map<int64_t, PlatformViewData> platform_views_;
/// The composition parameters for each platform view.
///
/// This state is only modified on the raster thread.
std::unordered_map<int64_t, EmbeddedViewParams> 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<int64_t> views_to_dispose_;
/// view IDs in composition order.
///
/// This state is only modified on the raster thread.
std::vector<int64_t> composition_order_;
/// platform view IDs visited during layer tree composition.
///
/// This state is only modified on the raster thread.
std::vector<int64_t> visited_platform_views_;
/// Only composite platform views in this set.
///
/// This state is only modified on the raster thread.
std::unordered_set<int64_t> views_to_recomposite_;
/// @brief The composition order from the previous thread.
///
/// Only accessed from the platform thread.
std::vector<int64_t> previous_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;
// WeakPtrFactory must be the last member.
std::unique_ptr<fml::WeakPtrFactory<PlatformViewsController>> weak_factory_;
};
} // namespace flutter
#endif // FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_PLATFORM_VIEWS_CONTROLLER_H_

View File

@ -1,874 +0,0 @@
// 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 "shell/platform/darwin/ios/framework/Source/platform_views_controller.h"
#include "flutter/display_list/effects/image_filters/dl_blur_image_filter.h"
#include "flutter/flow/surface_frame.h"
#include "flutter/flow/view_slicer.h"
#include "flutter/fml/make_copyable.h"
#include "flutter/fml/synchronization/count_down_latch.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"
namespace {
// 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;
static constexpr NSUInteger kFlutterClippingMaskViewPoolCapacity = 5;
// Converts a SkMatrix to CATransform3D.
//
// Certain fields are ignored in CATransform3D since SkMatrix is 3x3 and CATransform3D is 4x4.
CATransform3D GetCATransform3DFromSkMatrix(const SkMatrix& matrix) {
// Skia only supports 2D transform so we don't map z.
CATransform3D transform = CATransform3DIdentity;
transform.m11 = matrix.getScaleX();
transform.m21 = matrix.getSkewX();
transform.m41 = matrix.getTranslateX();
transform.m14 = matrix.getPerspX();
transform.m12 = matrix.getSkewY();
transform.m22 = matrix.getScaleY();
transform.m42 = matrix.getTranslateY();
transform.m24 = matrix.getPerspY();
return transform;
}
// Reset the anchor of `layer` to match the transform operation from flow.
//
// The position of the `layer` should be unchanged after resetting the anchor.
void ResetAnchor(CALayer* layer) {
// Flow uses (0, 0) to apply transform matrix so we need to match that in Quartz.
layer.anchorPoint = CGPointZero;
layer.position = CGPointZero;
}
CGRect GetCGRectFromSkRect(const SkRect& clipSkRect) {
return CGRectMake(clipSkRect.fLeft, clipSkRect.fTop, clipSkRect.fRight - clipSkRect.fLeft,
clipSkRect.fBottom - clipSkRect.fTop);
}
// Determines if the `clip_rect` from a clipRect mutator contains the
// `platformview_boundingrect`.
//
// `clip_rect` is in its own coordinate space. The rect needs to be transformed by
// `transform_matrix` to be in the coordinate space where the PlatformView is displayed.
//
// `platformview_boundingrect` is the final bounding rect of the PlatformView in the coordinate
// space where the PlatformView is displayed.
bool ClipRectContainsPlatformViewBoundingRect(const SkRect& clip_rect,
const SkRect& platformview_boundingrect,
const SkMatrix& transform_matrix) {
SkRect transformed_rect = transform_matrix.mapRect(clip_rect);
return transformed_rect.contains(platformview_boundingrect);
}
// Determines if the `clipRRect` from a clipRRect mutator contains the
// `platformview_boundingrect`.
//
// `clip_rrect` is in its own coordinate space. The rrect needs to be transformed by
// `transform_matrix` to be in the coordinate space where the PlatformView is displayed.
//
// `platformview_boundingrect` is the final bounding rect of the PlatformView in the coordinate
// space where the PlatformView is displayed.
bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect,
const SkRect& platformview_boundingrect,
const SkMatrix& transform_matrix) {
SkVector upper_left = clip_rrect.radii(SkRRect::Corner::kUpperLeft_Corner);
SkVector upper_right = clip_rrect.radii(SkRRect::Corner::kUpperRight_Corner);
SkVector lower_right = clip_rrect.radii(SkRRect::Corner::kLowerRight_Corner);
SkVector lower_left = clip_rrect.radii(SkRRect::Corner::kLowerLeft_Corner);
SkScalar transformed_upper_left_x = transform_matrix.mapRadius(upper_left.x());
SkScalar transformed_upper_left_y = transform_matrix.mapRadius(upper_left.y());
SkScalar transformed_upper_right_x = transform_matrix.mapRadius(upper_right.x());
SkScalar transformed_upper_right_y = transform_matrix.mapRadius(upper_right.y());
SkScalar transformed_lower_right_x = transform_matrix.mapRadius(lower_right.x());
SkScalar transformed_lower_right_y = transform_matrix.mapRadius(lower_right.y());
SkScalar transformed_lower_left_x = transform_matrix.mapRadius(lower_left.x());
SkScalar transformed_lower_left_y = transform_matrix.mapRadius(lower_left.y());
SkRect transformed_clip_rect = transform_matrix.mapRect(clip_rrect.rect());
SkRRect transformed_rrect;
SkVector corners[] = {{transformed_upper_left_x, transformed_upper_left_y},
{transformed_upper_right_x, transformed_upper_right_y},
{transformed_lower_right_x, transformed_lower_right_y},
{transformed_lower_left_x, transformed_lower_left_y}};
transformed_rrect.setRectRadii(transformed_clip_rect, corners);
return transformed_rrect.contains(platformview_boundingrect);
}
} // namespace
namespace flutter {
// Becomes NO if Apple's API changes and blurred backdrop filters cannot be applied.
BOOL canApplyBlurBackdrop = YES;
PlatformViewsController::PlatformViewsController()
: layer_pool_(std::make_unique<OverlayLayerPool>()),
weak_factory_(std::make_unique<fml::WeakPtrFactory<PlatformViewsController>>(this)) {
mask_view_pool_ =
[[FlutterClippingMaskViewPool alloc] initWithCapacity:kFlutterClippingMaskViewPoolCapacity];
};
void PlatformViewsController::SetTaskRunner(
const fml::RefPtr<fml::TaskRunner>& platform_task_runner) {
platform_task_runner_ = platform_task_runner;
}
fml::WeakPtr<flutter::PlatformViewsController> PlatformViewsController::GetWeakPtr() {
return weak_factory_->GetWeakPtr();
}
void PlatformViewsController::SetFlutterView(UIView* flutter_view) {
flutter_view_ = flutter_view;
}
void PlatformViewsController::SetFlutterViewController(
UIViewController<FlutterViewResponder>* flutter_view_controller) {
flutter_view_controller_ = flutter_view_controller;
}
UIViewController<FlutterViewResponder>* PlatformViewsController::GetFlutterViewController() {
return flutter_view_controller_;
}
void PlatformViewsController::OnMethodCall(FlutterMethodCall* call, FlutterResult result) {
if ([[call method] isEqualToString:@"create"]) {
OnCreate(call, result);
} else if ([[call method] isEqualToString:@"dispose"]) {
OnDispose(call, result);
} else if ([[call method] isEqualToString:@"acceptGesture"]) {
OnAcceptGesture(call, result);
} else if ([[call method] isEqualToString:@"rejectGesture"]) {
OnRejectGesture(call, result);
} else {
result(FlutterMethodNotImplemented);
}
}
void PlatformViewsController::OnCreate(FlutterMethodCall* call, FlutterResult result) {
NSDictionary<NSString*, id>* args = [call arguments];
int64_t viewId = [args[@"id"] longLongValue];
NSString* viewTypeString = args[@"viewType"];
std::string viewType(viewTypeString.UTF8String);
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<FlutterPlatformViewFactory>* factory = factories_[viewType];
if (factory == nil) {
result([FlutterError
errorWithCode:@"unregistered_view_type"
message:[NSString stringWithFormat:@"A UIKitView widget is trying to create a "
@"PlatformView with an unregistered type: < %@ >",
viewTypeString]
details:@"If you are the author of the PlatformView, make sure `registerViewFactory` "
@"is invoked.\n"
@"See: "
@"https://docs.flutter.dev/development/platform-integration/"
@"platform-views#on-the-platform-side-1 for more details.\n"
@"If you are not the author of the PlatformView, make sure to call "
@"`GeneratedPluginRegistrant.register`."]);
return;
}
id params = nil;
if ([factory respondsToSelector:@selector(createArgsCodec)]) {
NSObject<FlutterMessageCodec>* codec = [factory createArgsCodec];
if (codec != nil && args[@"params"] != nil) {
FlutterStandardTypedData* paramsData = args[@"params"];
params = [codec decode:paramsData.data];
}
}
NSObject<FlutterPlatformView>* embedded_view = [factory createWithFrame:CGRectZero
viewIdentifier:viewId
arguments:params];
UIView* platform_view = [embedded_view view];
// Set a unique view identifier, so the platform view can be identified in unit tests.
platform_view.accessibilityIdentifier =
[NSString stringWithFormat:@"platform_view[%lld]", viewId];
FlutterTouchInterceptingView* touch_interceptor = [[FlutterTouchInterceptingView alloc]
initWithEmbeddedView:platform_view
platformViewsController:GetWeakPtr()
gestureRecognizersBlockingPolicy:gesture_recognizers_blocking_policies_[viewType]];
ChildClippingView* clipping_view = [[ChildClippingView alloc] initWithFrame:CGRectZero];
[clipping_view addSubview:touch_interceptor];
platform_views_.emplace(viewId, PlatformViewData{
.view = embedded_view, //
.touch_interceptor = touch_interceptor, //
.root_view = clipping_view //
});
result(nil);
}
void PlatformViewsController::OnDispose(FlutterMethodCall* call, FlutterResult result) {
NSNumber* arg = [call arguments];
int64_t viewId = [arg longLongValue];
if (platform_views_.count(viewId) == 0) {
result([FlutterError errorWithCode:@"unknown_view"
message:@"trying to dispose an unknown"
details:[NSString stringWithFormat:@"view id: '%lld'", viewId]]);
return;
}
// We wait for next submitFrame to dispose views.
views_to_dispose_.insert(viewId);
result(nil);
}
void PlatformViewsController::OnAcceptGesture(FlutterMethodCall* call, FlutterResult result) {
NSDictionary<NSString*, id>* args = [call arguments];
int64_t viewId = [args[@"id"] longLongValue];
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 = platform_views_[viewId].touch_interceptor;
[view releaseGesture];
result(nil);
}
void PlatformViewsController::OnRejectGesture(FlutterMethodCall* call, FlutterResult result) {
NSDictionary<NSString*, id>* args = [call arguments];
int64_t viewId = [args[@"id"] longLongValue];
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 = platform_views_[viewId].touch_interceptor;
[view blockGesture];
result(nil);
}
void PlatformViewsController::RegisterViewFactory(
NSObject<FlutterPlatformViewFactory>* factory,
NSString* factoryId,
FlutterPlatformViewGestureRecognizersBlockingPolicy gestureRecognizerBlockingPolicy) {
std::string idString([factoryId UTF8String]);
FML_CHECK(factories_.count(idString) == 0);
factories_[idString] = factory;
gesture_recognizers_blocking_policies_[idString] = gestureRecognizerBlockingPolicy;
}
void PlatformViewsController::BeginFrame(SkISize frame_size) {
ResetFrameState();
frame_size_ = frame_size;
}
void PlatformViewsController::CancelFrame() {
ResetFrameState();
}
PostPrerollResult PlatformViewsController::PostPrerollAction(
const fml::RefPtr<fml::RasterThreadMerger>& raster_thread_merger,
bool impeller_enabled) {
// TODO(jonahwilliams): remove this once Software backend is removed for iOS Sim.
#ifdef FML_OS_IOS_SIMULATOR
const bool merge_threads = true;
#else
const bool merge_threads = !impeller_enabled;
#endif // FML_OS_IOS_SIMULATOR
if (merge_threads) {
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;
}
void PlatformViewsController::EndFrame(
bool should_resubmit_frame,
const fml::RefPtr<fml::RasterThreadMerger>& raster_thread_merger,
bool impeller_enabled) {
#if FML_OS_IOS_SIMULATOR
bool run_check = true;
#else
bool run_check = !impeller_enabled;
#endif // FML_OS_IOS_SIMULATOR
if (run_check && should_resubmit_frame) {
raster_thread_merger->MergeWithLease(kDefaultMergedLeaseDuration);
}
}
void PlatformViewsController::PushFilterToVisitedPlatformViews(
const std::shared_ptr<DlImageFilter>& filter,
const SkRect& filter_rect) {
for (int64_t id : visited_platform_views_) {
EmbeddedViewParams params = current_composition_params_[id];
params.PushImageFilter(filter, filter_rect);
current_composition_params_[id] = params;
}
}
void PlatformViewsController::PrerollCompositeEmbeddedView(
int64_t view_id,
std::unique_ptr<EmbeddedViewParams> params) {
SkRect view_bounds = SkRect::Make(frame_size_);
std::unique_ptr<EmbedderViewSlice> view;
view = std::make_unique<DisplayListEmbedderViewSlice>(view_bounds);
slices_.insert_or_assign(view_id, std::move(view));
composition_order_.push_back(view_id);
if (current_composition_params_.count(view_id) == 1 &&
current_composition_params_[view_id] == *params.get()) {
// Do nothing if the params didn't change.
return;
}
current_composition_params_[view_id] = EmbeddedViewParams(*params.get());
views_to_recomposite_.insert(view_id);
}
size_t PlatformViewsController::EmbeddedViewCount() const {
return composition_order_.size();
}
size_t PlatformViewsController::LayerPoolSize() const {
return layer_pool_->size();
}
UIView* PlatformViewsController::GetPlatformViewByID(int64_t view_id) {
return [GetFlutterTouchInterceptingViewByID(view_id) embeddedView];
}
FlutterTouchInterceptingView* PlatformViewsController::GetFlutterTouchInterceptingViewByID(
int64_t view_id) {
if (platform_views_.empty()) {
return nil;
}
return platform_views_[view_id].touch_interceptor;
}
long PlatformViewsController::FindFirstResponderPlatformViewId() {
for (auto const& [id, platform_view_data] : platform_views_) {
UIView* root_view = platform_view_data.root_view;
if (root_view.flt_hasFirstResponderInViewHierarchySubtree) {
return id;
}
}
return -1;
}
void PlatformViewsController::ClipViewSetMaskView(UIView* clipView) {
FML_DCHECK([[NSThread currentThread] isMainThread]);
if (clipView.maskView) {
return;
}
CGRect frame =
CGRectMake(-clipView.frame.origin.x, -clipView.frame.origin.y,
CGRectGetWidth(flutter_view_.bounds), CGRectGetHeight(flutter_view_.bounds));
clipView.maskView = [mask_view_pool_ getMaskViewWithFrame:frame];
}
// This method is only called when the `embedded_view` needs to be re-composited at the current
// frame. See: `CompositeWithParams` for details.
void PlatformViewsController::ApplyMutators(const MutatorsStack& mutators_stack,
UIView* embedded_view,
const SkRect& bounding_rect) {
if (flutter_view_ == nullptr) {
return;
}
ResetAnchor(embedded_view.layer);
ChildClippingView* clipView = (ChildClippingView*)embedded_view.superview;
SkMatrix transformMatrix;
NSMutableArray* blurFilters = [[NSMutableArray alloc] init];
FML_DCHECK(!clipView.maskView ||
[clipView.maskView isKindOfClass:[FlutterClippingMaskView class]]);
if (clipView.maskView) {
[mask_view_pool_ insertViewToPoolIfNeeded:(FlutterClippingMaskView*)(clipView.maskView)];
clipView.maskView = nil;
}
CGFloat screenScale = [UIScreen mainScreen].scale;
auto iter = mutators_stack.Begin();
while (iter != mutators_stack.End()) {
switch ((*iter)->GetType()) {
case kTransform: {
transformMatrix.preConcat((*iter)->GetMatrix());
break;
}
case kClipRect: {
if (ClipRectContainsPlatformViewBoundingRect((*iter)->GetRect(), bounding_rect,
transformMatrix)) {
break;
}
ClipViewSetMaskView(clipView);
[(FlutterClippingMaskView*)clipView.maskView clipRect:(*iter)->GetRect()
matrix:transformMatrix];
break;
}
case kClipRRect: {
if (ClipRRectContainsPlatformViewBoundingRect((*iter)->GetRRect(), bounding_rect,
transformMatrix)) {
break;
}
ClipViewSetMaskView(clipView);
[(FlutterClippingMaskView*)clipView.maskView clipRRect:(*iter)->GetRRect()
matrix:transformMatrix];
break;
}
case kClipPath: {
// TODO(cyanglaz): Find a way to pre-determine if path contains the PlatformView boudning
// rect. See `ClipRRectContainsPlatformViewBoundingRect`.
// https://github.com/flutter/flutter/issues/118650
ClipViewSetMaskView(clipView);
[(FlutterClippingMaskView*)clipView.maskView clipPath:(*iter)->GetPath()
matrix:transformMatrix];
break;
}
case kOpacity:
embedded_view.alpha = (*iter)->GetAlphaFloat() * embedded_view.alpha;
break;
case kBackdropFilter: {
// Only support DlBlurImageFilter for BackdropFilter.
if (!canApplyBlurBackdrop || !(*iter)->GetFilterMutation().GetFilter().asBlur()) {
break;
}
CGRect filterRect = GetCGRectFromSkRect((*iter)->GetFilterMutation().GetFilterRect());
// `filterRect` is in global coordinates. We need to convert to local space.
filterRect = CGRectApplyAffineTransform(
filterRect, CGAffineTransformMakeScale(1 / screenScale, 1 / screenScale));
// `filterRect` reprents the rect that should be filtered inside the `flutter_view_`.
// The `PlatformViewFilter` needs the frame inside the `clipView` that needs to be
// filtered.
if (CGRectIsNull(CGRectIntersection(filterRect, clipView.frame))) {
break;
}
CGRect intersection = CGRectIntersection(filterRect, clipView.frame);
CGRect frameInClipView = [flutter_view_ convertRect:intersection toView:clipView];
// sigma_x is arbitrarily chosen as the radius value because Quartz sets
// sigma_x and sigma_y equal to each other. DlBlurImageFilter's Tile Mode
// is not supported in Quartz's gaussianBlur CAFilter, so it is not used
// to blur the PlatformView.
CGFloat blurRadius = (*iter)->GetFilterMutation().GetFilter().asBlur()->sigma_x();
UIVisualEffectView* visualEffectView = [[UIVisualEffectView alloc]
initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]];
PlatformViewFilter* filter = [[PlatformViewFilter alloc] initWithFrame:frameInClipView
blurRadius:blurRadius
visualEffectView:visualEffectView];
if (!filter) {
canApplyBlurBackdrop = NO;
} else {
[blurFilters addObject:filter];
}
break;
}
}
++iter;
}
if (canApplyBlurBackdrop) {
[clipView applyBlurBackdropFilters:blurFilters];
}
// The UIKit frame is set based on the logical resolution (points) instead of physical.
// (https://developer.apple.com/library/archive/documentation/DeviceInformation/Reference/iOSDeviceCompatibility/Displays/Displays.html).
// However, flow is based on the physical resolution. For example, 1000 pixels in flow equals
// 500 points in UIKit for devices that has screenScale of 2. We need to scale the transformMatrix
// down to the logical resoltion before applying it to the layer of PlatformView.
transformMatrix.postScale(1 / screenScale, 1 / screenScale);
// Reverse the offset of the clipView.
// The clipView's frame includes the final translate of the final transform matrix.
// Thus, this translate needs to be reversed so the platform view can layout at the correct
// offset.
//
// Note that the transforms are not applied to the clipping paths because clipping paths happen on
// the mask view, whose origin is always (0,0) to the flutter_view.
transformMatrix.postTranslate(-clipView.frame.origin.x, -clipView.frame.origin.y);
embedded_view.layer.transform = GetCATransform3DFromSkMatrix(transformMatrix);
}
// Composite the PlatformView with `view_id`.
//
// Every frame, during the paint traversal of the layer tree, this method is called for all
// the PlatformViews in `views_to_recomposite_`.
//
// Note that `views_to_recomposite_` does not represent all the views in the view hierarchy,
// if a PlatformView does not change its composition parameter from last frame, it is not
// included in the `views_to_recomposite_`.
void PlatformViewsController::CompositeWithParams(int64_t view_id,
const EmbeddedViewParams& params) {
/// TODO(https://github.com/flutter/flutter/issues/109700)
CGRect frame = CGRectMake(0, 0, params.sizePoints().width(), params.sizePoints().height());
FlutterTouchInterceptingView* touchInterceptor = platform_views_[view_id].touch_interceptor;
touchInterceptor.layer.transform = CATransform3DIdentity;
touchInterceptor.frame = frame;
touchInterceptor.alpha = 1;
const MutatorsStack& mutatorStack = params.mutatorsStack();
UIView* clippingView = platform_views_[view_id].root_view;
// 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
// to remember to do a reverse translate.
const SkRect& rect = params.finalBoundingRect();
CGFloat screenScale = [UIScreen mainScreen].scale;
clippingView.frame = CGRectMake(rect.x() / screenScale, rect.y() / screenScale,
rect.width() / screenScale, rect.height() / screenScale);
ApplyMutators(mutatorStack, touchInterceptor, rect);
}
DlCanvas* PlatformViewsController::CompositeEmbeddedView(int64_t view_id) {
return slices_[view_id]->canvas();
}
void PlatformViewsController::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_) {
[platform_views_[view_id].root_view removeFromSuperview];
}
platform_views_.clear();
});
composition_order_.clear();
slices_.clear();
current_composition_params_.clear();
views_to_recomposite_.clear();
layer_pool_->RecycleLayers();
visited_platform_views_.clear();
}
bool PlatformViewsController::SubmitFrame(GrDirectContext* gr_context,
const std::shared_ptr<IOSContext>& ios_context,
std::unique_ptr<SurfaceFrame> background_frame) {
TRACE_EVENT0("flutter", "PlatformViewsController::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();
}
had_platform_views_ = !composition_order_.empty();
bool did_encode = true;
LayersMap platform_view_layers;
std::vector<std::unique_ptr<SurfaceFrame>> surface_frames;
surface_frames.reserve(composition_order_.size());
std::unordered_map<int64_t, SkRect> view_rects;
for (int64_t view_id : composition_order_) {
view_rects[view_id] = current_composition_params_[view_id].finalBoundingRect();
}
std::unordered_map<int64_t, SkRect> overlay_layers =
SliceViews(background_frame->Canvas(), composition_order_, slices_, view_rects);
size_t required_overlay_layers = 0;
for (int64_t view_id : composition_order_) {
std::unordered_map<int64_t, SkRect>::const_iterator overlay = overlay_layers.find(view_id);
if (overlay == overlay_layers.end()) {
continue;
}
required_overlay_layers++;
}
// 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.
CreateMissingOverlays(gr_context, ios_context, required_overlay_layers);
int64_t overlay_id = 0;
for (int64_t view_id : composition_order_) {
std::unordered_map<int64_t, SkRect>::const_iterator overlay = overlay_layers.find(view_id);
if (overlay == overlay_layers.end()) {
continue;
}
std::shared_ptr<OverlayLayer> layer = GetExistingLayer();
if (!layer) {
continue;
}
std::unique_ptr<SurfaceFrame> 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(overlay->second);
overlay_canvas->Clear(DlColor::kTransparent());
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});
layer->did_submit_last_frame = frame->Encode();
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++;
}
auto previous_submit_info = background_frame->submit_info();
background_frame->set_submit_info({
.frame_damage = previous_submit_info.frame_damage,
.buffer_damage = previous_submit_info.buffer_damage,
.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<std::shared_ptr<OverlayLayer>> 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_, //
composition_order = composition_order_, //
unused_layers = std::move(unused_layers), //
surface_frames = std::move(surface_frames) //
]() mutable {
PerformSubmit(platform_view_layers, //
current_composition_params, //
views_to_recomposite, //
composition_order, //
unused_layers, //
surface_frames //
);
};
fml::TaskRunner::RunNowOrPostTask(platform_task_runner_, fml::MakeCopyable(std::move(task)));
return did_encode;
}
void PlatformViewsController::CreateMissingOverlays(GrDirectContext* gr_context,
const std::shared_ptr<IOSContext>& ios_context,
size_t required_overlay_layers) {
TRACE_EVENT0("flutter", "PlatformViewsController::CreateMissingLayers");
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<fml::CountDownLatch>(1u);
fml::TaskRunner::RunNowOrPostTask(platform_task_runner_, [&]() {
for (auto i = 0u; i < missing_layer_count; i++) {
CreateLayer(gr_context, //
ios_context, //
((FlutterView*)flutter_view_).pixelFormat //
);
}
latch->CountDown();
});
if (![[NSThread currentThread] isMainThread]) {
latch->Wait();
}
}
/// Update the buffers and mutate the platform views in CATransaction on the platform thread.
void PlatformViewsController::PerformSubmit(
const LayersMap& platform_view_layers,
std::unordered_map<int64_t, EmbeddedViewParams>& current_composition_params,
const std::unordered_set<int64_t>& views_to_recomposite,
const std::vector<int64_t>& composition_order,
const std::vector<std::shared_ptr<OverlayLayer>>& unused_layers,
const std::vector<std::unique_ptr<SurfaceFrame>>& surface_frames) {
TRACE_EVENT0("flutter", "PlatformViewsController::PerformSubmit");
FML_DCHECK([[NSThread currentThread] isMainThread]);
[CATransaction begin];
// Configure Flutter overlay views.
for (const auto& [view_id, 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 : GetViewsToDispose()) {
[view removeFromSuperview];
}
// Composite Platform Views.
for (int64_t view_id : views_to_recomposite) {
CompositeWithParams(view_id, current_composition_params[view_id]);
}
// Present callbacks.
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,
// then it can be removed from the scene.
RemoveUnusedLayers(unused_layers, composition_order);
// Organize the layers by their z indexes.
BringLayersIntoView(platform_view_layers, composition_order);
[CATransaction commit];
}
void PlatformViewsController::BringLayersIntoView(const LayersMap& layer_map,
const std::vector<int64_t>& composition_order) {
FML_DCHECK(flutter_view_);
UIView* flutter_view = flutter_view_;
previous_composition_order_.clear();
NSMutableArray* desired_platform_subviews = [NSMutableArray array];
for (int64_t platform_view_id : composition_order) {
previous_composition_order_.push_back(platform_view_id);
UIView* platform_view_root = platform_views_[platform_view_id].root_view;
if (platform_view_root != nil) {
[desired_platform_subviews addObject:platform_view_root];
}
auto maybe_layer_data = layer_map.find(platform_view_id);
if (maybe_layer_data != layer_map.end()) {
auto view = maybe_layer_data->second.layer->overlay_view_wrapper;
if (view != nil) {
[desired_platform_subviews addObject:view];
}
}
}
NSSet* desired_platform_subviews_set = [NSSet setWithArray:desired_platform_subviews];
NSArray* existing_platform_subviews = [flutter_view.subviews
filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id object,
NSDictionary* bindings) {
return [desired_platform_subviews_set containsObject:object];
}]];
// Manipulate view hierarchy only if needed, to address a performance issue where
// `BringLayersIntoView` is called even when view hierarchy stays the same.
// See: https://github.com/flutter/flutter/issues/121833
// TODO(hellohuanlin): investigate if it is possible to skip unnecessary BringLayersIntoView.
if (![desired_platform_subviews isEqualToArray:existing_platform_subviews]) {
for (UIView* subview in desired_platform_subviews) {
// `addSubview` will automatically reorder subview if it is already added.
[flutter_view addSubview:subview];
}
}
}
std::shared_ptr<OverlayLayer> PlatformViewsController::GetExistingLayer() {
return layer_pool_->GetNextLayer();
}
void PlatformViewsController::CreateLayer(GrDirectContext* gr_context,
const std::shared_ptr<IOSContext>& ios_context,
MTLPixelFormat pixel_format) {
layer_pool_->CreateLayer(gr_context, ios_context, pixel_format);
}
void PlatformViewsController::RemoveUnusedLayers(
const std::vector<std::shared_ptr<OverlayLayer>>& unused_layers,
const std::vector<int64_t>& composition_order) {
for (const std::shared_ptr<OverlayLayer>& layer : unused_layers) {
[layer->overlay_view_wrapper removeFromSuperview];
}
std::unordered_set<int64_t> composition_order_set;
for (int64_t view_id : composition_order) {
composition_order_set.insert(view_id);
}
// 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 = platform_views_[view_id].root_view;
[platform_view_root removeFromSuperview];
}
}
}
std::vector<UIView*> PlatformViewsController::GetViewsToDispose() {
std::vector<UIView*> views;
if (views_to_dispose_.empty()) {
return views;
}
std::unordered_set<int64_t> views_to_composite(composition_order_.begin(),
composition_order_.end());
std::unordered_set<int64_t> views_to_delay_dispose;
for (int64_t viewId : views_to_dispose_) {
if (views_to_composite.count(viewId)) {
views_to_delay_dispose.insert(viewId);
continue;
}
UIView* root_view = platform_views_[viewId].root_view;
views.push_back(root_view);
current_composition_params_.erase(viewId);
views_to_recomposite_.erase(viewId);
platform_views_.erase(viewId);
}
views_to_dispose_ = std::move(views_to_delay_dispose);
return views;
}
void PlatformViewsController::ResetFrameState() {
slices_.clear();
composition_order_.clear();
visited_platform_views_.clear();
}
} // namespace flutter

View File

@ -13,14 +13,14 @@ namespace flutter {
class IOSExternalViewEmbedder : public ExternalViewEmbedder {
public:
IOSExternalViewEmbedder(
const std::shared_ptr<PlatformViewsController>& platform_views_controller,
__weak FlutterPlatformViewsController* platform_views_controller,
const std::shared_ptr<IOSContext>& context);
// |ExternalViewEmbedder|
virtual ~IOSExternalViewEmbedder() override;
private:
const std::shared_ptr<PlatformViewsController>& platform_views_controller_;
__weak FlutterPlatformViewsController* platform_views_controller_;
std::shared_ptr<IOSContext> ios_context_;
// |ExternalViewEmbedder|

View File

@ -12,7 +12,7 @@ FLUTTER_ASSERT_ARC
namespace flutter {
IOSExternalViewEmbedder::IOSExternalViewEmbedder(
const std::shared_ptr<PlatformViewsController>& platform_views_controller,
__weak FlutterPlatformViewsController* platform_views_controller,
const std::shared_ptr<IOSContext>& context)
: platform_views_controller_(platform_views_controller), ios_context_(context) {
FML_CHECK(ios_context_);
@ -31,7 +31,7 @@ DlCanvas* IOSExternalViewEmbedder::GetRootCanvas() {
void IOSExternalViewEmbedder::CancelFrame() {
TRACE_EVENT0("flutter", "IOSExternalViewEmbedder::CancelFrame");
FML_CHECK(platform_views_controller_);
platform_views_controller_->CancelFrame();
[platform_views_controller_ cancelFrame];
}
// |ExternalViewEmbedder|
@ -42,7 +42,7 @@ void IOSExternalViewEmbedder::BeginFrame(
// |ExternalViewEmbedder|
void IOSExternalViewEmbedder::PrepareFlutterView(SkISize frame_size, double device_pixel_ratio) {
FML_CHECK(platform_views_controller_);
platform_views_controller_->BeginFrame(frame_size);
[platform_views_controller_ beginFrameWithSize:frame_size];
}
// |ExternalViewEmbedder|
@ -51,7 +51,7 @@ void IOSExternalViewEmbedder::PrerollCompositeEmbeddedView(
std::unique_ptr<EmbeddedViewParams> params) {
TRACE_EVENT0("flutter", "IOSExternalViewEmbedder::PrerollCompositeEmbeddedView");
FML_CHECK(platform_views_controller_);
platform_views_controller_->PrerollCompositeEmbeddedView(view_id, std::move(params));
[platform_views_controller_ prerollCompositeEmbeddedView:view_id withParams:std::move(params)];
}
// |ExternalViewEmbedder|
@ -59,8 +59,10 @@ PostPrerollResult IOSExternalViewEmbedder::PostPrerollAction(
const fml::RefPtr<fml::RasterThreadMerger>& raster_thread_merger) {
TRACE_EVENT0("flutter", "IOSExternalViewEmbedder::PostPrerollAction");
FML_CHECK(platform_views_controller_);
PostPrerollResult result = platform_views_controller_->PostPrerollAction(
raster_thread_merger, ios_context_->GetBackend() != IOSRenderingBackend::kSkia);
BOOL impeller_enabled = ios_context_->GetBackend() != IOSRenderingBackend::kSkia;
PostPrerollResult result =
[platform_views_controller_ postPrerollActionWithThreadMerger:raster_thread_merger
impellerEnabled:impeller_enabled];
return result;
}
@ -68,7 +70,7 @@ PostPrerollResult IOSExternalViewEmbedder::PostPrerollAction(
DlCanvas* IOSExternalViewEmbedder::CompositeEmbeddedView(int64_t view_id) {
TRACE_EVENT0("flutter", "IOSExternalViewEmbedder::CompositeEmbeddedView");
FML_CHECK(platform_views_controller_);
return platform_views_controller_->CompositeEmbeddedView(view_id);
return [platform_views_controller_ compositeEmbeddedViewWithId:view_id];
}
// |ExternalViewEmbedder|
@ -83,7 +85,9 @@ void IOSExternalViewEmbedder::SubmitFlutterView(
// 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:std::move(frame)
withIosContext:ios_context_
grContext:context];
TRACE_EVENT0("flutter", "IOSExternalViewEmbedder::DidSubmitFrame");
}
@ -92,8 +96,10 @@ void IOSExternalViewEmbedder::EndFrame(
bool should_resubmit_frame,
const fml::RefPtr<fml::RasterThreadMerger>& raster_thread_merger) {
TRACE_EVENT0("flutter", "IOSExternalViewEmbedder::EndFrame");
platform_views_controller_->EndFrame(should_resubmit_frame, raster_thread_merger,
ios_context_->GetBackend() != IOSRenderingBackend::kSkia);
BOOL impeller_enabled = ios_context_->GetBackend() != IOSRenderingBackend::kSkia;
[platform_views_controller_ endFrameWithResubmit:should_resubmit_frame
threadMerger:raster_thread_merger
impellerEnabled:impeller_enabled];
}
// |ExternalViewEmbedder|
@ -110,12 +116,12 @@ bool IOSExternalViewEmbedder::SupportsDynamicThreadMerging() {
void IOSExternalViewEmbedder::PushFilterToVisitedPlatformViews(
const std::shared_ptr<DlImageFilter>& filter,
const SkRect& filter_rect) {
platform_views_controller_->PushFilterToVisitedPlatformViews(filter, filter_rect);
[platform_views_controller_ pushFilterToVisitedPlatformViews:filter withRect:filter_rect];
}
// |ExternalViewEmbedder|
void IOSExternalViewEmbedder::PushVisitedPlatformView(int64_t view_id) {
platform_views_controller_->PushVisitedPlatformView(view_id);
[platform_views_controller_ pushVisitedPlatformViewId:view_id];
}
} // namespace flutter

View File

@ -40,13 +40,13 @@ class PlatformViewIOS final : public PlatformView {
public:
PlatformViewIOS(PlatformView::Delegate& delegate,
const std::shared_ptr<IOSContext>& context,
const std::shared_ptr<PlatformViewsController>& platform_views_controller,
__weak FlutterPlatformViewsController* platform_views_controller,
const flutter::TaskRunners& task_runners);
explicit PlatformViewIOS(
PlatformView::Delegate& delegate,
IOSRenderingAPI rendering_api,
const std::shared_ptr<PlatformViewsController>& platform_views_controller,
__weak FlutterPlatformViewsController* platform_views_controller,
const flutter::TaskRunners& task_runners,
const std::shared_ptr<fml::ConcurrentTaskRunner>& worker_task_runner,
const std::shared_ptr<const fml::SyncSwitch>& is_gpu_disabled_sync_switch);
@ -137,7 +137,7 @@ class PlatformViewIOS final : public PlatformView {
std::mutex ios_surface_mutex_;
std::unique_ptr<IOSSurface> ios_surface_;
std::shared_ptr<IOSContext> ios_context_;
const std::shared_ptr<PlatformViewsController>& platform_views_controller_;
__weak FlutterPlatformViewsController* platform_views_controller_;
AccessibilityBridgeManager accessibility_bridge_;
ScopedObserver dealloc_view_controller_observer_;
std::vector<std::string> platform_resolved_locale_;

View File

@ -41,11 +41,10 @@ void PlatformViewIOS::AccessibilityBridgeManager::Clear() {
accessibility_bridge_.reset();
}
PlatformViewIOS::PlatformViewIOS(
PlatformView::Delegate& delegate,
const std::shared_ptr<IOSContext>& context,
const std::shared_ptr<PlatformViewsController>& platform_views_controller,
const flutter::TaskRunners& task_runners)
PlatformViewIOS::PlatformViewIOS(PlatformView::Delegate& delegate,
const std::shared_ptr<IOSContext>& context,
__weak FlutterPlatformViewsController* platform_views_controller,
const flutter::TaskRunners& task_runners)
: PlatformView(delegate, task_runners),
ios_context_(context),
platform_views_controller_(platform_views_controller),
@ -56,7 +55,7 @@ PlatformViewIOS::PlatformViewIOS(
PlatformViewIOS::PlatformViewIOS(
PlatformView::Delegate& delegate,
IOSRenderingAPI rendering_api,
const std::shared_ptr<PlatformViewsController>& platform_views_controller,
__weak FlutterPlatformViewsController* platform_views_controller,
const flutter::TaskRunners& task_runners,
const std::shared_ptr<fml::ConcurrentTaskRunner>& worker_task_runner,
const std::shared_ptr<const fml::SyncSwitch>& is_gpu_disabled_sync_switch)
@ -209,7 +208,7 @@ void PlatformViewIOS::OnPreEngineRestart() const {
if (!owner_controller_) {
return;
}
owner_controller_.platformViewsController->Reset();
[owner_controller_.platformViewsController reset];
[owner_controller_.restorationPlugin reset];
}