diff --git a/shell/platform/darwin/macos/framework/Headers/FlutterEngine.h b/shell/platform/darwin/macos/framework/Headers/FlutterEngine.h index 7c7bea715893b..957cae047a46f 100644 --- a/shell/platform/darwin/macos/framework/Headers/FlutterEngine.h +++ b/shell/platform/darwin/macos/framework/Headers/FlutterEngine.h @@ -32,9 +32,6 @@ extern const uint64_t kFlutterDefaultViewId; /** * Coordinates a single instance of execution of a Flutter engine. - * - * A FlutterEngine can only be attached with one controller from the native - * code. */ FLUTTER_DARWIN_EXPORT @interface FlutterEngine : NSObject @@ -79,9 +76,10 @@ FLUTTER_DARWIN_EXPORT - (BOOL)runWithEntrypoint:(nullable NSString*)entrypoint; /** - * The `FlutterViewController` of this engine, if any. + * The default `FlutterViewController` associated with this engine, if any. * - * This view is used by legacy APIs that assume a single view. + * The default view always has ID kFlutterDefaultViewId, and is the view + * operated by the APIs that do not have a view ID specified. * * Setting this field from nil to a non-nil view controller also updates * the view controller's engine and ID. diff --git a/shell/platform/darwin/macos/framework/Headers/FlutterPluginRegistrarMacOS.h b/shell/platform/darwin/macos/framework/Headers/FlutterPluginRegistrarMacOS.h index daedcb8773dae..c341f2354dcd9 100644 --- a/shell/platform/darwin/macos/framework/Headers/FlutterPluginRegistrarMacOS.h +++ b/shell/platform/darwin/macos/framework/Headers/FlutterPluginRegistrarMacOS.h @@ -36,18 +36,12 @@ FLUTTER_DARWIN_EXPORT @property(nonnull, readonly) id textures; /** - * The default view displaying Flutter content. + * The view displaying Flutter content. May return |nil|, for instance in a headless environment. * - * This method may return |nil|, for instance in a headless environment. - * - * The default view is a special view operated by single-view APIs. - */ -- (nullable NSView*)view; - -/** - * The `NSView` associated with the given view ID, if any. + * WARNING: If/when multiple Flutter views within the same application are supported (#30701), this + * API will change. */ -- (nullable NSView*)viewForId:(uint64_t)viewId; +@property(nullable, readonly) NSView* view; /** * Registers |delegate| to receive handleMethodCall:result: callbacks for the given |channel|. diff --git a/shell/platform/darwin/macos/framework/Headers/FlutterViewController.h b/shell/platform/darwin/macos/framework/Headers/FlutterViewController.h index 5979131f7451b..e4d8df7d4c48d 100644 --- a/shell/platform/darwin/macos/framework/Headers/FlutterViewController.h +++ b/shell/platform/darwin/macos/framework/Headers/FlutterViewController.h @@ -89,9 +89,7 @@ FLUTTER_DARWIN_EXPORT NS_DESIGNATED_INITIALIZER; - (nonnull instancetype)initWithCoder:(nonnull NSCoder*)nibNameOrNil NS_DESIGNATED_INITIALIZER; /** - * Initializes this FlutterViewController with an existing `FlutterEngine`. - * - * The initialized view controller will add itself to the engine as part of this process. + * Initializes this FlutterViewController with the specified `FlutterEngine`. * * This initializer is suitable for both the first Flutter view controller and * the following ones of the app. diff --git a/shell/platform/darwin/macos/framework/Source/FlutterCompositor.mm b/shell/platform/darwin/macos/framework/Source/FlutterCompositor.mm index 51b874f47dfbd..74173158d8c59 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterCompositor.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterCompositor.mm @@ -19,7 +19,7 @@ // 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 = [view_provider_ viewForId:kFlutterDefaultViewId]; + FlutterView* view = [view_provider_ getView:kFlutterDefaultViewId]; if (!view) { return false; } @@ -37,7 +37,7 @@ bool FlutterCompositor::Present(uint64_t view_id, const FlutterLayer** layers, size_t layers_count) { - FlutterView* view = [view_provider_ viewForId:view_id]; + FlutterView* view = [view_provider_ getView:view_id]; if (!view) { return false; } diff --git a/shell/platform/darwin/macos/framework/Source/FlutterCompositorTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterCompositorTest.mm index 4005b176aaf24..bd6bf4d57b58b 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterCompositorTest.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterCompositorTest.mm @@ -11,8 +11,6 @@ #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewProvider.h" #import "flutter/testing/testing.h" -extern const uint64_t kFlutterDefaultViewId; - @interface FlutterViewMockProvider : NSObject { FlutterView* _defaultView; } @@ -32,7 +30,7 @@ - (nonnull instancetype)initWithDefaultView:(nonnull FlutterView*)view { return self; } -- (nullable FlutterView*)viewForId:(uint64_t)viewId { +- (nullable FlutterView*)getView:(uint64_t)viewId { if (viewId == kFlutterDefaultViewId) { return _defaultView; } diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm index 481b2aab9997c..9fc17bd2e0af5 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm @@ -84,29 +84,6 @@ @interface FlutterEngine () - (nullable FlutterViewController*)viewControllerForId:(uint64_t)viewId; -/** - * An internal method that adds the view controller with the given ID. - * - * This method assigns the controller with the ID, puts the controller into the - * map, and does assertions related to the default view ID. - */ -- (void)registerViewController:(FlutterViewController*)controller forId:(uint64_t)viewId; - -/** - * An internal method that removes the view controller with the given ID. - * - * This method clears the ID of the controller, removes the controller from the - * map. This is an no-op if the view ID is not associated with any view - * controllers. - */ -- (void)deregisterViewControllerForId:(uint64_t)viewId; - -/** - * Shuts down the engine if view requirement is not met, and headless execution - * is not allowed. - */ -- (void)shutDownIfNeeded; - /** * Sends the list of user-preferred locales to the Flutter engine. */ @@ -184,18 +161,10 @@ - (instancetype)initWithPlugin:(NSString*)pluginKey flutterEngine:(FlutterEngine } - (NSView*)view { - return [self viewForId:kFlutterDefaultViewId]; -} - -- (NSView*)viewForId:(uint64_t)viewId { - FlutterViewController* controller = [_flutterEngine viewControllerForId:viewId]; - if (controller == nil) { - return nil; + if (!_flutterEngine.viewController.viewLoaded) { + [_flutterEngine.viewController loadView]; } - if (!controller.viewLoaded) { - [controller loadView]; - } - return controller.flutterView; + return _flutterEngine.viewController.flutterView; } - (void)addMethodCallDelegate:(nonnull id)delegate @@ -245,11 +214,6 @@ @implementation FlutterEngine { // when the engine is destroyed. std::unique_ptr _macOSCompositor; - // The information of all views attached to this engine mapped from IDs. - // - // It can't use NSDictionary, because the values need to be weak references. - NSMapTable* _viewControllers; - // FlutterCompositor is copied and used in embedder.cc. FlutterCompositor _compositor; @@ -266,8 +230,6 @@ @implementation FlutterEngine { // A method channel for miscellaneous platform functionality. FlutterMethodChannel* _platformChannel; - - int _nextViewId; } - (instancetype)initWithName:(NSString*)labelPrefix project:(FlutterDartProject*)project { @@ -287,14 +249,10 @@ - (instancetype)initWithName:(NSString*)labelPrefix _semanticsEnabled = NO; _isResponseValid = [[NSMutableArray alloc] initWithCapacity:1]; [_isResponseValid addObject:@YES]; - // kFlutterDefaultViewId is reserved for the default view. - // All IDs above it are for regular views. - _nextViewId = kFlutterDefaultViewId + 1; _embedderAPI.struct_size = sizeof(FlutterEngineProcTable); FlutterEngineGetProcAddresses(&_embedderAPI); - _viewControllers = [NSMapTable weakToWeakObjectsMapTable]; _renderer = [[FlutterRenderer alloc] initWithFlutterEngine:self]; NSNotificationCenter* notificationCenter = [NSNotificationCenter defaultCenter]; @@ -326,7 +284,7 @@ - (BOOL)runWithEntrypoint:(NSString*)entrypoint { return NO; } - if (!_allowHeadlessExecution && [_viewControllers count] == 0) { + if (!_allowHeadlessExecution && !_viewController) { NSLog(@"Attempted to run an engine with no view controller without headless mode enabled."); return NO; } @@ -353,11 +311,8 @@ - (BOOL)runWithEntrypoint:(NSString*)entrypoint { flutterArguments.platform_message_callback = (FlutterPlatformMessageCallback)OnPlatformMessage; flutterArguments.update_semantics_callback = [](const FlutterSemanticsUpdate* update, void* user_data) { - // TODO(dkwingsmt): This callback only supports single-view, therefore it - // only operates on the default view. To support multi-view, we need a - // way to pass in the ID (probably through FlutterSemanticsUpdate). FlutterEngine* engine = (__bridge FlutterEngine*)user_data; - [[engine viewControllerForId:kFlutterDefaultViewId] updateSemantics:update]; + [engine.viewController updateSemantics:update]; }; flutterArguments.custom_dart_entrypoint = entrypoint.UTF8String; flutterArguments.shutdown_dart_vm_when_done = true; @@ -419,14 +374,7 @@ - (BOOL)runWithEntrypoint:(NSString*)entrypoint { } [self sendUserLocales]; - - // Update window metric for all view controllers. - NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator]; - FlutterViewController* nextViewController; - while ((nextViewController = [viewControllerEnumerator nextObject])) { - [self updateWindowMetricsForViewController:nextViewController]; - } - + [self updateWindowMetrics]; [self updateDisplayConfig]; // Send the initial user settings such as brightness and text scale factor // to the engine. @@ -460,59 +408,28 @@ - (void)loadAOTData:(NSString*)assetsDir { } } -- (void)registerViewController:(FlutterViewController*)controller forId:(uint64_t)viewId { - NSAssert(controller != nil, @"The controller must not be nil."); - NSAssert(![controller attached], - @"The incoming view controller is already attached to an engine."); - NSAssert([_viewControllers objectForKey:@(viewId)] == nil, @"The requested view ID is occupied."); - [controller attachToEngine:self withId:viewId]; - NSAssert(controller.id == viewId, @"Failed to assign view ID."); - [_viewControllers setObject:controller forKey:@(viewId)]; -} - -- (void)deregisterViewControllerForId:(uint64_t)viewId { - FlutterViewController* oldController = [self viewControllerForId:viewId]; - if (oldController != nil) { - [oldController detachFromEngine]; - [_viewControllers removeObjectForKey:@(viewId)]; - } -} - -- (void)shutDownIfNeeded { - if ([_viewControllers count] == 0 && !_allowHeadlessExecution) { - [self shutDownEngine]; - } -} - -- (FlutterViewController*)viewControllerForId:(uint64_t)viewId { - FlutterViewController* controller = [_viewControllers objectForKey:@(viewId)]; - NSAssert(controller == nil || controller.id == viewId, - @"The stored controller has unexpected view ID."); - return controller; -} - - (void)setViewController:(FlutterViewController*)controller { - FlutterViewController* currentController = - [_viewControllers objectForKey:@(kFlutterDefaultViewId)]; - if (currentController == controller) { + if (_viewController == controller) { // From nil to nil, or from non-nil to the same controller. return; } - if (currentController == nil && controller != nil) { + if (_viewController == nil && controller != nil) { // From nil to non-nil. NSAssert(controller.engine == nil, @"Failed to set view controller to the engine: " @"The given FlutterViewController is already attached to an engine %@. " @"If you wanted to create an FlutterViewController and set it to an existing engine, " - @"you should use FlutterViewController#init(engine:, nibName, bundle:) instead.", + @"you should create it with init(engine:, nibName, bundle:) instead.", controller.engine); - [self registerViewController:controller forId:kFlutterDefaultViewId]; - } else if (currentController != nil && controller == nil) { - NSAssert(currentController.id == kFlutterDefaultViewId, - @"The default controller has an unexpected ID %llu", currentController.id); + _viewController = controller; + [_viewController attachToEngine:self withId:kFlutterDefaultViewId]; + } else if (_viewController != nil && controller == nil) { // From non-nil to nil. - [self deregisterViewControllerForId:kFlutterDefaultViewId]; - [self shutDownIfNeeded]; + [_viewController detachFromEngine]; + _viewController = nil; + if (!_allowHeadlessExecution) { + [self shutDownEngine]; + } } else { // From non-nil to a different non-nil view controller. NSAssert(NO, @@ -520,14 +437,10 @@ - (void)setViewController:(FlutterViewController*)controller { @"The engine already has a default view controller %@. " @"If you wanted to make the default view render in a different window, " @"you should attach the current view controller to the window instead.", - [_viewControllers objectForKey:@(kFlutterDefaultViewId)]); + _viewController); } } -- (FlutterViewController*)viewController { - return [self viewControllerForId:kFlutterDefaultViewId]; -} - - (FlutterCompositor*)createFlutterCompositor { _macOSCompositor = std::make_unique( [[FlutterViewEngineProvider alloc] initWithEngine:self], _platformViewController); @@ -572,17 +485,6 @@ - (FlutterCompositor*)createFlutterCompositor { #pragma mark - Framework-internal methods -- (void)addViewController:(FlutterViewController*)controller { - [self registerViewController:controller forId:kFlutterDefaultViewId]; -} - -- (void)removeViewController:(nonnull FlutterViewController*)viewController { - NSAssert([viewController attached] && viewController.engine == self, - @"The given view controller is not associated with this engine."); - [self deregisterViewControllerForId:viewController.id]; - [self shutDownIfNeeded]; -} - - (BOOL)running { return _engine != nullptr; } @@ -642,19 +544,11 @@ - (nonnull NSString*)executableName { return [[[NSProcessInfo processInfo] arguments] firstObject] ?: @"Flutter"; } -- (void)updateWindowMetricsForViewController:(FlutterViewController*)viewController { - if (viewController.id != kFlutterDefaultViewId) { - // TODO(dkwingsmt): The embedder API only supports single-view for now. As - // embedder APIs are converted to multi-view, this method should support any - // views. +- (void)updateWindowMetrics { + if (!_engine || !self.viewController.viewLoaded) { return; } - if (!_engine || !viewController || !viewController.viewLoaded) { - return; - } - NSAssert([self viewControllerForId:viewController.id] == viewController, - @"The provided view controller is not attached to this engine."); - NSView* view = viewController.flutterView; + NSView* view = self.viewController.flutterView; CGRect scaledBounds = [view convertRectToBacking:view.bounds]; CGSize scaledSize = scaledBounds.size; double pixelRatio = view.bounds.size.width == 0 ? 1 : scaledSize.width / view.bounds.size.width; @@ -685,14 +579,7 @@ - (void)setSemanticsEnabled:(BOOL)enabled { return; } _semanticsEnabled = enabled; - - // Update all view controllers' bridges. - NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator]; - FlutterViewController* nextViewController; - while ((nextViewController = [viewControllerEnumerator nextObject])) { - [nextViewController notifySemanticsEnabledChanged]; - } - + [_viewController notifySemanticsEnabledChanged]; _embedderAPI.UpdateSemanticsEnabled(_engine, _semanticsEnabled); } @@ -708,6 +595,17 @@ - (FlutterPlatformViewController*)platformViewController { #pragma mark - Private methods +- (FlutterViewController*)viewControllerForId:(uint64_t)viewId { + // TODO(dkwingsmt): The engine only supports single-view, therefore it + // only processes the default ID. After the engine supports multiple views, + // this method should be able to return the view for any IDs. + NSAssert(viewId == kFlutterDefaultViewId, @"Unexpected view ID %llu", viewId); + if (viewId == kFlutterDefaultViewId) { + return _viewController; + } + return nil; +} + - (void)sendUserLocales { if (!self.running) { return; @@ -771,10 +669,8 @@ - (void)engineCallbackOnPlatformMessage:(const FlutterPlatformMessage*)message { } - (void)engineCallbackOnPreEngineRestart { - NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator]; - FlutterViewController* nextViewController; - while ((nextViewController = [viewControllerEnumerator nextObject])) { - [nextViewController onPreEngineRestart]; + if (_viewController) { + [_viewController onPreEngineRestart]; } } @@ -786,11 +682,7 @@ - (void)shutDownEngine { return; } - NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator]; - FlutterViewController* nextViewController; - while ((nextViewController = [viewControllerEnumerator nextObject])) { - [nextViewController.flutterView shutdown]; - } + [self.viewController.flutterView shutdown]; FlutterEngineResult result = _embedderAPI.Deinitialize(_engine); if (result != kSuccess) { @@ -855,12 +747,7 @@ - (void)applicationWillTerminate:(NSNotification*)notification { - (void)onAccessibilityStatusChanged:(NSNotification*)notification { BOOL enabled = [notification.userInfo[kEnhancedUserInterfaceKey] boolValue]; - NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator]; - FlutterViewController* nextViewController; - while ((nextViewController = [viewControllerEnumerator nextObject])) { - [nextViewController onAccessibilityStatusChanged:enabled]; - } - + [self.viewController onAccessibilityStatusChanged:enabled]; self.semanticsEnabled = enabled; } diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm index d3b2be0406001..911ff76a07eb4 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm @@ -614,69 +614,6 @@ - (nonnull NSView*)createWithViewIdentifier:(int64_t)viewId arguments:(nullable rasterThread.join(); } -TEST_F(FlutterEngineTest, ManageControllersIfInitiatedByController) { - NSString* fixtures = @(flutter::testing::GetFixturesPath()); - FlutterDartProject* project = [[FlutterDartProject alloc] - initWithAssetsPath:fixtures - ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]]; - - FlutterEngine* engine; - FlutterViewController* viewController1; - - @autoreleasepool { - // Create FVC1. - viewController1 = [[FlutterViewController alloc] initWithProject:project]; - EXPECT_EQ(viewController1.id, 0ull); - - engine = viewController1.engine; - engine.viewController = nil; - - // Create FVC2 based on the same engine. - FlutterViewController* viewController2 = [[FlutterViewController alloc] initWithEngine:engine - nibName:nil - bundle:nil]; - EXPECT_EQ(engine.viewController, viewController2); - } - // FVC2 is deallocated but FVC1 is retained. - - EXPECT_EQ(engine.viewController, nil); - - engine.viewController = viewController1; - EXPECT_EQ(engine.viewController, viewController1); - EXPECT_EQ(viewController1.id, 0ull); -} - -TEST_F(FlutterEngineTest, ManageControllersIfInitiatedByEngine) { - // Don't create the engine with `CreateMockFlutterEngine`, because it adds - // additional references to FlutterViewControllers, which is crucial to this - // test case. - FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"io.flutter" - project:nil - allowHeadlessExecution:NO]; - FlutterViewController* viewController1; - - @autoreleasepool { - viewController1 = [[FlutterViewController alloc] initWithEngine:engine nibName:nil bundle:nil]; - EXPECT_EQ(viewController1.id, 0ull); - EXPECT_EQ(engine.viewController, viewController1); - - engine.viewController = nil; - - FlutterViewController* viewController2 = [[FlutterViewController alloc] initWithEngine:engine - nibName:nil - bundle:nil]; - EXPECT_EQ(viewController2.id, 0ull); - EXPECT_EQ(engine.viewController, viewController2); - } - // FVC2 is deallocated but FVC1 is retained. - - EXPECT_EQ(engine.viewController, nil); - - engine.viewController = viewController1; - EXPECT_EQ(engine.viewController, viewController1); - EXPECT_EQ(viewController1.id, 0ull); -} - } // namespace flutter::testing // NOLINTEND(clang-analyzer-core.StackAddressEscape) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h b/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h index 1879cb92dbdab..6eed864e6a433 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h @@ -48,39 +48,9 @@ @property(nonatomic, readonly, nonnull) NSPasteboard* pasteboard; /** - * Attach a view controller to the engine as its default controller. - * - * Practically, since FlutterEngine can only be attached with one controller, - * the given controller, if successfully attached, will always have the default - * view ID kFlutterDefaultViewId. - * - * The engine holds a weak reference to the attached view controller. - * - * If the given view controller is already attached to an engine, this call - * throws an assertion. + * Informs the engine that the associated view controller's view size has changed. */ -- (void)addViewController:(nonnull FlutterViewController*)viewController; - -/** - * Dissociate the given view controller from this engine. - * - * Practically, since FlutterEngine can only be attached with one controller, - * the given controller must be the default view controller. - * - * If the view controller is not associated with this engine, this call throws an - * assertion. - */ -- (void)removeViewController:(nonnull FlutterViewController*)viewController; - -/** - * The `FlutterViewController` associated with the given view ID, if any. - */ -- (nullable FlutterViewController*)viewControllerForId:(uint64_t)viewId; - -/** - * Informs the engine that the specified view controller's window metrics have changed. - */ -- (void)updateWindowMetricsForViewController:(nonnull FlutterViewController*)viewController; +- (void)updateWindowMetrics; /** * Dispatches the given pointer event data to engine. diff --git a/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.mm b/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.mm index ad50476a47675..78c18dd0b08e0 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.mm @@ -57,7 +57,7 @@ @interface FlutterKeyboardManager () /** * The text input plugin set by initialization. */ -@property(nonatomic, weak) id viewDelegate; +@property(nonatomic) id viewDelegate; /** * The primary responders added by addPrimaryResponder. diff --git a/shell/platform/darwin/macos/framework/Source/FlutterRenderer.mm b/shell/platform/darwin/macos/framework/Source/FlutterRenderer.mm index d7d2c2fc7d6ac..007a3cce157a5 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterRenderer.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterRenderer.mm @@ -89,7 +89,7 @@ - (FlutterRendererConfig)createRendererConfig { #pragma mark - Embedder callback implementations. - (FlutterMetalTexture)createTextureForView:(uint64_t)viewId size:(CGSize)size { - FlutterView* view = [_viewProvider viewForId:viewId]; + FlutterView* view = [_viewProvider getView:viewId]; NSAssert(view != nil, @"Can't create texture on a non-existent view 0x%llx.", viewId); if (view == nil) { // FlutterMetalTexture has texture `null`, therefore is discarded. @@ -99,7 +99,7 @@ - (FlutterMetalTexture)createTextureForView:(uint64_t)viewId size:(CGSize)size { } - (BOOL)present:(uint64_t)viewId texture:(const FlutterMetalTexture*)texture { - FlutterView* view = [_viewProvider viewForId:viewId]; + FlutterView* view = [_viewProvider getView:viewId]; if (view == nil) { return NO; } diff --git a/shell/platform/darwin/macos/framework/Source/FlutterRendererTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterRendererTest.mm index 46da658efd8e6..01146e1464c02 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterRendererTest.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterRendererTest.mm @@ -9,54 +9,37 @@ #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterRenderer.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterView.h" -#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTestUtils.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h" #include "flutter/shell/platform/embedder/embedder.h" #include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.h" #include "flutter/testing/testing.h" -@interface RendererTestViewController : FlutterViewController -- (void)loadMockFlutterView:(FlutterView*)mockView; -@end - -@implementation RendererTestViewController { - FlutterView* _mockFlutterView; -} - -- (void)loadMockFlutterView:(FlutterView*)mockView { - _mockFlutterView = mockView; - [self loadView]; -} - -- (nonnull FlutterView*)createFlutterViewWithMTLDevice:(id)device - commandQueue:(id)commandQueue { - return _mockFlutterView; -} -@end - namespace flutter::testing { namespace { // Returns an engine configured for the test fixture resource configuration. -RendererTestViewController* CreateTestViewController() { +FlutterEngine* CreateTestEngine() { NSString* fixtures = @(testing::GetFixturesPath()); FlutterDartProject* project = [[FlutterDartProject alloc] initWithAssetsPath:fixtures ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]]; - RendererTestViewController* viewController = - [[RendererTestViewController alloc] initWithProject:project]; - return viewController; + return [[FlutterEngine alloc] initWithName:@"test" project:project allowHeadlessExecution:true]; +} + +void SetEngineDefaultView(FlutterEngine* engine, id flutterView) { + id mockFlutterViewController = OCMClassMock([FlutterViewController class]); + OCMStub([mockFlutterViewController flutterView]).andReturn(flutterView); + [engine setViewController:mockFlutterViewController]; } } // namespace TEST(FlutterRenderer, PresentDelegatesToFlutterView) { - RendererTestViewController* viewController = CreateTestViewController(); - FlutterEngine* engine = viewController.engine; + FlutterEngine* engine = CreateTestEngine(); + FlutterRenderer* renderer = [[FlutterRenderer alloc] initWithFlutterEngine:engine]; id viewMock = OCMClassMock([FlutterView class]); - [viewController loadMockFlutterView:viewMock]; - FlutterRenderer* renderer = [[FlutterRenderer alloc] initWithFlutterEngine:engine]; + SetEngineDefaultView(engine, viewMock); id surfaceManagerMock = OCMClassMock([FlutterSurfaceManager class]); OCMStub([viewMock surfaceManager]).andReturn(surfaceManagerMock); @@ -78,12 +61,11 @@ - (nonnull FlutterView*)createFlutterViewWithMTLDevice:(id)device } TEST(FlutterRenderer, TextureReturnedByFlutterView) { - RendererTestViewController* viewController = CreateTestViewController(); - FlutterEngine* engine = viewController.engine; + FlutterEngine* engine = CreateTestEngine(); + FlutterRenderer* renderer = [[FlutterRenderer alloc] initWithFlutterEngine:engine]; id viewMock = OCMClassMock([FlutterView class]); - [viewController loadMockFlutterView:viewMock]; - FlutterRenderer* renderer = [[FlutterRenderer alloc] initWithFlutterEngine:engine]; + SetEngineDefaultView(engine, viewMock); id surfaceManagerMock = OCMClassMock([FlutterSurfaceManager class]); OCMStub([viewMock surfaceManager]).andReturn(surfaceManagerMock); diff --git a/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm b/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm index 994cc8f468d69..72d67f105e264 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm @@ -309,12 +309,11 @@ static void CommonInit(FlutterViewController* controller, FlutterEngine* engine) @"The FlutterViewController is unexpectedly attached to " @"engine %@ before initialization.", controller.engine); - [engine addViewController:controller]; + engine.viewController = controller; NSCAssert(controller.engine != nil, @"The FlutterViewController unexpectedly stays unattached after initialization. " @"In unit tests, this is likely because either the FlutterViewController or " - @"the FlutterEngine is mocked. Please subclass these classes instead.", - controller.engine, controller.id); + @"the FlutterEngine is mocked. Please subclass these classes instead."); controller->_mouseTrackingMode = FlutterMouseTrackingModeInKeyWindow; controller->_textInputPlugin = [[FlutterTextInputPlugin alloc] initWithViewController:controller]; [controller initializeKeyboard]; @@ -356,6 +355,11 @@ - (instancetype)initWithEngine:(nonnull FlutterEngine*)engine nibName:(nullable NSString*)nibName bundle:(nullable NSBundle*)nibBundle { NSAssert(engine != nil, @"Engine is required"); + NSAssert(engine.viewController == nil, + @"The supplied FlutterEngine is already used with FlutterViewController " + "instance. One instance of the FlutterEngine can only be attached to one " + "FlutterViewController at a time. Set FlutterEngine.viewController " + "to nil before attaching it to another FlutterViewController."); self = [super initWithNibName:nibName bundle:nibBundle]; if (self) { @@ -408,9 +412,7 @@ - (void)viewWillDisappear { } - (void)dealloc { - if ([self attached]) { - [_engine removeViewController:self]; - } + _engine.viewController = nil; CFNotificationCenterRef cfCenter = CFNotificationCenterGetDistributedCenter(); CFNotificationCenterRemoveEveryObserver(cfCenter, (__bridge void*)self); } @@ -581,7 +583,8 @@ - (void)configureTrackingArea { - (void)initializeKeyboard { // TODO(goderbauer): Seperate keyboard/textinput stuff into ViewController specific and Engine // global parts. Move the global parts to FlutterEngine. - _keyboardManager = [[FlutterKeyboardManager alloc] initWithViewDelegate:self]; + __weak FlutterViewController* weakSelf = self; + _keyboardManager = [[FlutterKeyboardManager alloc] initWithViewDelegate:weakSelf]; } - (void)dispatchMouseEvent:(nonnull NSEvent*)event { @@ -792,7 +795,7 @@ - (void)onKeyboardLayoutChanged { * Responds to view reshape by notifying the engine of the change in dimensions. */ - (void)viewDidReshape:(NSView*)view { - [_engine updateWindowMetricsForViewController:self]; + [_engine updateWindowMetrics]; } #pragma mark - FlutterPluginRegistry diff --git a/shell/platform/darwin/macos/framework/Source/FlutterViewEngineProvider.mm b/shell/platform/darwin/macos/framework/Source/FlutterViewEngineProvider.mm index 95c3aa53e0ee5..a063784b5e2c9 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterViewEngineProvider.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterViewEngineProvider.mm @@ -22,8 +22,14 @@ - (instancetype)initWithEngine:(FlutterEngine*)engine { return self; } -- (nullable FlutterView*)viewForId:(uint64_t)viewId { - return [_engine viewControllerForId:viewId].flutterView; +- (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/FlutterViewEngineProviderTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterViewEngineProviderTest.mm index 86c94a70caa60..6a1aa66b69c59 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterViewEngineProviderTest.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterViewEngineProviderTest.mm @@ -6,7 +6,6 @@ #import #import -#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterEngineTestUtils.h" #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" @@ -20,29 +19,23 @@ TEST(FlutterViewEngineProviderUnittests, GetViewReturnsTheCorrectView) { FlutterViewEngineProvider* viewProvider; - id mockEngine = CreateMockFlutterEngine(@""); + id mockEngine = OCMClassMock([FlutterEngine class]); __block id mockFlutterViewController; - OCMStub([mockEngine viewControllerForId:0]) - .ignoringNonObjectArgs() - .andDo(^(NSInvocation* invocation) { - uint64_t viewId; - [invocation getArgument:&viewId atIndex:2]; - if (viewId == 0 /* kFlutterDefaultViewId */) { - if (mockFlutterViewController != nil) { - [invocation setReturnValue:&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 viewForId:0], 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 viewForId:0], 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 index 9548b7afab1b8..f873c72266a95 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterViewProvider.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterViewProvider.h @@ -20,6 +20,6 @@ extern const uint64_t kFlutterDefaultViewId; * * Returns nil if the ID is invalid. */ -- (nullable FlutterView*)viewForId:(uint64_t)id; +- (nullable FlutterView*)getView:(uint64_t)id; @end