[macos] Enable macOS platform views only for Metal (flutter/engine#30853)

This commit is contained in:
Kaushik Iska
2022-01-20 15:01:41 -08:00
committed by GitHub
parent f65004e7c5
commit 66bbb434e5
16 changed files with 508 additions and 26 deletions

View File

@@ -1227,6 +1227,7 @@ FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterAppD
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterDartProject.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterEngine.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterMacOS.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterPlatformViews.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterPluginMacOS.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterPluginRegistrarMacOS.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterViewController.h
@@ -1288,6 +1289,9 @@ FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterOpenG
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformNodeDelegateMac.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformNodeDelegateMac.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformNodeDelegateMacTest.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewControllerTest.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterRenderer.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterRenderingBackend.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterRenderingBackend.mm
@@ -1316,6 +1320,8 @@ FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/KeyCodeMap.m
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/KeyCodeMap_Internal.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/MacOSGLContextSwitch.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/MacOSGLContextSwitch.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/TestFlutterPlatformView.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/TestFlutterPlatformView.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/fixtures/flutter_desktop_test.dart
FILE: ../../../flutter/shell/platform/darwin/macos/framework/module.modulemap
FILE: ../../../flutter/shell/platform/embedder/assets/EmbedderInfo.plist

View File

@@ -40,6 +40,7 @@ _flutter_framework_headers = [
"framework/Headers/FlutterDartProject.h",
"framework/Headers/FlutterEngine.h",
"framework/Headers/FlutterMacOS.h",
"framework/Headers/FlutterPlatformViews.h",
"framework/Headers/FlutterPluginMacOS.h",
"framework/Headers/FlutterPluginRegistrarMacOS.h",
"framework/Headers/FlutterViewController.h",
@@ -95,6 +96,8 @@ source_set("flutter_framework_source") {
"framework/Source/FlutterOpenGLRenderer.mm",
"framework/Source/FlutterPlatformNodeDelegateMac.h",
"framework/Source/FlutterPlatformNodeDelegateMac.mm",
"framework/Source/FlutterPlatformViewController.h",
"framework/Source/FlutterPlatformViewController.mm",
"framework/Source/FlutterRenderer.h",
"framework/Source/FlutterRenderingBackend.h",
"framework/Source/FlutterRenderingBackend.mm",
@@ -186,11 +189,14 @@ executable("flutter_desktop_darwin_unittests") {
"framework/Source/FlutterMetalSurfaceManagerTest.mm",
"framework/Source/FlutterOpenGLRendererTest.mm",
"framework/Source/FlutterPlatformNodeDelegateMacTest.mm",
"framework/Source/FlutterPlatformViewControllerTest.mm",
"framework/Source/FlutterTextInputPluginTest.mm",
"framework/Source/FlutterTextInputSemanticsObjectTest.mm",
"framework/Source/FlutterViewControllerTest.mm",
"framework/Source/FlutterViewControllerTestUtils.h",
"framework/Source/FlutterViewControllerTestUtils.mm",
"framework/Source/TestFlutterPlatformView.h",
"framework/Source/TestFlutterPlatformView.mm",
]
cflags_objcc = flutter_cflags_objcc_arc

View File

@@ -0,0 +1,40 @@
// 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_FLUTTERPLATFORMVIEWS_H_
#define FLUTTER_FLUTTERPLATFORMVIEWS_H_
#import <AppKit/AppKit.h>
#import "FlutterCodecs.h"
#import "FlutterMacros.h"
@protocol FlutterPlatformViewFactory <NSObject>
/**
* Create a Platform View which is an `NSView`.
*
* A MacOS plugin should implement this method and return an `NSView`, which can be embedded in a
* Flutter App.
*
* The implementation of this method should create a new `NSView`.
*
* @param viewId A unique identifier for this view.
* @param args Parameters for creating the view sent from the Dart side of the
* Flutter app. If `createArgsCodec` is not implemented, or if no creation arguments were sent from
* the Dart code, this will be null. Otherwise this will be the value sent from the Dart code as
* decoded by `createArgsCodec`.
*/
- (nonnull NSView*)createWithviewIdentifier:(int64_t)viewId arguments:(nullable id)args;
/**
* Returns the `FlutterMessageCodec` for decoding the args parameter of `createWithFrame`.
*
* Only implement this if `createWithFrame` needs an arguments parameter.
*/
@optional
- (nullable NSObject<FlutterMessageCodec>*)createArgsCodec;
@end
#endif // FLUTTER_FLUTTERPLATFORMVIEWS_H_

View File

@@ -7,6 +7,7 @@
#import "FlutterBinaryMessenger.h"
#import "FlutterChannels.h"
#import "FlutterMacros.h"
#import "FlutterPlatformViews.h"
#import "FlutterPluginMacOS.h"
#import "FlutterTexture.h"
@@ -48,6 +49,18 @@ FLUTTER_DARWIN_EXPORT
- (void)addMethodCallDelegate:(nonnull id<FlutterPlugin>)delegate
channel:(nonnull FlutterMethodChannel*)channel;
/**
* Registers a `FlutterPlatformViewFactory` for creation of platform views.
*
* Plugins expose `NSView` for embedding in Flutter apps by registering a view factory.
*
* @param factory The view factory that will be registered.
* @param factoryId A unique identifier for the factory, the Dart code of the Flutter app can use
* this identifier to request creation of a `NSView` by the registered factory.
*/
- (void)registerViewFactory:(nonnull NSObject<FlutterPlatformViewFactory>*)factory
withId:(nonnull NSString*)factoryId;
@end
/**

View File

@@ -6,6 +6,7 @@
#import "FlutterEngine.h"
#import "FlutterMacros.h"
#import "FlutterPlatformViews.h"
#import "FlutterPluginRegistrarMacOS.h"
/**

View File

@@ -15,9 +15,10 @@
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterMetalCompositor.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterMetalRenderer.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterOpenGLRenderer.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterRenderingBackend.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h"
#include "flutter/shell/platform/embedder/embedder.h"
#import "flutter/shell/platform/embedder/embedder.h"
/**
* Constructs and returns a FlutterLocale struct corresponding to |locale|, which must outlive
@@ -95,6 +96,11 @@ static FlutterLocale FlutterLocaleFromNSLocale(NSLocale* locale) {
*/
- (void)loadAOTData:(NSString*)assetsDir;
/**
* Creates a platform view channel and sets up the method handler.
*/
- (void)setupPlatformViewChannel;
@end
#pragma mark -
@@ -145,6 +151,11 @@ static FlutterLocale FlutterLocaleFromNSLocale(NSLocale* locale) {
}];
}
- (void)registerViewFactory:(nonnull NSObject<FlutterPlatformViewFactory>*)factory
withId:(nonnull NSString*)factoryId {
[[_flutterEngine platformViewController] registerViewFactory:factory withId:factoryId];
}
@end
// Callbacks provided to the engine. See the called methods for documentation.
@@ -186,6 +197,14 @@ static void OnPlatformMessage(const FlutterPlatformMessage* message, FlutterEngi
// FlutterCompositor is copied and used in embedder.cc.
FlutterCompositor _compositor;
// Method channel for platform view functions. These functions include creating, disposing and
// mutating a platform view.
FlutterMethodChannel* _platformViewsChannel;
// Used to support creation and deletion of platform views and registering platform view
// factories. Lifecycle is tied to the engine.
FlutterPlatformViewController* _platformViewController;
}
- (instancetype)initWithName:(NSString*)labelPrefix project:(FlutterDartProject*)project {
@@ -219,6 +238,9 @@ static void OnPlatformMessage(const FlutterPlatformMessage* message, FlutterEngi
name:NSCurrentLocaleDidChangeNotification
object:nil];
_platformViewController = [[FlutterPlatformViewController alloc] init];
[self setupPlatformViewChannel];
return self;
}
@@ -383,8 +405,8 @@ static void OnPlatformMessage(const FlutterPlatformMessage* message, FlutterEngi
if ([FlutterRenderingBackend renderUsingMetal]) {
FlutterMetalRenderer* metalRenderer = reinterpret_cast<FlutterMetalRenderer*>(_renderer);
_macOSCompositor =
std::make_unique<flutter::FlutterMetalCompositor>(_viewController, metalRenderer.device);
_macOSCompositor = std::make_unique<flutter::FlutterMetalCompositor>(
_viewController, _platformViewController, metalRenderer.device);
_macOSCompositor->SetPresentCallback([weakSelf](bool has_flutter_content) {
if (has_flutter_content) {
FlutterMetalRenderer* metalRenderer =
@@ -541,6 +563,10 @@ static void OnPlatformMessage(const FlutterPlatformMessage* message, FlutterEngi
_embedderAPI.DispatchSemanticsAction(_engine, target, action, data.GetMapping(), data.GetSize());
}
- (FlutterPlatformViewController*)platformViewController {
return _platformViewController;
}
#pragma mark - Private methods
- (void)sendUserLocales {
@@ -630,6 +656,18 @@ static void OnPlatformMessage(const FlutterPlatformMessage* message, FlutterEngi
_engine = nullptr;
}
- (void)setupPlatformViewChannel {
_platformViewsChannel =
[FlutterMethodChannel methodChannelWithName:@"flutter/platform_views"
binaryMessenger:self.binaryMessenger
codec:[FlutterStandardMethodCodec sharedInstance]];
__weak FlutterEngine* weakSelf = self;
[_platformViewsChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
[[weakSelf platformViewController] handleMethodCall:call result:result];
}];
}
#pragma mark - FlutterBinaryMessenger
- (void)sendOnChannel:(nonnull NSString*)channel message:(nullable NSData*)message {
@@ -781,20 +819,22 @@ static void OnPlatformMessage(const FlutterPlatformMessage* message, FlutterEngi
#pragma mark - Task runner integration
- (void)postMainThreadTask:(FlutterTask)task targetTimeInNanoseconds:(uint64_t)targetTime {
const auto engine_time = _embedderAPI.GetCurrentTime();
__weak FlutterEngine* weak_self = self;
auto worker = ^{
FlutterEngine* strong_self = weak_self;
if (strong_self && strong_self->_engine) {
auto result = _embedderAPI.RunTask(strong_self->_engine, &task);
if (result != kSuccess) {
NSLog(@"Could not post a task to the Flutter engine.");
}
- (void)runTaskOnEmbedder:(FlutterTask)task {
if (_engine) {
auto result = _embedderAPI.RunTask(_engine, &task);
if (result != kSuccess) {
NSLog(@"Could not post a task to the Flutter engine.");
}
}
}
- (void)postMainThreadTask:(FlutterTask)task targetTimeInNanoseconds:(uint64_t)targetTime {
__weak FlutterEngine* weakSelf = self;
auto worker = ^{
[weakSelf runTaskOnEmbedder:task];
};
const auto engine_time = _embedderAPI.GetCurrentTime();
if (targetTime <= engine_time) {
dispatch_async(dispatch_get_main_queue(), worker);

View File

@@ -343,7 +343,8 @@ TEST_F(FlutterEngineTest, NativeCallbacks) {
ASSERT_TRUE(latch_called);
}
TEST(FlutterEngine, Compositor) {
// TODO: Enable after https://github.com/flutter/flutter/issues/96668 is fixed.
TEST(FlutterEngine, DISABLED_Compositor) {
NSString* fixtures = @(flutter::testing::GetFixturesPath());
FlutterDartProject* project = [[FlutterDartProject alloc]
initWithAssetsPath:fixtures

View File

@@ -10,6 +10,7 @@
#include "flutter/shell/platform/common/accessibility_bridge.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterCompositor.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterRenderer.h"
@interface FlutterEngine ()
@@ -70,6 +71,8 @@
*/
- (BOOL)unregisterTextureWithID:(int64_t)textureID;
- (nonnull FlutterPlatformViewController*)platformViewController;
// Accessibility API.
/**

View File

@@ -7,13 +7,16 @@
#include "flutter/fml/macros.h"
#include "flutter/shell/platform/darwin/macos/framework/Source/FlutterCompositor.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.h"
namespace flutter {
class FlutterMetalCompositor : public FlutterCompositor {
public:
explicit FlutterMetalCompositor(FlutterViewController* view_controller,
id<MTLDevice> mtl_device);
explicit FlutterMetalCompositor(
FlutterViewController* view_controller,
FlutterPlatformViewController* platform_views_controller,
id<MTLDevice> mtl_device);
virtual ~FlutterMetalCompositor() = default;
@@ -42,7 +45,12 @@ class FlutterMetalCompositor : public FlutterCompositor {
bool Present(const FlutterLayer** layers, size_t layers_count) override;
private:
// Presents the platform view layer represented by `layer`. `layer_index` is
// used to position the layer in the z-axis.
void PresentPlatformView(const FlutterLayer* layer, size_t layer_index);
const id<MTLDevice> mtl_device_;
const FlutterPlatformViewController* platform_views_controller_;
FML_DISALLOW_COPY_AND_ASSIGN(FlutterMetalCompositor);
};

View File

@@ -10,9 +10,13 @@
namespace flutter {
FlutterMetalCompositor::FlutterMetalCompositor(FlutterViewController* view_controller,
id<MTLDevice> mtl_device)
: FlutterCompositor(view_controller), mtl_device_(mtl_device) {}
FlutterMetalCompositor::FlutterMetalCompositor(
FlutterViewController* view_controller,
FlutterPlatformViewController* platform_views_controller,
id<MTLDevice> mtl_device)
: FlutterCompositor(view_controller),
mtl_device_(mtl_device),
platform_views_controller_(platform_views_controller) {}
bool FlutterMetalCompositor::CreateBackingStore(const FlutterBackingStoreConfig* config,
FlutterBackingStore* backing_store_out) {
@@ -77,7 +81,6 @@ bool FlutterMetalCompositor::Present(const FlutterLayer** layers, size_t layers_
SetFrameStatus(FrameStatus::kPresenting);
bool has_flutter_content = false;
for (size_t i = 0; i < layers_count; ++i) {
const auto* layer = layers[i];
FlutterBackingStore* backing_store = const_cast<FlutterBackingStore*>(layer->backing_store);
@@ -94,8 +97,7 @@ bool FlutterMetalCompositor::Present(const FlutterLayer** layers, size_t layers_
break;
}
case kFlutterLayerContentTypePlatformView:
// Add functionality in follow up PR.
FML_LOG(WARNING) << "Presenting PlatformViews not yet supported";
PresentPlatformView(layer, i);
break;
};
}
@@ -103,4 +105,24 @@ bool FlutterMetalCompositor::Present(const FlutterLayer** layers, size_t layers_
return EndFrame(has_flutter_content);
}
void FlutterMetalCompositor::PresentPlatformView(const FlutterLayer* layer, size_t layer_position) {
// TODO (https://github.com/flutter/flutter/issues/96668)
// once the issue is fixed, this check will pass.
FML_DCHECK([[NSThread currentThread] isMainThread])
<< "Must be on the main thread to present platform views";
int64_t platform_view_id = layer->platform_view->identifier;
NSView* platform_view = [platform_views_controller_ platformViewWithID:platform_view_id];
FML_DCHECK(platform_view) << "Platform view not found for id: " << platform_view_id;
CGFloat scale = [[NSScreen mainScreen] backingScaleFactor];
platform_view.frame = CGRectMake(layer->offset.x / scale, layer->offset.y / scale,
layer->size.width / scale, layer->size.height / scale);
if (platform_view.superview == nil) {
[view_controller_.flutterView addSubview:platform_view];
}
platform_view.layer.zPosition = layer_position;
}
} // namespace flutter

View File

@@ -14,7 +14,8 @@ TEST(FlutterMetalCompositorTest, TestPresent) {
id mockViewController = CreateMockViewController(nil);
std::unique_ptr<flutter::FlutterMetalCompositor> macos_compositor =
std::make_unique<FlutterMetalCompositor>(mockViewController, nullptr);
std::make_unique<FlutterMetalCompositor>(
mockViewController, /*platform_view_controller*/ nullptr, /*mtl_device*/ nullptr);
bool flag = false;
macos_compositor->SetPresentCallback([f = &flag](bool has_flutter_content) {
@@ -31,7 +32,8 @@ TEST(FlutterMetalCompositorTest, TestCreate) {
[mockViewController loadView];
std::unique_ptr<flutter::FlutterMetalCompositor> macos_compositor =
std::make_unique<FlutterMetalCompositor>(mockViewController, nullptr);
std::make_unique<FlutterMetalCompositor>(
mockViewController, /*platform_view_controller*/ nullptr, /*mtl_device*/ nullptr);
FlutterBackingStore backing_store;
FlutterBackingStoreConfig config;
@@ -52,7 +54,8 @@ TEST(FlutterMetalCompositorTest, TestCompositing) {
[mockViewController loadView];
std::unique_ptr<flutter::FlutterMetalCompositor> macos_compositor =
std::make_unique<FlutterMetalCompositor>(mockViewController, nullptr);
std::make_unique<FlutterMetalCompositor>(
mockViewController, /*platform_view_controller*/ nullptr, /*mtl_device*/ nullptr);
FlutterBackingStore backing_store;
FlutterBackingStoreConfig config;

View File

@@ -0,0 +1,56 @@
// 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 <Cocoa/Cocoa.h>
#import "FlutterChannels.h"
#import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterPlatformViews.h"
#include <map>
#include <unordered_set>
@interface FlutterPlatformViewController : NSViewController
@end
@interface FlutterPlatformViewController ()
/**
* Creates a platform view of viewType with viewId.
* FlutterResult is updated to contain nil for success or to contain
* a FlutterError if there is an error.
*/
- (void)onCreateWithViewID:(int64_t)viewId
viewType:(nonnull NSString*)viewType
result:(nonnull FlutterResult)result;
/**
* Disposes the platform view with `viewId`.
* FlutterResult is updated to contain nil for success or a FlutterError if there is an error.
*/
- (void)onDisposeWithViewID:(int64_t)viewId result:(nonnull FlutterResult)result;
/**
* Returns the platform view associated with the viewId.
*/
- (nullable NSView*)platformViewWithID:(int64_t)viewId;
/**
* Register a view factory by adding an entry into the platformViewFactories map with key factoryId
* and value factory.
*/
- (void)registerViewFactory:(nonnull NSObject<FlutterPlatformViewFactory>*)factory
withId:(nonnull NSString*)factoryId;
/**
* Handles platform view related method calls, for example create, dispose, etc.
*/
- (void)handleMethodCall:(nonnull FlutterMethodCall*)call result:(nonnull FlutterResult)result;
/**
* Removes platform views slated to be disposed via method handler calls.
*/
- (void)disposePlatformViews;
@end

View File

@@ -0,0 +1,114 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/fml/logging.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.h"
@implementation FlutterPlatformViewController {
// NSDictionary maps platform view type identifiers to FlutterPlatformViewFactories.
NSMutableDictionary<NSString*, NSObject<FlutterPlatformViewFactory>*>* _platformViewFactories;
// Map from platform view id to the underlying NSView.
std::map<int, NSView*> _platformViews;
// View ids that are going to be disposed on the next present call.
std::unordered_set<int64_t> _platformViewsToDispose;
}
- (instancetype)init {
self = [super init];
if (self) {
_platformViewFactories = [[NSMutableDictionary alloc] init];
}
return self;
}
- (void)onCreateWithViewID:(int64_t)viewId
viewType:(nonnull NSString*)viewType
result:(nonnull FlutterResult)result {
if (_platformViews.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 = _platformViewFactories[viewType];
if (!factory) {
result([FlutterError
errorWithCode:@"unregistered_view_type"
message:@"trying to create a view with an unregistered type"
details:[NSString stringWithFormat:@"unregistered view type: '%@'", viewType]]);
return;
}
NSView* platform_view = [factory createWithviewIdentifier:viewId arguments:nil];
_platformViews[viewId] = platform_view;
result(nil);
}
- (void)onDisposeWithViewID:(int64_t)viewId result:(nonnull FlutterResult)result {
if (_platformViews.count(viewId) == 0) {
result([FlutterError errorWithCode:@"unknown_view"
message:@"trying to dispose an unknown"
details:[NSString stringWithFormat:@"view id: '%lld'", viewId]]);
return;
}
// The following disposePlatformViews call will dispose the views.
_platformViewsToDispose.insert(viewId);
result(nil);
}
- (void)registerViewFactory:(nonnull NSObject<FlutterPlatformViewFactory>*)factory
withId:(nonnull NSString*)factoryId {
_platformViewFactories[factoryId] = factory;
}
- (nullable NSView*)platformViewWithID:(int64_t)viewId {
if (_platformViews.count(viewId)) {
return _platformViews[viewId];
} else {
return nil;
}
}
- (void)handleMethodCall:(nonnull FlutterMethodCall*)call result:(nonnull FlutterResult)result {
if ([[call method] isEqualToString:@"create"]) {
NSMutableDictionary<NSString*, id>* args = [call arguments];
if ([args objectForKey:@"id"]) {
int64_t viewId = [args[@"id"] longValue];
NSString* viewType = [NSString stringWithUTF8String:([args[@"viewType"] UTF8String])];
[self onCreateWithViewID:viewId viewType:viewType result:result];
} else {
result([FlutterError errorWithCode:@"unknown_view"
message:@"'id' argument must be passed to create a platform view."
details:[NSString stringWithFormat:@"'id' not specified."]]);
}
} else if ([[call method] isEqualToString:@"dispose"]) {
NSNumber* arg = [call arguments];
int64_t viewId = [arg longValue];
[self onDisposeWithViewID:viewId result:result];
} else {
result(FlutterMethodNotImplemented);
}
}
- (void)disposePlatformViews {
if (_platformViewsToDispose.empty()) {
return;
}
FML_DCHECK([[NSThread currentThread] isMainThread])
<< "Must be on the main thread to handle disposing platform views";
for (int64_t viewId : _platformViewsToDispose) {
NSView* view = _platformViews[viewId];
[view removeFromSuperview];
_platformViews.erase(viewId);
}
_platformViewsToDispose.clear();
}
@end

View File

@@ -0,0 +1,128 @@
// 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 "flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.h"
#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/TestFlutterPlatformView.h"
#include "flutter/testing/testing.h"
namespace flutter::testing {
TEST(FlutterPlatformViewController, TestCreatePlatformViewNoMatchingViewType) {
// Use id so we can access handleMethodCall method.
id platformViewController = [[FlutterPlatformViewController alloc] init];
FlutterMethodCall* methodCall =
[FlutterMethodCall methodCallWithMethodName:@"create"
arguments:@{
@"id" : @2,
@"viewType" : @"FlutterPlatformViewMock"
}];
__block bool errored = false;
FlutterResult result = ^(id result) {
if ([result isKindOfClass:[FlutterError class]]) {
errored = true;
}
};
[platformViewController handleMethodCall:methodCall result:result];
// We expect the call to error since no factories are registered.
EXPECT_TRUE(errored);
}
TEST(FlutterPlatformViewController, TestRegisterPlatformViewFactoryAndCreate) {
// Use id so we can access handleMethodCall method.
id platformViewController = [[FlutterPlatformViewController alloc] init];
TestFlutterPlatformViewFactory* factory = [TestFlutterPlatformViewFactory alloc];
[platformViewController registerViewFactory:factory withId:@"MockPlatformView"];
FlutterMethodCall* methodCall =
[FlutterMethodCall methodCallWithMethodName:@"create"
arguments:@{
@"id" : @2,
@"viewType" : @"MockPlatformView"
}];
__block bool success = false;
FlutterResult result = ^(id result) {
// If a platform view is successfully created, the result is nil.
if (result == nil) {
success = true;
}
};
[platformViewController handleMethodCall:methodCall result:result];
EXPECT_TRUE(success);
}
TEST(FlutterPlatformViewController, TestCreateAndDispose) {
// Use id so we can access handleMethodCall method.
id platformViewController = [[FlutterPlatformViewController alloc] init];
TestFlutterPlatformViewFactory* factory = [TestFlutterPlatformViewFactory alloc];
[platformViewController registerViewFactory:factory withId:@"MockPlatformView"];
FlutterMethodCall* methodCallOnCreate =
[FlutterMethodCall methodCallWithMethodName:@"create"
arguments:@{
@"id" : @2,
@"viewType" : @"MockPlatformView"
}];
__block bool created = false;
FlutterResult resultOnCreate = ^(id result) {
// If a platform view is successfully created, the result is nil.
if (result == nil) {
created = true;
}
};
[platformViewController handleMethodCall:methodCallOnCreate result:resultOnCreate];
FlutterMethodCall* methodCallOnDispose =
[FlutterMethodCall methodCallWithMethodName:@"dispose"
arguments:[NSNumber numberWithLongLong:2]];
__block bool disposed = false;
FlutterResult resultOnDispose = ^(id result) {
// If a platform view is successfully created, the result is nil.
if (result == nil) {
disposed = true;
}
};
[platformViewController handleMethodCall:methodCallOnDispose result:resultOnDispose];
EXPECT_TRUE(created);
EXPECT_TRUE(disposed);
}
TEST(FlutterPlatformViewController, TestDisposeOnMissingViewId) {
// Use id so we can access handleMethodCall method.
id platformViewController = [[FlutterPlatformViewController alloc] init];
FlutterMethodCall* methodCall =
[FlutterMethodCall methodCallWithMethodName:@"dispose"
arguments:[NSNumber numberWithLongLong:20]];
__block bool errored = false;
FlutterResult result = ^(id result) {
if ([result isKindOfClass:[FlutterError class]]) {
errored = true;
}
};
[platformViewController handleMethodCall:methodCall result:result];
EXPECT_TRUE(errored);
}
} // namespace flutter::testing

View File

@@ -0,0 +1,13 @@
// 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 <Foundation/Foundation.h>
#import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterEngine.h"
@interface TestFlutterPlatformView : NSView
@end
@interface TestFlutterPlatformViewFactory : NSObject <FlutterPlatformViewFactory>
@end

View File

@@ -0,0 +1,28 @@
// 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 <Cocoa/Cocoa.h>
#import <Foundation/Foundation.h>
#import "flutter/shell/platform/darwin/macos/framework/Source/TestFlutterPlatformView.h"
@implementation TestFlutterPlatformView
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
return self;
}
@end
@implementation TestFlutterPlatformViewFactory
- (NSView*)createWithviewIdentifier:(int64_t)viewId arguments:(nullable id)args {
return [[TestFlutterPlatformView alloc] initWithFrame:CGRectZero];
}
- (NSObject<FlutterMessageCodec>*)createArgsCodec {
return [FlutterStandardMessageCodec sharedInstance];
}
@end