diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 7f906a56127f5..8b80489d5eecf 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -2661,6 +2661,10 @@ FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterViewC FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTestUtils.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTestUtils.mm FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterViewEngineProvider.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterViewEngineProvider.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterViewEngineProviderUnittests.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterViewProvider.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/KeyCodeMap.g.mm FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/KeyCodeMap_Internal.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/MacOSGLContextSwitch.h diff --git a/shell/platform/darwin/macos/BUILD.gn b/shell/platform/darwin/macos/BUILD.gn index d2f5b3df28619..abe1fc3f76cb2 100644 --- a/shell/platform/darwin/macos/BUILD.gn +++ b/shell/platform/darwin/macos/BUILD.gn @@ -121,6 +121,9 @@ source_set("flutter_framework_source") { "framework/Source/FlutterView.mm", "framework/Source/FlutterViewController.mm", "framework/Source/FlutterViewController_Internal.h", + "framework/Source/FlutterViewEngineProvider.h", + "framework/Source/FlutterViewEngineProvider.mm", + "framework/Source/FlutterViewProvider.h", "framework/Source/KeyCodeMap.g.mm", "framework/Source/MacOSGLContextSwitch.h", "framework/Source/MacOSGLContextSwitch.mm", @@ -202,6 +205,7 @@ executable("flutter_desktop_darwin_unittests") { "framework/Source/FlutterViewControllerTest.mm", "framework/Source/FlutterViewControllerTestUtils.h", "framework/Source/FlutterViewControllerTestUtils.mm", + "framework/Source/FlutterViewEngineProviderUnitTests.mm", "framework/Source/TestFlutterPlatformView.h", "framework/Source/TestFlutterPlatformView.mm", ] diff --git a/shell/platform/darwin/macos/framework/Source/FlutterCompositor.h b/shell/platform/darwin/macos/framework/Source/FlutterCompositor.h index 141e27509dd44..498d8c85e378a 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterCompositor.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterCompositor.h @@ -9,7 +9,8 @@ #include #include "flutter/fml/macros.h" -#include "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h" +#include "flutter/shell/platform/darwin/macos/framework/Source/FlutterView.h" +#include "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewProvider.h" #include "flutter/shell/platform/embedder/embedder.h" namespace flutter { @@ -19,7 +20,12 @@ namespace flutter { // Platform views are not yet supported. class FlutterCompositor { public: - explicit FlutterCompositor(FlutterViewController* view_controller); + // Create a FlutterCompositor with a view provider. + // + // The view_provider is used to query FlutterViews from view IDs, + // which are used for presenting and creating backing stores. + // It must not be null, and is typically FlutterViewEngineProvider. + explicit FlutterCompositor(id view_provider); virtual ~FlutterCompositor() = default; @@ -62,7 +68,10 @@ class FlutterCompositor { typedef enum { kStarted, kPresenting, kEnded } FrameStatus; protected: - __weak const FlutterViewController* view_controller_; + // Get the view associated with the view ID. + // + // Returns nil if the ID is invalid. + FlutterView* GetView(uint64_t view_id); // Gets and sets the FrameStatus for the current frame. void SetFrameStatus(FrameStatus frame_status); @@ -76,8 +85,9 @@ class FlutterCompositor { bool EndFrame(bool has_flutter_content); // Creates a CALayer object which is backed by the supplied IOSurface, and - // adds it to the root CALayer for this FlutterViewController's view. + // adds it to the root CALayer for the given view. void InsertCALayerForIOSurface( + FlutterView* view, const IOSurfaceRef& io_surface, CATransform3D transform = CATransform3DIdentity); @@ -85,6 +95,9 @@ class FlutterCompositor { // A list of the active CALayer objects for the frame that need to be removed. std::list active_ca_layers_; + // Where the compositor can query FlutterViews. Must not be null. + id const view_provider_; + // Callback set by the embedder to be called when the layer tree has been // correctly set up for this frame. PresentCallback present_callback_; diff --git a/shell/platform/darwin/macos/framework/Source/FlutterCompositor.mm b/shell/platform/darwin/macos/framework/Source/FlutterCompositor.mm index 3015a716c6ca6..70be1049d0bcd 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterCompositor.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterCompositor.mm @@ -7,10 +7,9 @@ namespace flutter { -FlutterCompositor::FlutterCompositor(FlutterViewController* view_controller) { - FML_CHECK(view_controller != nullptr) << "FlutterViewController* cannot be nullptr"; - - view_controller_ = view_controller; +FlutterCompositor::FlutterCompositor(id view_provider) + : view_provider_(view_provider) { + FML_CHECK(view_provider != nullptr) << "FlutterViewProvider* cannot be nullptr"; } void FlutterCompositor::SetPresentCallback( @@ -35,6 +34,10 @@ return status; } +FlutterView* FlutterCompositor::GetView(uint64_t view_id) { + return [view_provider_ getView:view_id]; +} + void FlutterCompositor::SetFrameStatus(FlutterCompositor::FrameStatus frame_status) { frame_status_ = frame_status; } @@ -43,14 +46,15 @@ return frame_status_; } -void FlutterCompositor::InsertCALayerForIOSurface(const IOSurfaceRef& io_surface, +void FlutterCompositor::InsertCALayerForIOSurface(FlutterView* view, + const IOSurfaceRef& io_surface, CATransform3D transform) { // FlutterCompositor manages the lifecycle of CALayers. CALayer* content_layer = [[CALayer alloc] init]; content_layer.transform = transform; - content_layer.frame = view_controller_.flutterView.layer.bounds; + content_layer.frame = view.layer.bounds; [content_layer setContents:(__bridge id)io_surface]; - [view_controller_.flutterView.layer addSublayer:content_layer]; + [view.layer addSublayer:content_layer]; active_ca_layers_.push_back(content_layer); } diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm index 9c15b52f7d8ff..adffefbd027b0 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm @@ -21,6 +21,7 @@ #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" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewEngineProvider.h" #include "flutter/shell/platform/embedder/embedder.h" /** @@ -211,6 +212,8 @@ @implementation FlutterEngine { // This is either a FlutterGLCompositor or a FlutterMetalCompositor instance. std::unique_ptr _macOSCompositor; + FlutterViewEngineProvider* _viewProvider; + // FlutterCompositor is copied and used in embedder.cc. FlutterCompositor _compositor; @@ -244,6 +247,7 @@ - (instancetype)initWithName:(NSString*)labelPrefix _currentMessengerConnection = 1; _allowHeadlessExecution = allowHeadlessExecution; _semanticsEnabled = NO; + _viewProvider = [[FlutterViewEngineProvider alloc] initWithEngine:self]; _embedderAPI.struct_size = sizeof(FlutterEngineProcTable); FlutterEngineGetProcAddresses(&_embedderAPI); @@ -437,12 +441,12 @@ - (FlutterCompositor*)createFlutterCompositor { if ([FlutterRenderingBackend renderUsingMetal]) { FlutterMetalRenderer* metalRenderer = reinterpret_cast(_renderer); _macOSCompositor = std::make_unique( - _viewController, _platformViewController, metalRenderer.device); + _viewProvider, _platformViewController, metalRenderer.device); } else { FlutterOpenGLRenderer* openGLRenderer = reinterpret_cast(_renderer); [openGLRenderer.openGLContext makeCurrentContext]; - _macOSCompositor = std::make_unique(_viewController, - openGLRenderer.openGLContext); + _macOSCompositor = + std::make_unique(_viewProvider, openGLRenderer.openGLContext); } _macOSCompositor->SetPresentCallback([weakSelf](bool has_flutter_content) { if (has_flutter_content) { diff --git a/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.h b/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.h index 385099a66d1b5..ef704134429d9 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.h @@ -19,7 +19,7 @@ namespace flutter { // FlutterGLCompositor is created and destroyed by FlutterEngine. class FlutterGLCompositor : public FlutterCompositor { public: - FlutterGLCompositor(FlutterViewController* view_controller, + FlutterGLCompositor(id view_provider, NSOpenGLContext* opengl_context); virtual ~FlutterGLCompositor() = default; diff --git a/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.mm b/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.mm index c8c6ce7f93056..8ab6a993f8c86 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.mm @@ -19,13 +19,17 @@ namespace flutter { -FlutterGLCompositor::FlutterGLCompositor(FlutterViewController* view_controller, +FlutterGLCompositor::FlutterGLCompositor(id view_provider, NSOpenGLContext* opengl_context) - : FlutterCompositor(view_controller), open_gl_context_(opengl_context) {} + : FlutterCompositor(view_provider), open_gl_context_(opengl_context) {} bool FlutterGLCompositor::CreateBackingStore(const FlutterBackingStoreConfig* config, FlutterBackingStore* backing_store_out) { - if (!view_controller_) { + // TODO(dkwingsmt): This class only supports single-view for now. As more + // classes are gradually converted to multi-view, it should get the view ID + // from somewhere. + FlutterView* view = GetView(kFlutterDefaultViewId); + if (!view) { return false; } @@ -36,8 +40,7 @@ // If the backing store is for the first layer, return the fbo for the // FlutterView. FlutterOpenGLRenderBackingStore* backingStore = - reinterpret_cast( - [view_controller_.flutterView backingStoreForSize:size]); + reinterpret_cast([view backingStoreForSize:size]); backing_store_out->open_gl.framebuffer.name = backingStore.frameBufferID; } else { FlutterFrameBufferProvider* fb_provider = @@ -75,6 +78,14 @@ } bool FlutterGLCompositor::Present(const FlutterLayer** layers, size_t layers_count) { + // TODO(dkwingsmt): This class only supports single-view for now. As more + // classes are gradually converted to multi-view, it should get the view ID + // from somewhere. + FlutterView* view = GetView(kFlutterDefaultViewId); + if (!view) { + return false; + } + SetFrameStatus(FrameStatus::kPresenting); bool has_flutter_content = false; @@ -93,7 +104,7 @@ // The surface is an OpenGL texture, which means it has origin in bottom left corner // and needs to be flipped vertically - InsertCALayerForIOSurface(io_surface, CATransform3DMakeScale(1, -1, 1)); + InsertCALayerForIOSurface(view, io_surface, CATransform3DMakeScale(1, -1, 1)); } has_flutter_content = true; break; diff --git a/shell/platform/darwin/macos/framework/Source/FlutterGLCompositorUnittests.mm b/shell/platform/darwin/macos/framework/Source/FlutterGLCompositorUnittests.mm index 93e76ea5f251d..82b9a629652db 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterGLCompositorUnittests.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterGLCompositorUnittests.mm @@ -3,18 +3,52 @@ // found in the LICENSE file. #import +#import #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTestUtils.h" #import "flutter/testing/testing.h" +@interface FlutterViewMockProviderGL : NSObject { + FlutterView* _defaultView; +} +/** + * Create a FlutterViewMockProviderGL with the provided view as the default view. + */ +- (nonnull instancetype)initWithDefaultView:(nonnull FlutterView*)view; +@end + +@implementation FlutterViewMockProviderGL + +- (nonnull instancetype)initWithDefaultView:(nonnull FlutterView*)view { + self = [super init]; + if (self != nil) { + _defaultView = view; + } + return self; +} + +- (nullable FlutterView*)getView:(uint64_t)viewId { + if (viewId == kFlutterDefaultViewId) { + return _defaultView; + } + return nil; +} + +@end + namespace flutter::testing { +namespace { -TEST(FlutterGLCompositorTest, TestPresent) { - id mockViewController = CreateMockViewController(); +id MockViewProvider() { + id viewMock = OCMClassMock([FlutterView class]); + return [[FlutterViewMockProviderGL alloc] initWithDefaultView:viewMock]; +} +} // namespace +TEST(FlutterGLCompositorTest, TestPresent) { std::unique_ptr macos_compositor = - std::make_unique(mockViewController, nullptr); + std::make_unique(MockViewProvider(), nullptr); bool flag = false; macos_compositor->SetPresentCallback([f = &flag](bool has_flutter_content) { diff --git a/shell/platform/darwin/macos/framework/Source/FlutterMetalCompositor.h b/shell/platform/darwin/macos/framework/Source/FlutterMetalCompositor.h index 84e67710fc445..9a4d462d81f2c 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterMetalCompositor.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterMetalCompositor.h @@ -14,7 +14,7 @@ namespace flutter { class FlutterMetalCompositor : public FlutterCompositor { public: explicit FlutterMetalCompositor( - FlutterViewController* view_controller, + id view_provider, FlutterPlatformViewController* platform_views_controller, id mtl_device); @@ -46,8 +46,11 @@ class FlutterMetalCompositor : public FlutterCompositor { 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); + // used to position the layer in the z-axis. If the layer does not have a + // superview, it will become subview of `default_base_view`. + void PresentPlatformView(FlutterView* default_base_view, + const FlutterLayer* layer, + size_t layer_position); const id mtl_device_; const FlutterPlatformViewController* platform_views_controller_; diff --git a/shell/platform/darwin/macos/framework/Source/FlutterMetalCompositor.mm b/shell/platform/darwin/macos/framework/Source/FlutterMetalCompositor.mm index e1bcf9bab1f6b..74eae76f8e885 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterMetalCompositor.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterMetalCompositor.mm @@ -11,16 +11,20 @@ namespace flutter { FlutterMetalCompositor::FlutterMetalCompositor( - FlutterViewController* view_controller, + id view_provider, FlutterPlatformViewController* platform_views_controller, id mtl_device) - : FlutterCompositor(view_controller), + : FlutterCompositor(view_provider), mtl_device_(mtl_device), platform_views_controller_(platform_views_controller) {} bool FlutterMetalCompositor::CreateBackingStore(const FlutterBackingStoreConfig* config, FlutterBackingStore* backing_store_out) { - if (!view_controller_) { + // TODO(dkwingsmt): This class only supports single-view for now. As more + // classes are gradually converted to multi-view, it should get the view ID + // from somewhere. + FlutterView* view = GetView(kFlutterDefaultViewId); + if (!view) { return false; } @@ -34,8 +38,7 @@ // If the backing store is for the first layer, return the MTLTexture for the // FlutterView. FlutterMetalRenderBackingStore* backingStore = - reinterpret_cast( - [view_controller_.flutterView backingStoreForSize:size]); + reinterpret_cast([view backingStoreForSize:size]); backing_store_out->metal.texture.texture = (__bridge FlutterMetalTextureHandle)backingStore.texture; } else { @@ -78,6 +81,14 @@ } bool FlutterMetalCompositor::Present(const FlutterLayer** layers, size_t layers_count) { + // TODO(dkwingsmt): This class only supports single-view for now. As more + // classes are gradually converted to multi-view, it should get the view ID + // from somewhere. + FlutterView* view = GetView(kFlutterDefaultViewId); + if (!view) { + return false; + } + SetFrameStatus(FrameStatus::kPresenting); bool has_flutter_content = false; @@ -91,21 +102,24 @@ FlutterIOSurfaceHolder* io_surface_holder = (__bridge FlutterIOSurfaceHolder*)backing_store->metal.texture.user_data; IOSurfaceRef io_surface = [io_surface_holder ioSurface]; - InsertCALayerForIOSurface(io_surface); + InsertCALayerForIOSurface(view, io_surface); } has_flutter_content = true; break; } - case kFlutterLayerContentTypePlatformView: - PresentPlatformView(layer, i); + case kFlutterLayerContentTypePlatformView: { + PresentPlatformView(view, layer, i); break; + } }; } return EndFrame(has_flutter_content); } -void FlutterMetalCompositor::PresentPlatformView(const FlutterLayer* layer, size_t layer_position) { +void FlutterMetalCompositor::PresentPlatformView(FlutterView* default_base_view, + 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]) @@ -120,7 +134,7 @@ 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]; + [default_base_view addSubview:platform_view]; } platform_view.layer.zPosition = layer_position; } diff --git a/shell/platform/darwin/macos/framework/Source/FlutterMetalCompositorUnittests.mm b/shell/platform/darwin/macos/framework/Source/FlutterMetalCompositorUnittests.mm index ac0e0330405bd..4a2884cf61432 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterMetalCompositorUnittests.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterMetalCompositorUnittests.mm @@ -3,19 +3,69 @@ // found in the LICENSE file. #import +#import #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterMetalCompositor.h" -#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTestUtils.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterView.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewProvider.h" #import "flutter/testing/testing.h" +@interface FlutterViewMockProviderMetal : NSObject { + FlutterView* _defaultView; +} +/** + * Create a FlutterViewMockProviderMetal with the provided view as the default view. + */ +- (nonnull instancetype)initWithDefaultView:(nonnull FlutterView*)view; +@end + +@implementation FlutterViewMockProviderMetal + +- (nonnull instancetype)initWithDefaultView:(nonnull FlutterView*)view { + self = [super init]; + if (self != nil) { + _defaultView = view; + } + return self; +} + +- (nullable FlutterView*)getView:(uint64_t)viewId { + if (viewId == kFlutterDefaultViewId) { + return _defaultView; + } + return nil; +} + +@end + namespace flutter::testing { +namespace { + +id MockViewProvider() { + FlutterView* viewMock = OCMClassMock([FlutterView class]); + FlutterMetalRenderBackingStore* backingStoreMock = + OCMClassMock([FlutterMetalRenderBackingStore class]); + __block id textureMock = OCMProtocolMock(@protocol(MTLTexture)); + OCMStub([backingStoreMock texture]).andReturn(textureMock); + + OCMStub([viewMock backingStoreForSize:CGSize{}]) + .ignoringNonObjectArgs() + .andDo(^(NSInvocation* invocation) { + CGSize size; + [invocation getArgument:&size atIndex:2]; + OCMStub([textureMock width]).andReturn(size.width); + OCMStub([textureMock height]).andReturn(size.height); + }) + .andReturn(backingStoreMock); + + return [[FlutterViewMockProviderMetal alloc] initWithDefaultView:viewMock]; +} +} // namespace TEST(FlutterMetalCompositorTest, TestPresent) { - id mockViewController = CreateMockViewController(); - std::unique_ptr macos_compositor = std::make_unique( - mockViewController, /*platform_view_controller*/ nullptr, /*mtl_device*/ nullptr); + MockViewProvider(), /*platform_view_controller*/ nullptr, /*mtl_device*/ nullptr); bool flag = false; macos_compositor->SetPresentCallback([f = &flag](bool has_flutter_content) { @@ -28,12 +78,9 @@ } TEST(FlutterMetalCompositorTest, TestCreate) { - id mockViewController = CreateMockViewController(); - [mockViewController loadView]; - std::unique_ptr macos_compositor = std::make_unique( - mockViewController, /*platform_view_controller*/ nullptr, /*mtl_device*/ nullptr); + MockViewProvider(), /*platform_view_controller*/ nullptr, /*mtl_device*/ nullptr); FlutterBackingStore backing_store; FlutterBackingStoreConfig config; @@ -50,12 +97,9 @@ } TEST(FlutterMetalCompositorTest, TestCompositing) { - id mockViewController = CreateMockViewController(); - [mockViewController loadView]; - std::unique_ptr macos_compositor = std::make_unique( - mockViewController, /*platform_view_controller*/ nullptr, /*mtl_device*/ nullptr); + MockViewProvider(), /*platform_view_controller*/ nullptr, /*mtl_device*/ nullptr); FlutterBackingStore backing_store; FlutterBackingStoreConfig config; diff --git a/shell/platform/darwin/macos/framework/Source/FlutterView.h b/shell/platform/darwin/macos/framework/Source/FlutterView.h index 6dd21830b2c8d..f45f94655445b 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterView.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterView.h @@ -3,9 +3,21 @@ // found in the LICENSE file. #import +#include #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterResizableBackingStoreProvider.h" +/** + * The view ID for APIs that don't support multi-view. + * + * Some single-view APIs will eventually be replaced by their multi-view + * variant. During the deprecation period, the single-view APIs will coexist with + * and work with the multi-view APIs as if the other views don't exist. For + * backward compatibility, single-view APIs will always operate the view with + * this ID. Also, the first view assigned to the engine will also have this ID. + */ +constexpr uint64_t kFlutterDefaultViewId = 0; + /** * Listener for view resizing. */ diff --git a/shell/platform/darwin/macos/framework/Source/FlutterViewEngineProvider.h b/shell/platform/darwin/macos/framework/Source/FlutterViewEngineProvider.h new file mode 100644 index 0000000000000..feafa708be803 --- /dev/null +++ b/shell/platform/darwin/macos/framework/Source/FlutterViewEngineProvider.h @@ -0,0 +1,22 @@ +// 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/FlutterViewProvider.h" + +@class FlutterEngine; + +/** + * A facade over FlutterEngine that allows FlutterEngine's children components + * to query FlutterView. + * + * FlutterViewProvider only holds a weak reference to FlutterEngine. + */ +@interface FlutterViewEngineProvider : NSObject + +/** + * Create a FlutterViewProvider with the underlying engine. + */ +- (nonnull instancetype)initWithEngine:(nonnull __weak FlutterEngine*)engine; + +@end diff --git a/shell/platform/darwin/macos/framework/Source/FlutterViewEngineProvider.mm b/shell/platform/darwin/macos/framework/Source/FlutterViewEngineProvider.mm new file mode 100644 index 0000000000000..a063784b5e2c9 --- /dev/null +++ b/shell/platform/darwin/macos/framework/Source/FlutterViewEngineProvider.mm @@ -0,0 +1,35 @@ +// 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/FlutterViewEngineProvider.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h" + +@interface FlutterViewEngineProvider () { + __weak FlutterEngine* _engine; +} + +@end + +@implementation FlutterViewEngineProvider + +- (instancetype)initWithEngine:(FlutterEngine*)engine { + self = [super init]; + if (self != nil) { + _engine = engine; + } + return self; +} + +- (nullable FlutterView*)getView:(uint64_t)viewId { + // TODO(dkwingsmt): This class only supports the first view for now. After + // FlutterEngine supports multi-view, it should get the view associated to the + // ID. + if (viewId == kFlutterDefaultViewId) { + return _engine.viewController.flutterView; + } + return nil; +} + +@end diff --git a/shell/platform/darwin/macos/framework/Source/FlutterViewEngineProviderUnittests.mm b/shell/platform/darwin/macos/framework/Source/FlutterViewEngineProviderUnittests.mm new file mode 100644 index 0000000000000..6a1aa66b69c59 --- /dev/null +++ b/shell/platform/darwin/macos/framework/Source/FlutterViewEngineProviderUnittests.mm @@ -0,0 +1,41 @@ +// 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 +#import +#import + +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewEngineProvider.h" +#import "flutter/testing/testing.h" +#include "third_party/googletest/googletest/include/gtest/gtest.h" + +#import "flutter/testing/testing.h" +#include "third_party/googletest/googletest/include/gtest/gtest.h" + +namespace flutter::testing { + +TEST(FlutterViewEngineProviderUnittests, GetViewReturnsTheCorrectView) { + FlutterViewEngineProvider* viewProvider; + id mockEngine = OCMClassMock([FlutterEngine class]); + __block id mockFlutterViewController; + OCMStub([mockEngine viewController]).andDo(^(NSInvocation* invocation) { + if (mockFlutterViewController != nil) { + [invocation setReturnValue:&mockFlutterViewController]; + } + }); + viewProvider = [[FlutterViewEngineProvider alloc] initWithEngine:mockEngine]; + + // When the view controller is not set, the returned view is nil. + EXPECT_EQ([viewProvider getView:0], nil); + + // When the view controller is set, the returned view is the controller's view. + mockFlutterViewController = OCMStrictClassMock([FlutterViewController class]); + id mockView = OCMStrictClassMock([FlutterView class]); + OCMStub([mockFlutterViewController flutterView]).andReturn(mockView); + EXPECT_EQ([viewProvider getView:0], mockView); +} + +} // namespace flutter::testing diff --git a/shell/platform/darwin/macos/framework/Source/FlutterViewProvider.h b/shell/platform/darwin/macos/framework/Source/FlutterViewProvider.h new file mode 100644 index 0000000000000..98aca04055d5b --- /dev/null +++ b/shell/platform/darwin/macos/framework/Source/FlutterViewProvider.h @@ -0,0 +1,23 @@ +// 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/FlutterView.h" + +/** + * An interface to query FlutterView. + * + * See also: + * + * * FlutterViewEngineProvider, a typical implementation. + */ +@protocol FlutterViewProvider + +/** + * Get the FlutterView with the given view ID. + * + * Returns nil if the ID is invalid. + */ +- (nullable FlutterView*)getView:(uint64_t)id; + +@end