From c1fbeacd344753895fc6fb4de3c8b1daa76bc0d5 Mon Sep 17 00:00:00 2001 From: richardjcai Date: Fri, 4 Dec 2020 23:24:11 -0500 Subject: [PATCH 1/5] Add MacOS platform view support to FlutterViewController Add platform view support in FlutterGLCompositor and FlutterViewController --- ci/licenses_golden/licenses_flutter | 1 + shell/platform/darwin/macos/BUILD.gn | 1 + .../framework/Source/FlutterGLCompositor.h | 9 +++ .../framework/Source/FlutterGLCompositor.mm | 71 ++++++++++++++----- .../framework/Source/FlutterPlatformViews.h | 62 ++++++++++++++++ .../framework/Source/FlutterViewController.mm | 68 ++++++++++++++++++ .../Source/FlutterViewController_Internal.h | 50 ++++++++++++- 7 files changed, 243 insertions(+), 19 deletions(-) create mode 100644 shell/platform/darwin/macos/framework/Source/FlutterPlatformViews.h diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 9c211f9916270..7ecb73163b7b9 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1094,6 +1094,7 @@ FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterMouse FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterOpenGLRenderer.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterOpenGLRenderer.mm FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterOpenGLRendererTest.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViews.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterRenderer.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterResizableBackingStoreProvider.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterResizableBackingStoreProvider.mm diff --git a/shell/platform/darwin/macos/BUILD.gn b/shell/platform/darwin/macos/BUILD.gn index 4085e39f73e9f..bd8d06792806c 100644 --- a/shell/platform/darwin/macos/BUILD.gn +++ b/shell/platform/darwin/macos/BUILD.gn @@ -77,6 +77,7 @@ source_set("flutter_framework_source") { "framework/Source/FlutterMouseCursorPlugin.mm", "framework/Source/FlutterOpenGLRenderer.h", "framework/Source/FlutterOpenGLRenderer.mm", + "framework/Source/FlutterPlatformViews.h", "framework/Source/FlutterRenderer.h", "framework/Source/FlutterResizableBackingStoreProvider.h", "framework/Source/FlutterResizableBackingStoreProvider.mm", diff --git a/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.h b/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.h index 8f0be73999a08..c7563a977a393 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.h @@ -5,6 +5,7 @@ #include #include "flutter/fml/macros.h" +#include "flutter/shell/platform/darwin/macos/framework/Source/FlutterBackingStoreData.h" #include "flutter/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.h" #include "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h" #include "flutter/shell/platform/embedder/embedder.h" @@ -67,9 +68,17 @@ class FlutterGLCompositor { // created for the frame. bool frame_started_ = false; + // Update the backing CALayer using the backing store's specifications. + void PresentBackingStoreContent( + FlutterBackingStoreData* flutter_backing_store_data, + size_t layer_position); + // Set frame_started_ to true and reset all layer state. void StartFrame(); + // Remove platform views that are specified for deletion. + void DisposePlatformViews(); + // Creates a CALayer and adds it to ca_layer_map_ and increments // ca_layer_count_; Returns the key value (size_t) for the layer in // ca_layer_map_. diff --git a/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.mm b/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.mm index 125b90bc07e74..5629e039473c1 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.mm @@ -75,35 +75,31 @@ } bool FlutterGLCompositor::Present(const FlutterLayer** layers, size_t layers_count) { + DisposePlatformViews(); for (size_t i = 0; i < layers_count; ++i) { const auto* layer = layers[i]; FlutterBackingStore* backing_store = const_cast(layer->backing_store); switch (layer->type) { case kFlutterLayerContentTypeBackingStore: { if (backing_store->open_gl.framebuffer.user_data) { - FlutterBackingStoreData* backing_store_data = + FlutterBackingStoreData* flutter_backing_store_data = (__bridge FlutterBackingStoreData*)backing_store->open_gl.framebuffer.user_data; - - FlutterIOSurfaceHolder* io_surface_holder = [backing_store_data ioSurfaceHolder]; - size_t layer_id = [backing_store_data layerId]; - - CALayer* content_layer = ca_layer_map_[layer_id]; - - FML_CHECK(content_layer) << "Unable to find a content layer with layer id " << layer_id; - - content_layer.frame = content_layer.superlayer.bounds; - - // The surface is an OpenGL texture, which means it has origin in bottom left corner - // and needs to be flipped vertically - content_layer.transform = CATransform3DMakeScale(1, -1, 1); - IOSurfaceRef io_surface_contents = [io_surface_holder ioSurface]; - [content_layer setContents:(__bridge id)io_surface_contents]; + PresentBackingStoreContent(flutter_backing_store_data, i); } break; } case kFlutterLayerContentTypePlatformView: - // Add functionality in follow up PR. - FML_LOG(WARNING) << "Presenting PlatformViews not yet supported"; + FML_CHECK([[NSThread currentThread] isMainThread]) + << "Must be on the main thread to handle presenting platform views"; + NSView* platform_view = view_controller_.platformViews[layer->platform_view->identifier]; + 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]; + } else { + platform_view.layer.zPosition = i; + } break; }; } @@ -113,6 +109,29 @@ return present_callback_(); } +void FlutterGLCompositor::PresentBackingStoreContent( + FlutterBackingStoreData* flutter_backing_store_data, + size_t layer_position) { + FML_CHECK([[NSThread currentThread] isMainThread]) + << "Must be on the main thread to update CALayer contents"; + + FlutterIOSurfaceHolder* io_surface_holder = [flutter_backing_store_data ioSurfaceHolder]; + size_t layer_id = [flutter_backing_store_data layerId]; + + CALayer* content_layer = ca_layer_map_[layer_id]; + + FML_CHECK(content_layer) << "Unable to find a content layer with layer id " << layer_id; + + content_layer.frame = content_layer.superlayer.bounds; + content_layer.zPosition = layer_position; + + // The surface is an OpenGL texture, which means it has origin in bottom left corner + // and needs to be flipped vertically + content_layer.transform = CATransform3DMakeScale(1, -1, 1); + IOSurfaceRef io_surface_contents = [io_surface_holder ioSurface]; + [content_layer setContents:(__bridge id)io_surface_contents]; +} + void FlutterGLCompositor::SetPresentCallback( const FlutterGLCompositor::PresentCallback& present_callback) { present_callback_ = present_callback; @@ -143,4 +162,20 @@ return ca_layer_count_++; } +void FlutterGLCompositor::DisposePlatformViews() { + auto views_to_dispose = view_controller_.platformViewsToDispose; + if (views_to_dispose.empty()) { + return; + } + + for (int64_t viewId : views_to_dispose) { + FML_CHECK([[NSThread currentThread] isMainThread]) + << "Must be on the main thread to handle disposing platform views"; + NSView* view = view_controller_.platformViews[viewId]; + [view removeFromSuperview]; + view_controller_.platformViews.erase(viewId); + } + views_to_dispose.clear(); +} + } // namespace flutter diff --git a/shell/platform/darwin/macos/framework/Source/FlutterPlatformViews.h b/shell/platform/darwin/macos/framework/Source/FlutterPlatformViews.h new file mode 100644 index 0000000000000..d0b415c62876f --- /dev/null +++ b/shell/platform/darwin/macos/framework/Source/FlutterPlatformViews.h @@ -0,0 +1,62 @@ +// 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 + +#import "FlutterCodecs.h" +#import "FlutterMacros.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * Wraps a `NSView` for embedding in the Flutter hierarchy + */ +@protocol FlutterPlatformView +/** + * Returns a reference to the `NSView` that is wrapped by this `FlutterPlatformView`. + * + * It is recommended to return a cached view instance in this method. + * Constructing and returning a new NSView instance in this method might cause undefined behavior. + * + * TODO(richardjcai): Prevent [FlutterPlatformView view] to be called multiple times + * in a single frame. + */ +- (NSView*)view; +@end + +FLUTTER_EXPORT +@protocol FlutterPlatformViewFactory +/** + * Create a `FlutterPlatformView`. + * + * Implemented by MacOS code that expose a `FlutterPlatformView` for embedding in a Flutter app. + * + * The implementation of this method should create a new `FlutterPlatformView` and return it. + * + * @param frame The rectangle for the newly created `FlutterPlatformView` measured in points. + * @param viewId A unique identifier for this `FlutterPlatformView`. + * @param args Parameters for creating the `FlutterPlatformView` 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`. + */ +- (NSObject*)createWithFrame:(CGRect)frame + viewIdentifier:(int64_t)viewId + arguments:(id _Nullable)args; + +/** + * Returns the `FlutterMessageCodec` for decoding the args parameter of `createWithFrame`. + * + * Only needs to be implemented if `createWithFrame` needs an arguments parameter. + */ +@optional +- (NSObject*)createArgsCodec; +@end + +NS_ASSUME_NONNULL_END + +#endif // FLUTTER_FLUTTERPLATFORMVIEWS_H_ diff --git a/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm b/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm index 08859294ced61..03686443b5b19 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm @@ -213,6 +213,9 @@ @implementation FlutterViewController { // A method channel for miscellaneous platform functionality. FlutterMethodChannel* _platformChannel; + + // A method channel for platform view functionality. + FlutterMethodChannel* _platformViewsChannel; } @dynamic view; @@ -228,6 +231,7 @@ static void CommonInit(FlutterViewController* controller) { } controller->_additionalKeyResponders = [[NSMutableOrderedSet alloc] init]; controller->_mouseTrackingMode = FlutterMouseTrackingModeInKeyWindow; + controller->_factories = [[NSMutableDictionary alloc] init]; } - (instancetype)initWithCoder:(NSCoder*)coder { @@ -435,10 +439,70 @@ - (void)addInternalPlugins { [FlutterMethodChannel methodChannelWithName:@"flutter/platform" binaryMessenger:_engine.binaryMessenger codec:[FlutterJSONMethodCodec sharedInstance]]; + + _platformViewsChannel = + [FlutterMethodChannel methodChannelWithName:@"flutter/platform_views" + binaryMessenger:_engine.binaryMessenger + codec:[FlutterStandardMethodCodec sharedInstance]]; + __weak FlutterViewController* weakSelf = self; [_platformChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { [weakSelf handleMethodCall:call result:result]; }]; + + [_platformViewsChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { + [weakSelf handleMethodCall:call result:result]; + }]; +} + +- (void)onCreate:(nonnull FlutterMethodCall*)call result:(nonnull FlutterResult)result { + NSMutableDictionary* args = [call arguments]; + int64_t viewId = [args[@"id"] longValue]; + NSString* viewType = [NSString stringWithUTF8String:([args[@"viewType"] UTF8String])]; + + 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]]); + } + + NSObject* factory = _factories[viewType]; + if (factory == nil) { + result([FlutterError errorWithCode:@"unregistered_view_type" + message:@"trying to create a view with an unregistered type" + details:[NSString stringWithFormat:@"unregistered view type: '%@'", + args[@"viewType"]]]); + return; + } + + NSObject* platform_view = [factory createWithFrame:CGRectZero + viewIdentifier:viewId + arguments:nil]; + + _platformViews[viewId] = [platform_view view]; + result(nil); +} + +- (void)onDispose:(nonnull FlutterMethodCall*)call result:(nonnull FlutterResult)result { + NSNumber* arg = [call arguments]; + int64_t viewId = [arg longLongValue]; + NSLog(@"onDispose ViewId: %lld", viewId); + + 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 FlutterGLCompositor::Present call will dispose the views. + _platformViewsToDispose.insert(viewId); + result(nil); +} + +- (void)registerViewFactory:(nonnull NSObject*)factory + withId:(nonnull NSString*)factoryId { + _factories[factoryId] = factory; } - (void)dispatchMouseEvent:(nonnull NSEvent*)event { @@ -614,6 +678,10 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { result(nil); } else if ([call.method isEqualToString:@"Clipboard.hasStrings"]) { result(@{@"value" : @([self clipboardHasStrings])}); + } else if ([[call method] isEqualToString:@"create"]) { + [self onCreate:call result:result]; + } else if ([[call method] isEqualToString:@"dispose"]) { + [self onDispose:call result:result]; } else { result(FlutterMethodNotImplemented); } diff --git a/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h b/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h index acef9f23bae5e..deadb8f4f0247 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h @@ -2,9 +2,12 @@ // 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/Headers/FlutterViewController.h" +#include +#include +#import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterViewController.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterIntermediateKeyResponder.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViews.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterView.h" @interface FlutterViewController () @@ -12,14 +15,59 @@ // The FlutterView for this view controller. @property(nonatomic, readonly, nullable) FlutterView* flutterView; +// NSDictionary maps strings to FlutterPlatformViewFactorys. +@property(nonnull, nonatomic) + NSMutableDictionary*>* factories; + +// A map of platform view ids to views. +@property(nonatomic) std::map platformViews; + +// View ids that are going to be disposed on the next present call. +@property(nonatomic) std::unordered_set platformViewsToDispose; + /** * This just returns the NSPasteboard so that it can be mocked in the tests. */ @property(nonatomic, readonly, nonnull) NSPasteboard* pasteboard; /** +<<<<<<< HEAD * Adds an intermediate responder for keyboard events. Key up and key down events are forwarded to * all added responders, and they either handle the keys or not. +======= + * Platform View Methods. + */ + +/** + * Creates a platform view using the arguments from the provided call. + * The call's arguments should be castable to an NSMutableDictionary* + * and the dictionary should at least hold one key for "id" that maps to the view id and + * one key for "viewType" which maps to the view type (string) that was used to register + * the factory. + * FlutterResult is updated to contain nil for success or to contain + * a FlutterError if there is an error. + */ +- (void)onCreate:(nonnull FlutterMethodCall*)call result:(nonnull FlutterResult)result; + +/** + * Disposes a platform view using the arguments from the provided call. + * The call's arguments should be the Id (castable to NSNumber*) of the platform view + * that should be disposed. + * FlutterResult is updated to contain nil for success or a FlutterError if there is an error. + */ +- (void)onDispose:(nonnull FlutterMethodCall*)call result:(nonnull FlutterResult)result; + +/** + * Register a view factory by adding an entry into the factories_ map with key factoryId + * and value factory. + */ +- (void)registerViewFactory:(nonnull NSObject*)factory + withId:(nonnull NSString*)factoryId; + +/** + * Adds a responder for keyboard events. Key up and key down events are forwarded to all added + * responders. +>>>>>>> 182b97516 (Add MacOS platform view support to FlutterViewController) */ - (void)addKeyResponder:(nonnull FlutterIntermediateKeyResponder*)responder; From f049b8b78d4e3a9b0f0f7413cce82f4a10e6244b Mon Sep 17 00:00:00 2001 From: richardjcai Date: Mon, 7 Dec 2020 19:29:19 -0500 Subject: [PATCH 2/5] Create FlutterPlatformViewController --- ci/licenses_golden/licenses_flutter | 2 + shell/platform/darwin/macos/BUILD.gn | 2 + .../macos/framework/Source/FlutterEngine.mm | 80 ++++++++++++++++ .../framework/Source/FlutterGLCompositor.h | 5 +- .../framework/Source/FlutterGLCompositor.mm | 36 +++----- .../Source/FlutterGLCompositorUnittests.mm | 5 +- .../Source/FlutterPlatformViewController.mm | 92 +++++++++++++++++++ .../FlutterPlatformViewController_Internal.h | 58 ++++++++++++ .../framework/Source/FlutterViewController.mm | 68 -------------- .../Source/FlutterViewController_Internal.h | 19 +--- 10 files changed, 256 insertions(+), 111 deletions(-) create mode 100644 shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.mm create mode 100644 shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController_Internal.h diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 7ecb73163b7b9..17cf9cd5f2a06 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1094,6 +1094,8 @@ FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterMouse FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterOpenGLRenderer.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterOpenGLRenderer.mm FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterOpenGLRendererTest.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/FlutterPlatformViews.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterRenderer.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterResizableBackingStoreProvider.h diff --git a/shell/platform/darwin/macos/BUILD.gn b/shell/platform/darwin/macos/BUILD.gn index bd8d06792806c..e60b8f5cb642d 100644 --- a/shell/platform/darwin/macos/BUILD.gn +++ b/shell/platform/darwin/macos/BUILD.gn @@ -77,6 +77,8 @@ source_set("flutter_framework_source") { "framework/Source/FlutterMouseCursorPlugin.mm", "framework/Source/FlutterOpenGLRenderer.h", "framework/Source/FlutterOpenGLRenderer.mm", + "framework/Source/FlutterPlatformViewController.mm", + "framework/Source/FlutterPlatformViewController_Internal.h", "framework/Source/FlutterPlatformViews.h", "framework/Source/FlutterRenderer.h", "framework/Source/FlutterResizableBackingStoreProvider.h", diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm index 626197ed1ec9e..9c580cb63bcaa 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm @@ -14,6 +14,7 @@ #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/FlutterViewController_Internal.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController_Internal.h" #import "flutter/shell/platform/embedder/embedder.h" /** @@ -141,6 +142,13 @@ @implementation FlutterEngine { // FlutterCompositor is copied and used in embedder.cc. FlutterCompositor _compositor; + + // A method channel for platform view functionality. + FlutterMethodChannel* _platformViewsChannel; + + // Used to support creation and deletion of platform views and + // registering platform view factories. + FlutterPlatformViewController* _platformViewController; } - (instancetype)initWithName:(NSString*)labelPrefix project:(FlutterDartProject*)project { @@ -248,6 +256,9 @@ - (BOOL)runWithEntrypoint:(NSString*)entrypoint { flutterArguments.aot_data = _aotData; } + [self setupPlatformViewChannel]; + [self createPlatformViewController]; + flutterArguments.compositor = [self createFlutterCompositor]; FlutterRendererConfig rendererConfig = [_renderer createRendererConfig]; @@ -428,6 +439,10 @@ - (void)sendPointerEvent:(const FlutterPointerEvent&)event { _embedderAPI.SendPointerEvent(_engine, &event, 1); } +- (FlutterPlatformViewController*)platformViewController { + return _platformViewController; +} + #pragma mark - Private methods - (void)sendUserLocales { @@ -504,6 +519,71 @@ - (void)shutDownEngine { _engine = nullptr; } +- (FlutterCompositor*)createFlutterCompositor { + // TODO(richardjcai): Add support for creating a FlutterGLCompositor + // with a nil _viewController for headless engines. + // https://github.com/flutter/flutter/issues/71606 + if (_viewController == nullptr) { + return nullptr; + } + + [_mainOpenGLContext makeCurrentContext]; + + _macOSGLCompositor = + std::make_unique(_viewController, _platformViewController); + + _compositor = {}; + _compositor.struct_size = sizeof(FlutterCompositor); + _compositor.user_data = _macOSGLCompositor.get(); + + _compositor.create_backing_store_callback = [](const FlutterBackingStoreConfig* config, // + FlutterBackingStore* backing_store_out, // + void* user_data // + ) { + return reinterpret_cast(user_data)->CreateBackingStore( + config, backing_store_out); + }; + + _compositor.collect_backing_store_callback = [](const FlutterBackingStore* backing_store, // + void* user_data // + ) { + return reinterpret_cast(user_data)->CollectBackingStore( + backing_store); + }; + + _compositor.present_layers_callback = [](const FlutterLayer** layers, // + size_t layers_count, // + void* user_data // + ) { + return reinterpret_cast(user_data)->Present(layers, + layers_count); + }; + + __weak FlutterEngine* weak_self = self; + _macOSGLCompositor->SetPresentCallback( + [weak_self]() { return [weak_self engineCallbackOnPresent]; }); + + _compositor.avoid_backing_store_cache = true; + + return &_compositor; +} + +- (void)createPlatformViewController { + _platformViewController = [[FlutterPlatformViewController alloc] init]; +} + +- (void)setupPlatformViewChannel { + _platformViewsChannel = + [FlutterMethodChannel methodChannelWithName:@"flutter/platform_views" + binaryMessenger:self.binaryMessenger + codec:[FlutterStandardMethodCodec sharedInstance]]; + + __weak FlutterEngine* weak_self = self; + [_platformViewsChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { + [[weak_self platformViewController] handleMethodCall:call result:result]; + }]; +} + #pragma mark - FlutterBinaryMessenger - (void)sendOnChannel:(nonnull NSString*)channel message:(nullable NSData*)message { diff --git a/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.h b/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.h index c7563a977a393..b4ca146d7374c 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.h @@ -6,6 +6,7 @@ #include "flutter/fml/macros.h" #include "flutter/shell/platform/darwin/macos/framework/Source/FlutterBackingStoreData.h" +#include "flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController_Internal.h" #include "flutter/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.h" #include "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h" #include "flutter/shell/platform/embedder/embedder.h" @@ -51,6 +52,7 @@ class FlutterGLCompositor { private: const FlutterViewController* view_controller_; + const FlutterPlatformViewController* platform_view_controller_; const NSOpenGLContext* open_gl_context_; PresentCallback present_callback_; @@ -76,9 +78,6 @@ class FlutterGLCompositor { // Set frame_started_ to true and reset all layer state. void StartFrame(); - // Remove platform views that are specified for deletion. - void DisposePlatformViews(); - // Creates a CALayer and adds it to ca_layer_map_ and increments // ca_layer_count_; Returns the key value (size_t) for the layer in // ca_layer_map_. diff --git a/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.mm b/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.mm index 5629e039473c1..a17fd975b1997 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.mm @@ -23,7 +23,11 @@ NSOpenGLContext* opengl_context) : open_gl_context_(opengl_context) { FML_CHECK(view_controller != nullptr) << "FlutterViewController* cannot be nullptr"; + FML_CHECK(platform_view_controller != nullptr) + << "FlutterPlatformViewController* cannot be nullptr"; + view_controller_ = view_controller; + platform_view_controller_ = platform_view_controller; } bool FlutterGLCompositor::CreateBackingStore(const FlutterBackingStoreConfig* config, @@ -75,7 +79,7 @@ } bool FlutterGLCompositor::Present(const FlutterLayer** layers, size_t layers_count) { - DisposePlatformViews(); + [platform_view_controller_ disposePlatformViews]; for (size_t i = 0; i < layers_count; ++i) { const auto* layer = layers[i]; FlutterBackingStore* backing_store = const_cast(layer->backing_store); @@ -89,9 +93,15 @@ break; } case kFlutterLayerContentTypePlatformView: - FML_CHECK([[NSThread currentThread] isMainThread]) + FML_DCHECK([[NSThread currentThread] isMainThread]) << "Must be on the main thread to handle presenting platform views"; - NSView* platform_view = view_controller_.platformViews[layer->platform_view->identifier]; + + FML_DCHECK(platform_view_controller_.platformViews.count(layer->platform_view->identifier)) + << "Platform view not found for id: " << layer->platform_view->identifier; + + NSView* platform_view = + platform_view_controller_.platformViews[layer->platform_view->identifier]; + 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); @@ -112,7 +122,7 @@ void FlutterGLCompositor::PresentBackingStoreContent( FlutterBackingStoreData* flutter_backing_store_data, size_t layer_position) { - FML_CHECK([[NSThread currentThread] isMainThread]) + FML_DCHECK([[NSThread currentThread] isMainThread]) << "Must be on the main thread to update CALayer contents"; FlutterIOSurfaceHolder* io_surface_holder = [flutter_backing_store_data ioSurfaceHolder]; @@ -120,7 +130,7 @@ CALayer* content_layer = ca_layer_map_[layer_id]; - FML_CHECK(content_layer) << "Unable to find a content layer with layer id " << layer_id; + FML_DCHECK(content_layer) << "Unable to find a content layer with layer id " << layer_id; content_layer.frame = content_layer.superlayer.bounds; content_layer.zPosition = layer_position; @@ -162,20 +172,4 @@ return ca_layer_count_++; } -void FlutterGLCompositor::DisposePlatformViews() { - auto views_to_dispose = view_controller_.platformViewsToDispose; - if (views_to_dispose.empty()) { - return; - } - - for (int64_t viewId : views_to_dispose) { - FML_CHECK([[NSThread currentThread] isMainThread]) - << "Must be on the main thread to handle disposing platform views"; - NSView* view = view_controller_.platformViews[viewId]; - [view removeFromSuperview]; - view_controller_.platformViews.erase(viewId); - } - views_to_dispose.clear(); -} - } // namespace flutter diff --git a/shell/platform/darwin/macos/framework/Source/FlutterGLCompositorUnittests.mm b/shell/platform/darwin/macos/framework/Source/FlutterGLCompositorUnittests.mm index 25fc35575176f..b783307c7e45b 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterGLCompositorUnittests.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterGLCompositorUnittests.mm @@ -5,6 +5,7 @@ #import #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController_Internal.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTestUtils.h" #import "flutter/testing/testing.h" @@ -12,9 +13,11 @@ TEST(FlutterGLCompositorTest, TestPresent) { id mockViewController = CreateMockViewController(nil); + FlutterPlatformViewController* platformViewController = + [[FlutterPlatformViewController alloc] init]; std::unique_ptr macos_compositor = - std::make_unique(mockViewController, nullptr); + std::make_unique(mockViewController, platformViewController); bool flag = false; macos_compositor->SetPresentCallback([f = &flag]() { diff --git a/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.mm b/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.mm new file mode 100644 index 0000000000000..b25ce75cf81a6 --- /dev/null +++ b/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.mm @@ -0,0 +1,92 @@ +// 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_Internal.h" + +@implementation FlutterPlatformViewController + +- (instancetype)init { + self = [super init]; + + 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]]); + } + + NSObject* factory = _platformViewFactories[viewType]; + if (factory == nil) { + result([FlutterError + errorWithCode:@"unregistered_view_type" + message:@"trying to create a view with an unregistered type" + details:[NSString stringWithFormat:@"unregistered view type: '%@'", viewType]]); + return; + } + + NSObject* platform_view = [factory createWithFrame:CGRectZero + viewIdentifier:viewId + arguments:nil]; + + _platformViews[viewId] = [platform_view 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*)factory + withId:(nonnull NSString*)factoryId { + _platformViewFactories[factoryId] = factory; +} + +- (void)handleMethodCall:(nonnull FlutterMethodCall*)call result:(nonnull FlutterResult)result { + if ([[call method] isEqualToString:@"create"]) { + NSMutableDictionary* args = [call arguments]; + int64_t viewId = [args[@"id"] longValue]; + NSString* viewType = [NSString stringWithUTF8String:([args[@"viewType"] UTF8String])]; + [self onCreateWithViewId:viewId viewType:viewType result:result]; + } else if ([[call method] isEqualToString:@"dispose"]) { + NSNumber* arg = [call arguments]; + int64_t viewId = [arg longLongValue]; + [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 diff --git a/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController_Internal.h b/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController_Internal.h new file mode 100644 index 0000000000000..7e82e0fa3fa4f --- /dev/null +++ b/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController_Internal.h @@ -0,0 +1,58 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import +#import "FlutterChannels.h" + +#include +#include + +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViews.h" + +@interface FlutterPlatformViewController : NSViewController +@end + +@interface FlutterPlatformViewController () + +// NSDictionary maps strings to FlutterPlatformViewFactorys. +@property(nonnull, nonatomic) + NSMutableDictionary*>* platformViewFactories; + +// A map of platform view ids to views. +@property(nonatomic) std::map platformViews; + +// View ids that are going to be disposed on the next present call. +@property(nonatomic) std::unordered_set platformViewsToDispose; + +/** + * 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 id 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; + +/** + * Register a view factory by adding an entry into the platformViewFactories map with key factoryId + * and value factory. + */ +- (void)registerViewFactory:(nonnull NSObject*)factory + withId:(nonnull NSString*)factoryId; + +- (void)handleMethodCall:(nonnull FlutterMethodCall*)call result:(nonnull FlutterResult)result; + +/** + * Remove platform views who's ids are in the platformViewsToDispose set. + * Called before a new frame is presented. + */ +- (void)disposePlatformViews; + +@end diff --git a/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm b/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm index 03686443b5b19..08859294ced61 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm @@ -213,9 +213,6 @@ @implementation FlutterViewController { // A method channel for miscellaneous platform functionality. FlutterMethodChannel* _platformChannel; - - // A method channel for platform view functionality. - FlutterMethodChannel* _platformViewsChannel; } @dynamic view; @@ -231,7 +228,6 @@ static void CommonInit(FlutterViewController* controller) { } controller->_additionalKeyResponders = [[NSMutableOrderedSet alloc] init]; controller->_mouseTrackingMode = FlutterMouseTrackingModeInKeyWindow; - controller->_factories = [[NSMutableDictionary alloc] init]; } - (instancetype)initWithCoder:(NSCoder*)coder { @@ -439,70 +435,10 @@ - (void)addInternalPlugins { [FlutterMethodChannel methodChannelWithName:@"flutter/platform" binaryMessenger:_engine.binaryMessenger codec:[FlutterJSONMethodCodec sharedInstance]]; - - _platformViewsChannel = - [FlutterMethodChannel methodChannelWithName:@"flutter/platform_views" - binaryMessenger:_engine.binaryMessenger - codec:[FlutterStandardMethodCodec sharedInstance]]; - __weak FlutterViewController* weakSelf = self; [_platformChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { [weakSelf handleMethodCall:call result:result]; }]; - - [_platformViewsChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { - [weakSelf handleMethodCall:call result:result]; - }]; -} - -- (void)onCreate:(nonnull FlutterMethodCall*)call result:(nonnull FlutterResult)result { - NSMutableDictionary* args = [call arguments]; - int64_t viewId = [args[@"id"] longValue]; - NSString* viewType = [NSString stringWithUTF8String:([args[@"viewType"] UTF8String])]; - - 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]]); - } - - NSObject* factory = _factories[viewType]; - if (factory == nil) { - result([FlutterError errorWithCode:@"unregistered_view_type" - message:@"trying to create a view with an unregistered type" - details:[NSString stringWithFormat:@"unregistered view type: '%@'", - args[@"viewType"]]]); - return; - } - - NSObject* platform_view = [factory createWithFrame:CGRectZero - viewIdentifier:viewId - arguments:nil]; - - _platformViews[viewId] = [platform_view view]; - result(nil); -} - -- (void)onDispose:(nonnull FlutterMethodCall*)call result:(nonnull FlutterResult)result { - NSNumber* arg = [call arguments]; - int64_t viewId = [arg longLongValue]; - NSLog(@"onDispose ViewId: %lld", viewId); - - 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 FlutterGLCompositor::Present call will dispose the views. - _platformViewsToDispose.insert(viewId); - result(nil); -} - -- (void)registerViewFactory:(nonnull NSObject*)factory - withId:(nonnull NSString*)factoryId { - _factories[factoryId] = factory; } - (void)dispatchMouseEvent:(nonnull NSEvent*)event { @@ -678,10 +614,6 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { result(nil); } else if ([call.method isEqualToString:@"Clipboard.hasStrings"]) { result(@{@"value" : @([self clipboardHasStrings])}); - } else if ([[call method] isEqualToString:@"create"]) { - [self onCreate:call result:result]; - } else if ([[call method] isEqualToString:@"dispose"]) { - [self onDispose:call result:result]; } else { result(FlutterMethodNotImplemented); } diff --git a/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h b/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h index deadb8f4f0247..7745b9e407830 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h @@ -2,9 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include -#include - #import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterViewController.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterIntermediateKeyResponder.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViews.h" @@ -15,26 +12,12 @@ // The FlutterView for this view controller. @property(nonatomic, readonly, nullable) FlutterView* flutterView; -// NSDictionary maps strings to FlutterPlatformViewFactorys. -@property(nonnull, nonatomic) - NSMutableDictionary*>* factories; - -// A map of platform view ids to views. -@property(nonatomic) std::map platformViews; - -// View ids that are going to be disposed on the next present call. -@property(nonatomic) std::unordered_set platformViewsToDispose; - /** * This just returns the NSPasteboard so that it can be mocked in the tests. */ @property(nonatomic, readonly, nonnull) NSPasteboard* pasteboard; /** -<<<<<<< HEAD - * Adds an intermediate responder for keyboard events. Key up and key down events are forwarded to - * all added responders, and they either handle the keys or not. -======= * Platform View Methods. */ @@ -64,10 +47,10 @@ - (void)registerViewFactory:(nonnull NSObject*)factory withId:(nonnull NSString*)factoryId; + /** * Adds a responder for keyboard events. Key up and key down events are forwarded to all added * responders. ->>>>>>> 182b97516 (Add MacOS platform view support to FlutterViewController) */ - (void)addKeyResponder:(nonnull FlutterIntermediateKeyResponder*)responder; From 4b85acfbe25345c138da55ba713da3a57a8a64f1 Mon Sep 17 00:00:00 2001 From: richardjcai Date: Mon, 7 Dec 2020 20:28:00 -0500 Subject: [PATCH 3/5] Adding tests for FlutterPlatformViewController --- ci/licenses_golden/licenses_flutter | 3 + shell/platform/darwin/macos/BUILD.gn | 3 + .../FlutterPlatformViewControllerTest.mm | 128 ++++++++++++++++++ .../Source/FlutterPlatformViewMock.h | 18 +++ .../Source/FlutterPlatformViewMock.mm | 42 ++++++ 5 files changed, 194 insertions(+) create mode 100644 shell/platform/darwin/macos/framework/Source/FlutterPlatformViewControllerTest.mm create mode 100644 shell/platform/darwin/macos/framework/Source/FlutterPlatformViewMock.h create mode 100644 shell/platform/darwin/macos/framework/Source/FlutterPlatformViewMock.mm diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 17cf9cd5f2a06..b92b795fb16aa 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1096,6 +1096,9 @@ FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterOpenG FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterOpenGLRendererTest.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/FlutterPlatformViewMock.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewMock.mm FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViews.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterRenderer.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterResizableBackingStoreProvider.h diff --git a/shell/platform/darwin/macos/BUILD.gn b/shell/platform/darwin/macos/BUILD.gn index e60b8f5cb642d..b9a942a82a869 100644 --- a/shell/platform/darwin/macos/BUILD.gn +++ b/shell/platform/darwin/macos/BUILD.gn @@ -150,6 +150,9 @@ executable("flutter_desktop_darwin_unittests") { sources = [ "framework/Source/FlutterEngineTest.mm", "framework/Source/FlutterGLCompositorUnittests.mm", + "framework/Source/FlutterPlatformViewControllerTest.mm", + "framework/Source/FlutterPlatformViewMock.h", + "framework/Source/FlutterPlatformViewMock.mm", "framework/Source/FlutterViewControllerTest.mm", "framework/Source/FlutterViewControllerTestUtils.h", "framework/Source/FlutterViewControllerTestUtils.mm", diff --git a/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewControllerTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewControllerTest.mm new file mode 100644 index 0000000000000..e20d9d4091522 --- /dev/null +++ b/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewControllerTest.mm @@ -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_Internal.h" + +#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewMock.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]; + + FlutterPlatformViewMockFactory* factory = [FlutterPlatformViewMockFactory 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]; + + FlutterPlatformViewMockFactory* factory = [FlutterPlatformViewMockFactory 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); +} + +} // flutter::testing diff --git a/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewMock.h b/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewMock.h new file mode 100644 index 0000000000000..b1b7e398c5f7e --- /dev/null +++ b/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewMock.h @@ -0,0 +1,18 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import +#import + +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViews.h" + +@interface FlutterPlatformViewMock : NSObject +@property(nonatomic, strong) NSView* view; +@end + +@interface MockPlatformView : NSView +@end + +@interface FlutterPlatformViewMockFactory : NSObject +@end diff --git a/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewMock.mm b/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewMock.mm new file mode 100644 index 0000000000000..9c2b891e794cb --- /dev/null +++ b/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewMock.mm @@ -0,0 +1,42 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import +#import + +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewMock.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViews.h" + +@implementation MockPlatformView + +- (instancetype)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + return self; +} + +@end + +@implementation FlutterPlatformViewMock + +- (instancetype)initWithFrame:(CGRect)frame arguments:(id _Nullable)args { + if (self = [super init]) { + _view = [[MockPlatformView alloc] initWithFrame:frame]; + } + return self; +} + +@end + +@implementation FlutterPlatformViewMockFactory +- (NSObject*)createWithFrame:(CGRect)frame + viewIdentifier:(int64_t)viewId + arguments:(id _Nullable)args { + return [[FlutterPlatformViewMock alloc] initWithFrame:frame arguments:args]; +} + +- (NSObject*)createArgsCodec { + return [FlutterStandardMessageCodec sharedInstance]; +} + +@end From 4dbbff9cb71599731ce43eca6e1134c08a8551ef Mon Sep 17 00:00:00 2001 From: richardjcai Date: Tue, 8 Dec 2020 11:07:52 -0500 Subject: [PATCH 4/5] Update license for FlutterPlatformViewContoller_Internal.h --- ci/licenses_golden/licenses_flutter | 1 + 1 file changed, 1 insertion(+) diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index b92b795fb16aa..743bcefa688c5 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1097,6 +1097,7 @@ FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterOpenG 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/FlutterPlatformViewController_Internal.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewMock.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewMock.mm FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViews.h From b8f5837f51367fc3773fc73753f6c4d4b175f377 Mon Sep 17 00:00:00 2001 From: richardjcai Date: Tue, 8 Dec 2020 16:35:26 -0500 Subject: [PATCH 5/5] Addressing PR comments. Renaming variables, created PresentPlatformView method and updated documentation --- .../macos/framework/Source/FlutterEngine.mm | 32 +++++++++------- .../framework/Source/FlutterGLCompositor.h | 4 ++ .../framework/Source/FlutterGLCompositor.mm | 37 ++++++++++--------- .../Source/FlutterPlatformViewController.mm | 5 ++- .../Source/FlutterPlatformViewMock.h | 1 - 5 files changed, 46 insertions(+), 33 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm index 9c580cb63bcaa..1de1542ff8356 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm @@ -58,6 +58,16 @@ - (void)postMainThreadTask:(FlutterTask)task targetTimeInNanoseconds:(uint64_t)t */ - (void)loadAOTData:(NSString*)assetsDir; +/** + * Creates and returns a FlutterCompositor* to be used by the embedder. + */ +- (FlutterCompositor*)createFlutterCompositor; + +/** + * Create a platform view channel and setup a method call handler. + */ +- (void)setupPlatformViewChannel; + @end #pragma mark - @@ -257,7 +267,7 @@ - (BOOL)runWithEntrypoint:(NSString*)entrypoint { } [self setupPlatformViewChannel]; - [self createPlatformViewController]; + _platformViewController = [[FlutterPlatformViewController alloc] init]; flutterArguments.compositor = [self createFlutterCompositor]; @@ -559,28 +569,24 @@ - (FlutterCompositor*)createFlutterCompositor { layers_count); }; - __weak FlutterEngine* weak_self = self; + __weak FlutterEngine* weakSelf = self; _macOSGLCompositor->SetPresentCallback( - [weak_self]() { return [weak_self engineCallbackOnPresent]; }); + [weakSelf]() { return [weakSelf engineCallbackOnPresent]; }); _compositor.avoid_backing_store_cache = true; return &_compositor; } -- (void)createPlatformViewController { - _platformViewController = [[FlutterPlatformViewController alloc] init]; -} - - (void)setupPlatformViewChannel { _platformViewsChannel = [FlutterMethodChannel methodChannelWithName:@"flutter/platform_views" binaryMessenger:self.binaryMessenger codec:[FlutterStandardMethodCodec sharedInstance]]; - __weak FlutterEngine* weak_self = self; + __weak FlutterEngine* weakSelf = self; [_platformViewsChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { - [[weak_self platformViewController] handleMethodCall:call result:result]; + [[weakSelf platformViewController] handleMethodCall:call result:result]; }]; } @@ -690,11 +696,11 @@ - (BOOL)unregisterTextureWithID:(int64_t)textureID { - (void)postMainThreadTask:(FlutterTask)task targetTimeInNanoseconds:(uint64_t)targetTime { const auto engine_time = _embedderAPI.GetCurrentTime(); - __weak FlutterEngine* weak_self = self; + __weak FlutterEngine* weakSelf = self; auto worker = ^{ - FlutterEngine* strong_self = weak_self; - if (strong_self && strong_self->_engine) { - auto result = _embedderAPI.RunTask(strong_self->_engine, &task); + FlutterEngine* strongSelf = weakSelf; + if (strongSelf && strongSelf->_engine) { + auto result = _embedderAPI.RunTask(strongSelf->_engine, &task); if (result != kSuccess) { NSLog(@"Could not post a task to the Flutter engine."); } diff --git a/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.h b/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.h index b4ca146d7374c..81f7c6e5e8c85 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.h @@ -75,6 +75,10 @@ class FlutterGLCompositor { FlutterBackingStoreData* flutter_backing_store_data, size_t layer_position); + // Add the Platform View's content to the FlutterView at depth + // layer_position. + void PresentPlatformView(const FlutterLayer* layer, size_t layer_position); + // Set frame_started_ to true and reset all layer state. void StartFrame(); diff --git a/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.mm b/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.mm index a17fd975b1997..354c6fc3eedaf 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.mm @@ -93,23 +93,7 @@ break; } case kFlutterLayerContentTypePlatformView: - FML_DCHECK([[NSThread currentThread] isMainThread]) - << "Must be on the main thread to handle presenting platform views"; - - FML_DCHECK(platform_view_controller_.platformViews.count(layer->platform_view->identifier)) - << "Platform view not found for id: " << layer->platform_view->identifier; - - NSView* platform_view = - platform_view_controller_.platformViews[layer->platform_view->identifier]; - - 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]; - } else { - platform_view.layer.zPosition = i; - } + PresentPlatformView(layer, i); break; }; } @@ -142,6 +126,25 @@ [content_layer setContents:(__bridge id)io_surface_contents]; } +void FlutterGLCompositor::PresentPlatformView(const FlutterLayer* layer, size_t layer_position) { + FML_DCHECK([[NSThread currentThread] isMainThread]) + << "Must be on the main thread to handle presenting platform views"; + + FML_DCHECK(platform_view_controller_.platformViews.count(layer->platform_view->identifier)) + << "Platform view not found for id: " << layer->platform_view->identifier; + + NSView* platform_view = platform_view_controller_.platformViews[layer->platform_view->identifier]; + + 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]; + } else { + platform_view.layer.zPosition = layer_position; + } +} + void FlutterGLCompositor::SetPresentCallback( const FlutterGLCompositor::PresentCallback& present_callback) { present_callback_ = present_callback; diff --git a/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.mm b/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.mm index b25ce75cf81a6..c3f995c5ea7c6 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.mm @@ -10,8 +10,9 @@ @implementation FlutterPlatformViewController - (instancetype)init { self = [super init]; - - self->_platformViewFactories = [[NSMutableDictionary alloc] init]; + if (self) { + _platformViewFactories = [[NSMutableDictionary alloc] init]; + } return self; } diff --git a/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewMock.h b/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewMock.h index b1b7e398c5f7e..796e67389b03e 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewMock.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewMock.h @@ -3,7 +3,6 @@ // found in the LICENSE file. #import -#import #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViews.h"