Revert "iOS: Migrate PlatformViewsController to Objective-C (#56790)" (flutter/engine#56817)

This is a combination of 3 reverts, required to get back to the revert that caused `ios_platform_view_tests` to start failing in the framework repo. In reverse chronological order, this reverts two trivial commits plus the non-trivial commit that likely caused the breakage:

* Revert "iOS: Eliminate global in platformviews controller (#56805)" This reverts commit cea4600caa7098fa7ec109d18b869db46cda726a.
* Revert "iOS: Delete FlutterPlatformViewsController.layerPoolSize (#56806)" This reverts commit 80fa8a590876e0d29055b9ddbfa8670c1f83759e.
* Revert "iOS: Migrate PlatformViewsController to Objective-C (#56790)" This reverts commit afd05afc406deb79fbe9c16684aeeeb19322b288.

[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 11:52:13 -08:00 committed by GitHub
parent 61d0019469
commit 52661e51c0
24 changed files with 1973 additions and 2279 deletions

View File

@ -44564,8 +44564,6 @@ 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
@ -44627,6 +44625,8 @@ 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
@ -47500,8 +47500,6 @@ 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
@ -47563,6 +47561,8 @@ 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,8 +80,6 @@ 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",
@ -122,6 +120,8 @@ 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,8 +105,6 @@ 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;
@ -152,6 +150,7 @@ 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;
@ -212,7 +211,7 @@ static constexpr int kNumProfilerSamplesPerSec = 5;
_pluginPublications = [[NSMutableDictionary alloc] init];
_registrars = [[NSMutableDictionary alloc] init];
[self recreatePlatformViewsController];
[self recreatePlatformViewController];
_binaryMessenger = [[FlutterBinaryMessengerRelay alloc] initWithParent:self];
_textureRegistry = [[FlutterTextureRegistryRelay alloc] initWithParent:self];
_connections.reset(new flutter::ConnectionCollection());
@ -263,9 +262,9 @@ static constexpr int kNumProfilerSamplesPerSec = 5;
object:nil];
}
- (void)recreatePlatformViewsController {
- (void)recreatePlatformViewController {
_renderingApi = flutter::GetRenderingAPIForProcess(FlutterView.forceSoftwareRendering);
_platformViewsController = [[FlutterPlatformViewsController alloc] init];
_platformViewsController.reset(new flutter::PlatformViewsController());
}
- (flutter::IOSRenderingAPI)platformViewsRenderingAPI {
@ -453,7 +452,11 @@ static constexpr int kNumProfilerSamplesPerSec = 5;
_shell.reset();
_profiler.reset();
_threadHost.reset();
_platformViewsController = nil;
_platformViewsController.reset();
}
- (std::shared_ptr<flutter::PlatformViewsController>&)platformViewsController {
return _platformViewsController;
}
- (NSURL*)observatoryUrl {
@ -632,7 +635,7 @@ static constexpr int kNumProfilerSamplesPerSec = 5;
[self.platformViewsChannel
setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
if (weakSelf) {
[weakSelf.platformViewsController onMethodCall:call result:result];
weakSelf.platformViewsController->OnMethodCall(call, result);
}
}];
@ -774,11 +777,11 @@ static void SetEntryPoint(flutter::Settings* settings, NSString* entrypoint, NSS
if (!strongSelf) {
return std::unique_ptr<flutter::PlatformViewIOS>();
}
[strongSelf recreatePlatformViewsController];
strongSelf.platformViewsController.taskRunner =
shell.GetTaskRunners().GetPlatformTaskRunner();
[strongSelf recreatePlatformViewController];
strongSelf->_platformViewsController->SetTaskRunner(
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());
};
@ -1100,7 +1103,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 firstResponderPlatformViewId];
long platform_view_id = self.platformViewsController->FindFirstResponderPlatformViewId();
if (platform_view_id == -1) {
return;
}
@ -1397,10 +1400,11 @@ 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 recreatePlatformViewsController];
result.platformViewsController.taskRunner = shell.GetTaskRunners().GetPlatformTaskRunner();
[result recreatePlatformViewController];
result->_platformViewsController->SetTaskRunner(
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 =
@ -1495,9 +1499,8 @@ static void SetEntryPoint(flutter::Settings* settings, NSString* entrypoint, NSS
withId:(NSString*)factoryId
gestureRecognizersBlockingPolicy:
(FlutterPlatformViewGestureRecognizersBlockingPolicy)gestureRecognizersBlockingPolicy {
[_flutterEngine.platformViewsController registerViewFactory:factory
withId:factoryId
gestureRecognizersBlockingPolicy:gestureRecognizersBlockingPolicy];
[_flutterEngine platformViewsController]->RegisterViewFactory(factory, factoryId,
gestureRecognizersBlockingPolicy);
}
@end

View File

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

View File

@ -518,7 +518,8 @@ static BOOL _preparedOnce = NO;
@implementation FlutterTouchInterceptingView
- (instancetype)initWithEmbeddedView:(UIView*)embeddedView
platformViewsController:(FlutterPlatformViewsController*)platformViewsController
platformViewsController:
(fml::WeakPtr<flutter::PlatformViewsController>)platformViewsController
gestureRecognizersBlockingPolicy:
(FlutterPlatformViewGestureRecognizersBlockingPolicy)blockingPolicy {
self = [super initWithFrame:embeddedView.frame];
@ -666,7 +667,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.
__weak FlutterPlatformViewsController* _platformViewsController;
fml::WeakPtr<flutter::PlatformViewsController> _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.
@ -677,12 +678,13 @@ static BOOL _preparedOnce = NO;
}
- (instancetype)initWithTarget:(id)target
platformViewsController:(FlutterPlatformViewsController*)platformViewsController {
platformViewsController:
(fml::WeakPtr<flutter::PlatformViewsController>)platformViewsController {
self = [super initWithTarget:target action:nil];
if (self) {
self.delegate = self;
FML_DCHECK(platformViewsController);
_platformViewsController = platformViewsController;
FML_DCHECK(platformViewsController.get() != nullptr);
_platformViewsController = std::move(platformViewsController);
_currentTouchPointersCount = 0;
}
return self;
@ -690,7 +692,7 @@ static BOOL _preparedOnce = NO;
- (ForwardingGestureRecognizer*)recreateRecognizerWithTarget:(id)target {
return [[ForwardingGestureRecognizer alloc] initWithTarget:target
platformViewsController:_platformViewsController];
platformViewsController:std::move(_platformViewsController)];
}
- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
@ -699,7 +701,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.flutterViewController;
_flutterViewController = _platformViewsController->GetFlutterViewController();
}
[_flutterViewController touchesBegan:touches withEvent:event];
_currentTouchPointersCount += touches.count;

View File

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

View File

@ -14,11 +14,12 @@
@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.platformViewsController.flutterView = self.flutterView;
self.platformViewsController.flutterViewController = self;
[self.engine platformViewsController]->SetFlutterView(self.flutterView);
[self.engine platformViewsController]->SetFlutterViewController(self);
[self.engine iosPlatformView]->NotifyCreated();
} else {
self.displayingFlutterUI = NO;
[self.engine iosPlatformView]->NotifyDestroyed();
self.platformViewsController.flutterView = nil;
self.platformViewsController.flutterViewController = nil;
[self.engine platformViewsController]->SetFlutterView(nullptr);
[self.engine platformViewsController]->SetFlutterViewController(nullptr);
}
}
@ -2321,7 +2321,7 @@ static flutter::PointerData::DeviceKind DeviceKindFromTouchType(UITouch* touch)
#pragma mark - Platform views
- (FlutterPlatformViewsController*)platformViewsController {
- (std::shared_ptr<flutter::PlatformViewsController>&)platformViewsController {
return self.engine.platformViewsController;
}

View File

@ -5,12 +5,11 @@
#ifndef FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERVIEWCONTROLLER_INTERNAL_H_
#define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERVIEWCONTROLLER_INTERNAL_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/Headers/FlutterViewController.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"
@ -57,8 +56,7 @@ typedef void (^FlutterKeyboardAnimationCallback)(fml::TimePoint);
*/
@property(nonatomic, assign, readwrite) BOOL prefersStatusBarHidden;
@property(nonatomic, readonly) FlutterPlatformViewsController* platformViewsController;
- (std::shared_ptr<flutter::PlatformViewsController>&)platformViewsController;
- (FlutterRestorationPlugin*)restorationPlugin;
// Accepts keypress events, and then calls |nextAction| if the event was not

View File

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

View File

@ -43,7 +43,9 @@ class MockAccessibilityBridge : public AccessibilityBridgeIos {
}
void AccessibilityObjectDidBecomeFocused(int32_t id) override {}
void AccessibilityObjectDidLoseFocus(int32_t id) override {}
FlutterPlatformViewsController* GetPlatformViewsController() const override { return nil; }
std::shared_ptr<PlatformViewsController> GetPlatformViewsController() const override {
return nil;
}
std::vector<SemanticsActionObservation> observations;
bool isVoiceOverRunningValue;
@ -72,7 +74,9 @@ class MockAccessibilityBridgeNoWindow : public AccessibilityBridgeIos {
}
void AccessibilityObjectDidBecomeFocused(int32_t id) override {}
void AccessibilityObjectDidLoseFocus(int32_t id) override {}
FlutterPlatformViewsController* GetPlatformViewsController() const override { return nil; }
std::shared_ptr<PlatformViewsController> 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,
__weak FlutterPlatformViewsController* platform_views_controller,
std::shared_ptr<PlatformViewsController> platform_views_controller,
std::unique_ptr<IosDelegate> ios_delegate = nullptr);
~AccessibilityBridge();
@ -72,7 +72,7 @@ class AccessibilityBridge final : public AccessibilityBridgeIos {
fml::WeakPtr<AccessibilityBridge> GetWeakPtr();
FlutterPlatformViewsController* GetPlatformViewsController() const override {
std::shared_ptr<PlatformViewsController> GetPlatformViewsController() const override {
return platform_views_controller_;
};
@ -91,7 +91,7 @@ class AccessibilityBridge final : public AccessibilityBridgeIos {
FlutterViewController* view_controller_;
PlatformViewIOS* platform_view_;
__weak FlutterPlatformViewsController* platform_views_controller_;
const std::shared_ptr<PlatformViewsController> 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,
__weak FlutterPlatformViewsController* platform_views_controller,
std::shared_ptr<PlatformViewsController> platform_views_controller,
std::unique_ptr<IosDelegate> ios_delegate)
: view_controller_(view_controller),
platform_view_(platform_view),
platform_views_controller_(platform_views_controller),
platform_views_controller_(std::move(platform_views_controller)),
last_focused_semantics_object_id_(kSemanticObjectIdInvalid),
objects_([[NSMutableDictionary alloc] init]),
previous_routes_({}),
@ -271,13 +271,11 @@ 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()) {
FlutterPlatformViewsController* platformViewsController =
weak_ptr->GetPlatformViewsController();
FlutterTouchInterceptingView* touchInterceptingView =
[platformViewsController flutterTouchInterceptingViewForId:node.platformViewId];
return [[FlutterPlatformViewSemanticsContainer alloc] initWithBridge:weak_ptr
uid:node.id
platformView:touchInterceptingView];
return [[FlutterPlatformViewSemanticsContainer alloc]
initWithBridge:weak_ptr
uid:node.id
platformView:weak_ptr->GetPlatformViewsController()->GetFlutterTouchInterceptingViewByID(
node.platformViewId)];
} 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 FlutterPlatformViewsController* GetPlatformViewsController() const = 0;
virtual std::shared_ptr<PlatformViewsController> GetPlatformViewsController() const = 0;
};
} // namespace flutter

View File

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

View File

@ -0,0 +1,318 @@
// 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

@ -0,0 +1,874 @@
// 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(
__weak FlutterPlatformViewsController* platform_views_controller,
const std::shared_ptr<PlatformViewsController>& platform_views_controller,
const std::shared_ptr<IOSContext>& context);
// |ExternalViewEmbedder|
virtual ~IOSExternalViewEmbedder() override;
private:
__weak FlutterPlatformViewsController* platform_views_controller_;
const std::shared_ptr<PlatformViewsController>& platform_views_controller_;
std::shared_ptr<IOSContext> ios_context_;
// |ExternalViewEmbedder|

View File

@ -12,7 +12,7 @@ FLUTTER_ASSERT_ARC
namespace flutter {
IOSExternalViewEmbedder::IOSExternalViewEmbedder(
__weak FlutterPlatformViewsController* platform_views_controller,
const std::shared_ptr<PlatformViewsController>& 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_ beginFrameWithSize:frame_size];
platform_views_controller_->BeginFrame(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 withParams:std::move(params)];
platform_views_controller_->PrerollCompositeEmbeddedView(view_id, std::move(params));
}
// |ExternalViewEmbedder|
@ -59,10 +59,8 @@ PostPrerollResult IOSExternalViewEmbedder::PostPrerollAction(
const fml::RefPtr<fml::RasterThreadMerger>& raster_thread_merger) {
TRACE_EVENT0("flutter", "IOSExternalViewEmbedder::PostPrerollAction");
FML_CHECK(platform_views_controller_);
BOOL impeller_enabled = ios_context_->GetBackend() != IOSRenderingBackend::kSkia;
PostPrerollResult result =
[platform_views_controller_ postPrerollActionWithThreadMerger:raster_thread_merger
impellerEnabled:impeller_enabled];
PostPrerollResult result = platform_views_controller_->PostPrerollAction(
raster_thread_merger, ios_context_->GetBackend() != IOSRenderingBackend::kSkia);
return result;
}
@ -70,7 +68,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_ compositeEmbeddedViewWithId:view_id];
return platform_views_controller_->CompositeEmbeddedView(view_id);
}
// |ExternalViewEmbedder|
@ -85,9 +83,7 @@ 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:std::move(frame)
withIosContext:ios_context_
grContext:context];
platform_views_controller_->SubmitFrame(context, ios_context_, std::move(frame));
TRACE_EVENT0("flutter", "IOSExternalViewEmbedder::DidSubmitFrame");
}
@ -96,10 +92,8 @@ void IOSExternalViewEmbedder::EndFrame(
bool should_resubmit_frame,
const fml::RefPtr<fml::RasterThreadMerger>& raster_thread_merger) {
TRACE_EVENT0("flutter", "IOSExternalViewEmbedder::EndFrame");
BOOL impeller_enabled = ios_context_->GetBackend() != IOSRenderingBackend::kSkia;
[platform_views_controller_ endFrameWithResubmit:should_resubmit_frame
threadMerger:raster_thread_merger
impellerEnabled:impeller_enabled];
platform_views_controller_->EndFrame(should_resubmit_frame, raster_thread_merger,
ios_context_->GetBackend() != IOSRenderingBackend::kSkia);
}
// |ExternalViewEmbedder|
@ -116,12 +110,12 @@ bool IOSExternalViewEmbedder::SupportsDynamicThreadMerging() {
void IOSExternalViewEmbedder::PushFilterToVisitedPlatformViews(
const std::shared_ptr<DlImageFilter>& filter,
const SkRect& filter_rect) {
[platform_views_controller_ pushFilterToVisitedPlatformViews:filter withRect:filter_rect];
platform_views_controller_->PushFilterToVisitedPlatformViews(filter, filter_rect);
}
// |ExternalViewEmbedder|
void IOSExternalViewEmbedder::PushVisitedPlatformView(int64_t view_id) {
[platform_views_controller_ pushVisitedPlatformViewId:view_id];
platform_views_controller_->PushVisitedPlatformView(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,
__weak FlutterPlatformViewsController* platform_views_controller,
const std::shared_ptr<PlatformViewsController>& platform_views_controller,
const flutter::TaskRunners& task_runners);
explicit PlatformViewIOS(
PlatformView::Delegate& delegate,
IOSRenderingAPI rendering_api,
__weak FlutterPlatformViewsController* platform_views_controller,
const std::shared_ptr<PlatformViewsController>& 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_;
__weak FlutterPlatformViewsController* platform_views_controller_;
const std::shared_ptr<PlatformViewsController>& platform_views_controller_;
AccessibilityBridgeManager accessibility_bridge_;
ScopedObserver dealloc_view_controller_observer_;
std::vector<std::string> platform_resolved_locale_;

View File

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