diff --git a/shell/platform/darwin/macos/framework/Headers/FlutterEngine.h b/shell/platform/darwin/macos/framework/Headers/FlutterEngine.h index 891ae28928a1e..49032f87d3a91 100644 --- a/shell/platform/darwin/macos/framework/Headers/FlutterEngine.h +++ b/shell/platform/darwin/macos/framework/Headers/FlutterEngine.h @@ -7,8 +7,6 @@ #import -#include - #import "FlutterBinaryMessenger.h" #import "FlutterDartProject.h" #import "FlutterMacros.h" @@ -17,17 +15,6 @@ // TODO: Merge this file with the iOS FlutterEngine.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. - */ -extern const uint64_t kFlutterDefaultViewId; - @class FlutterViewController; /** @@ -80,15 +67,6 @@ FLUTTER_DARWIN_EXPORT * * 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. - * - * Setting this field from non-nil to nil will terminate the engine if - * allowHeadlessExecution is NO. - * - * Setting this field from non-nil to a different non-nil FlutterViewController - * is prohibited and will throw an assertion error. */ @property(nonatomic, nullable, weak) FlutterViewController* viewController; diff --git a/shell/platform/darwin/macos/framework/Headers/FlutterViewController.h b/shell/platform/darwin/macos/framework/Headers/FlutterViewController.h index e4d8df7d4c48d..4dea666e1262c 100644 --- a/shell/platform/darwin/macos/framework/Headers/FlutterViewController.h +++ b/shell/platform/darwin/macos/framework/Headers/FlutterViewController.h @@ -25,25 +25,6 @@ typedef NS_ENUM(NSInteger, FlutterMouseTrackingMode) { /** * Controls a view that displays Flutter content and manages input. - * - * A FlutterViewController works with a FlutterEngine. Upon creation, the view - * controller is always added to an engine, either a given engine, or it implicitly - * creates an engine and add itself to that engine. - * - * The FlutterEngine assigns each view controller attached to it a unique ID. - * Each view controller corresponds to a view, and the ID is used by the framework - * to specify which view to operate. - * - * A FlutterViewController can also be unattached to an engine after it is manually - * unset from the engine, or transiently during the initialization process. - * An unattached view controller is invalid. Whether the view controller is attached - * can be queried using FlutterViewController#attached. - * - * The FlutterViewController strongly references the FlutterEngine, while - * the engine weakly the view controller. When a FlutterViewController is deallocated, - * it automatically removes itself from its attached engine. When a FlutterEngine - * has no FlutterViewControllers attached, it might shut down itself or not depending - * on its configuration. */ FLUTTER_DARWIN_EXPORT @interface FlutterViewController : NSViewController @@ -53,16 +34,6 @@ FLUTTER_DARWIN_EXPORT */ @property(nonatomic, nonnull, readonly) FlutterEngine* engine; -/** - * The identifier for this view controller. - * - * The ID is assigned by FlutterEngine when the view controller is attached. - * - * If the view controller is unattached (see FlutterViewController#attached), - * reading this property throws an assertion. - */ -@property(nonatomic, readonly) uint64_t id; - /** * The style of mouse tracking to use for the view. Defaults to * FlutterMouseTrackingModeInKeyWindow. @@ -72,12 +43,6 @@ FLUTTER_DARWIN_EXPORT /** * Initializes a controller that will run the given project. * - * In this initializer, this controller creates an engine, and is attached to - * that engine as the default controller. In this way, this controller can not - * be set to other engines. This initializer is suitable for the first Flutter - * view controller of the app. To use the controller with an existing engine, - * use initWithEngine:nibName:bundle: instead. - * * @param project The project to run in this view controller. If nil, a default `FlutterDartProject` * will be used. */ @@ -91,8 +56,7 @@ FLUTTER_DARWIN_EXPORT /** * 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. + * The initialized viewcontroller will attach itself to the engine as part of this process. * * @param engine The `FlutterEngine` instance to attach to. Cannot be nil. * @param nibName The NIB name to initialize this controller with. @@ -101,12 +65,6 @@ FLUTTER_DARWIN_EXPORT - (nonnull instancetype)initWithEngine:(nonnull FlutterEngine*)engine nibName:(nullable NSString*)nibName bundle:(nullable NSBundle*)nibBundle NS_DESIGNATED_INITIALIZER; - -/** - * Return YES if the view controller is attached to an engine. - */ -- (BOOL)attached; - /** * Invoked by the engine right before the engine is restarted. * diff --git a/shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMacTest.mm b/shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMacTest.mm index d493da6461719..0ad11af0e3e8f 100644 --- a/shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMacTest.mm +++ b/shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMacTest.mm @@ -51,22 +51,26 @@ @implementation AccessibilityBridgeTestEngine namespace { // Returns an engine configured for the text fixture resource configuration. -FlutterViewController* CreateTestViewController() { +FlutterEngine* CreateTestEngine() { NSString* fixtures = @(testing::GetFixturesPath()); FlutterDartProject* project = [[FlutterDartProject alloc] initWithAssetsPath:fixtures ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]]; - FlutterEngine* engine = [[AccessibilityBridgeTestEngine alloc] initWithName:@"test" - project:project - allowHeadlessExecution:true]; - return [[FlutterViewController alloc] initWithEngine:engine nibName:nil bundle:nil]; + return [[AccessibilityBridgeTestEngine alloc] initWithName:@"test" + project:project + allowHeadlessExecution:true]; } } // namespace TEST(AccessibilityBridgeMacTest, sendsAccessibilityCreateNotificationToWindowOfFlutterView) { - FlutterViewController* viewController = CreateTestViewController(); - FlutterEngine* engine = viewController.engine; + FlutterEngine* engine = CreateTestEngine(); + NSString* fixtures = @(testing::GetFixturesPath()); + FlutterDartProject* project = [[FlutterDartProject alloc] + initWithAssetsPath:fixtures + ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]]; + FlutterViewController* viewController = [[FlutterViewController alloc] initWithProject:project]; [viewController loadView]; + [engine setViewController:viewController]; NSWindow* expectedTarget = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 800, 600) styleMask:NSBorderlessWindowMask @@ -118,9 +122,14 @@ @implementation AccessibilityBridgeTestEngine } TEST(AccessibilityBridgeMacTest, doesNotSendAccessibilityCreateNotificationWhenHeadless) { - FlutterViewController* viewController = CreateTestViewController(); - FlutterEngine* engine = viewController.engine; + FlutterEngine* engine = CreateTestEngine(); + NSString* fixtures = @(testing::GetFixturesPath()); + FlutterDartProject* project = [[FlutterDartProject alloc] + initWithAssetsPath:fixtures + ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]]; + FlutterViewController* viewController = [[FlutterViewController alloc] initWithProject:project]; [viewController loadView]; + [engine setViewController:viewController]; // Setting up bridge so that the AccessibilityBridgeMacDelegateSpy // can query semantics information from. engine.semanticsEnabled = YES; @@ -164,9 +173,15 @@ @implementation AccessibilityBridgeTestEngine } TEST(AccessibilityBridgeMacTest, doesNotSendAccessibilityCreateNotificationWhenNoWindow) { - FlutterViewController* viewController = CreateTestViewController(); - FlutterEngine* engine = viewController.engine; + FlutterEngine* engine = CreateTestEngine(); + // Create a view controller without attaching it to a window. + NSString* fixtures = @(testing::GetFixturesPath()); + FlutterDartProject* project = [[FlutterDartProject alloc] + initWithAssetsPath:fixtures + ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]]; + FlutterViewController* viewController = [[FlutterViewController alloc] initWithProject:project]; [viewController loadView]; + [engine setViewController:viewController]; // Setting up bridge so that the AccessibilityBridgeMacDelegateSpy // can query semantics information from. diff --git a/shell/platform/darwin/macos/framework/Source/FlutterCompositor.h b/shell/platform/darwin/macos/framework/Source/FlutterCompositor.h index ba20f4dadbbfa..99983e8ddb9bf 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterCompositor.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterCompositor.h @@ -10,6 +10,7 @@ #include "flutter/fml/macros.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterView.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewProvider.h" #include "flutter/shell/platform/embedder/embedder.h" diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm index 07a9836f414c7..870ca4802f3d9 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm @@ -19,8 +19,6 @@ #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewEngineProvider.h" #include "flutter/shell/platform/embedder/embedder.h" -const uint64_t kFlutterDefaultViewId = 0; - /** * Constructs and returns a FlutterLocale struct corresponding to |locale|, which must outlive * the returned struct. @@ -82,8 +80,6 @@ @interface FlutterEngine () */ @property(nonatomic, strong) NSMutableArray* isResponseValid; -- (nullable FlutterViewController*)viewControllerForId:(uint64_t)viewId; - /** * Sends the list of user-preferred locales to the Flutter engine. */ @@ -217,6 +213,8 @@ @implementation FlutterEngine { // when the engine is destroyed. std::unique_ptr _macOSCompositor; + FlutterViewEngineProvider* _viewProvider; + // FlutterCompositor is copied and used in embedder.cc. FlutterCompositor _compositor; @@ -250,6 +248,7 @@ - (instancetype)initWithName:(NSString*)labelPrefix _currentMessengerConnection = 1; _allowHeadlessExecution = allowHeadlessExecution; _semanticsEnabled = NO; + _viewProvider = [[FlutterViewEngineProvider alloc] initWithEngine:self]; _isResponseValid = [[NSMutableArray alloc] initWithCapacity:1]; [_isResponseValid addObject:@YES]; @@ -412,41 +411,29 @@ - (void)loadAOTData:(NSString*)assetsDir { } - (void)setViewController:(FlutterViewController*)controller { - if (_viewController == controller) { - // From nil to nil, or from non-nil to the same controller. - return; - } - 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 create it with init(engine:, nibName, bundle:) instead.", - controller.engine); + if (_viewController != controller) { _viewController = controller; - [_viewController attachToEngine:self withId:kFlutterDefaultViewId]; - } else if (_viewController != nil && controller == nil) { - // From non-nil to nil. - [_viewController detachFromEngine]; - _viewController = nil; - if (!_allowHeadlessExecution) { + + if (_semanticsEnabled && _bridge) { + _bridge->UpdateDefaultViewController(_viewController); + } + + if (!controller && !_allowHeadlessExecution) { [self shutDownEngine]; } - } else { - // From non-nil to a different non-nil view controller. - NSAssert(NO, - @"Failed to set view controller to the engine: " - @"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.", - _viewController); } } - (FlutterCompositor*)createFlutterCompositor { - _macOSCompositor = std::make_unique( - [[FlutterViewEngineProvider alloc] initWithEngine:self], _platformViewController); + // TODO(richardjcai): Add support for creating a FlutterCompositor + // with a nil _viewController for headless engines. + // https://github.com/flutter/flutter/issues/71606 + if (!_viewController) { + return nil; + } + + _macOSCompositor = + std::make_unique(_viewProvider, _platformViewController); _compositor = {}; _compositor.struct_size = sizeof(FlutterCompositor); @@ -552,10 +539,10 @@ - (nonnull NSString*)executableName { } - (void)updateWindowMetrics { - if (!_engine || !self.viewController.viewLoaded) { + if (!_engine || !_viewController.viewLoaded) { return; } - NSView* view = self.viewController.flutterView; + NSView* view = _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; @@ -616,17 +603,6 @@ - (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; @@ -703,7 +679,9 @@ - (void)shutDownEngine { return; } - [self.viewController.flutterView shutdown]; + if (_viewController && _viewController.flutterView) { + [_viewController.flutterView shutdown]; + } FlutterEngineResult result = _embedderAPI.Deinitialize(_engine); if (result != kSuccess) { diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm index bf8da30fe1df8..a162fbb64c3cf 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm @@ -134,11 +134,14 @@ - (nonnull NSView*)createWithViewIdentifier:(int64_t)viewId arguments:(nullable EXPECT_TRUE([engine runWithEntrypoint:@"backgroundTest"]); EXPECT_TRUE(engine.running); - FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:engine - nibName:nil - bundle:nil]; + NSString* fixtures = @(flutter::testing::GetFixturesPath()); + FlutterDartProject* project = [[FlutterDartProject alloc] + initWithAssetsPath:fixtures + ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]]; + FlutterViewController* viewController = [[FlutterViewController alloc] initWithProject:project]; [viewController loadView]; viewController.flutterView.frame = CGRectMake(0, 0, 800, 600); + [engine setViewController:viewController]; latch.Wait(); } @@ -163,11 +166,14 @@ - (nonnull NSView*)createWithViewIdentifier:(int64_t)viewId arguments:(nullable EXPECT_TRUE([engine runWithEntrypoint:@"backgroundTest"]); EXPECT_TRUE(engine.running); - FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:engine - nibName:nil - bundle:nil]; + NSString* fixtures = @(flutter::testing::GetFixturesPath()); + FlutterDartProject* project = [[FlutterDartProject alloc] + initWithAssetsPath:fixtures + ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]]; + FlutterViewController* viewController = [[FlutterViewController alloc] initWithProject:project]; [viewController loadView]; viewController.flutterView.frame = CGRectMake(0, 0, 800, 600); + [engine setViewController:viewController]; viewController.flutterView.backgroundColor = [NSColor whiteColor]; latch.Wait(); @@ -187,10 +193,13 @@ - (nonnull NSView*)createWithViewIdentifier:(int64_t)viewId arguments:(nullable })); EXPECT_TRUE([engine runWithEntrypoint:@"main"]); // Set up view controller. - FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:engine - nibName:nil - bundle:nil]; + NSString* fixtures = @(testing::GetFixturesPath()); + FlutterDartProject* project = [[FlutterDartProject alloc] + initWithAssetsPath:fixtures + ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]]; + FlutterViewController* viewController = [[FlutterViewController alloc] initWithProject:project]; [viewController loadView]; + [engine setViewController:viewController]; // Enable the semantics. bool enabled_called = false; engine.embedderAPI.UpdateSemanticsEnabled = @@ -328,8 +337,6 @@ - (nonnull NSView*)createWithViewIdentifier:(int64_t)viewId arguments:(nullable FlutterSemanticsNode nodes[] = {root, child1}; update.nodes = nodes; update.custom_actions_count = 0; - // This call updates semantics for the default view, which does not exist, - // and therefore this call is invalid. But the engine should not crash. update_semantics_callback(&update, (__bridge void*)engine); // No crashes. @@ -348,6 +355,93 @@ - (nonnull NSView*)createWithViewIdentifier:(int64_t)viewId arguments:(nullable EXPECT_EQ(engine.viewController, nil); } +TEST_F(FlutterEngineTest, ResetsAccessibilityBridgeWhenSetsNewViewController) { + FlutterEngine* engine = GetFlutterEngine(); + // Capture the update callbacks before the embedder API initializes. + auto original_init = engine.embedderAPI.Initialize; + std::function update_semantics_callback; + engine.embedderAPI.Initialize = MOCK_ENGINE_PROC( + Initialize, ([&update_semantics_callback, &original_init]( + size_t version, const FlutterRendererConfig* config, + const FlutterProjectArgs* args, void* user_data, auto engine_out) { + update_semantics_callback = args->update_semantics_callback; + return original_init(version, config, args, user_data, engine_out); + })); + EXPECT_TRUE([engine runWithEntrypoint:@"main"]); + // Set up view controller. + NSString* fixtures = @(testing::GetFixturesPath()); + FlutterDartProject* project = [[FlutterDartProject alloc] + initWithAssetsPath:fixtures + ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]]; + FlutterViewController* viewController = [[FlutterViewController alloc] initWithProject:project]; + [viewController loadView]; + [engine setViewController:viewController]; + // Enable the semantics. + bool enabled_called = false; + engine.embedderAPI.UpdateSemanticsEnabled = + MOCK_ENGINE_PROC(UpdateSemanticsEnabled, ([&enabled_called](auto engine, bool enabled) { + enabled_called = enabled; + return kSuccess; + })); + engine.semanticsEnabled = YES; + EXPECT_TRUE(enabled_called); + // Send flutter semantics updates. + FlutterSemanticsNode root; + root.id = 0; + root.flags = static_cast(0); + root.actions = static_cast(0); + root.text_selection_base = -1; + root.text_selection_extent = -1; + root.label = "root"; + root.hint = ""; + root.value = ""; + root.increased_value = ""; + root.decreased_value = ""; + root.tooltip = ""; + root.child_count = 1; + int32_t children[] = {1}; + root.children_in_traversal_order = children; + root.custom_accessibility_actions_count = 0; + + FlutterSemanticsNode child1; + child1.id = 1; + child1.flags = static_cast(0); + child1.actions = static_cast(0); + child1.text_selection_base = -1; + child1.text_selection_extent = -1; + child1.label = "child 1"; + child1.hint = ""; + child1.value = ""; + child1.increased_value = ""; + child1.decreased_value = ""; + child1.tooltip = ""; + child1.child_count = 0; + child1.custom_accessibility_actions_count = 0; + + FlutterSemanticsUpdate update; + update.nodes_count = 2; + FlutterSemanticsNode nodes[] = {root, child1}; + update.nodes = nodes; + update.custom_actions_count = 0; + update_semantics_callback(&update, (__bridge void*)engine); + + auto native_root = engine.accessibilityBridge.lock()->GetFlutterPlatformNodeDelegateFromID(0); + EXPECT_FALSE(native_root.expired()); + + // Set up a new view controller. + FlutterViewController* newViewController = + [[FlutterViewController alloc] initWithProject:project]; + [newViewController loadView]; + [engine setViewController:newViewController]; + + auto new_native_root = engine.accessibilityBridge.lock()->GetFlutterPlatformNodeDelegateFromID(0); + // The tree is recreated and the old tree will be destroyed. + EXPECT_FALSE(new_native_root.expired()); + EXPECT_TRUE(native_root.expired()); + + [engine setViewController:nil]; +} + TEST_F(FlutterEngineTest, NativeCallbacks) { fml::AutoResetWaitableEvent latch; bool latch_called = false; @@ -371,11 +465,10 @@ - (nonnull NSView*)createWithViewIdentifier:(int64_t)viewId arguments:(nullable ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]]; FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"test" project:project]; - FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:engine - nibName:nil - bundle:nil]; + FlutterViewController* viewController = [[FlutterViewController alloc] initWithProject:project]; [viewController loadView]; viewController.flutterView.frame = CGRectMake(0, 0, 800, 600); + [engine setViewController:viewController]; EXPECT_TRUE([engine runWithEntrypoint:@"canCompositePlatformViews"]); diff --git a/shell/platform/darwin/macos/framework/Source/FlutterPlatformNodeDelegateMacTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterPlatformNodeDelegateMacTest.mm index 84372964f9501..a5809bef78d92 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterPlatformNodeDelegateMacTest.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterPlatformNodeDelegateMacTest.mm @@ -19,19 +19,18 @@ namespace flutter::testing { namespace { -// Returns a view controller configured for the text fixture resource configuration. -FlutterViewController* CreateTestViewController() { +// Returns an engine configured for the text fixture resource configuration. +FlutterEngine* CreateTestEngine() { NSString* fixtures = @(testing::GetFixturesPath()); FlutterDartProject* project = [[FlutterDartProject alloc] initWithAssetsPath:fixtures ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]]; - return [[FlutterViewController alloc] initWithProject:project]; + return [[FlutterEngine alloc] initWithName:@"test" project:project allowHeadlessExecution:true]; } } // namespace TEST(FlutterPlatformNodeDelegateMac, Basics) { - FlutterViewController* viewController = CreateTestViewController(); - FlutterEngine* engine = viewController.engine; + FlutterEngine* engine = CreateTestEngine(); engine.semanticsEnabled = YES; auto bridge = engine.accessibilityBridge.lock(); // Initialize ax node data. @@ -66,8 +65,7 @@ } TEST(FlutterPlatformNodeDelegateMac, SelectableTextHasCorrectSemantics) { - FlutterViewController* viewController = CreateTestViewController(); - FlutterEngine* engine = viewController.engine; + FlutterEngine* engine = CreateTestEngine(); engine.semanticsEnabled = YES; auto bridge = engine.accessibilityBridge.lock(); // Initialize ax node data. @@ -108,8 +106,7 @@ } TEST(FlutterPlatformNodeDelegateMac, SelectableTextWithoutSelectionReturnZeroRange) { - FlutterViewController* viewController = CreateTestViewController(); - FlutterEngine* engine = viewController.engine; + FlutterEngine* engine = CreateTestEngine(); engine.semanticsEnabled = YES; auto bridge = engine.accessibilityBridge.lock(); // Initialize ax node data. @@ -147,8 +144,15 @@ // NOLINTBEGIN(clang-analyzer-core.StackAddressEscape) TEST(FlutterPlatformNodeDelegateMac, CanPerformAction) { - FlutterViewController* viewController = CreateTestViewController(); - FlutterEngine* engine = viewController.engine; + FlutterEngine* engine = CreateTestEngine(); + + // Set up view controller. + NSString* fixtures = @(testing::GetFixturesPath()); + FlutterDartProject* project = [[FlutterDartProject alloc] + initWithAssetsPath:fixtures + ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]]; + FlutterViewController* viewController = [[FlutterViewController alloc] initWithProject:project]; + [engine setViewController:viewController]; // Attach the view to a NSWindow. NSWindow* window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 800, 600) @@ -218,9 +222,14 @@ // NOLINTEND(clang-analyzer-core.StackAddressEscape) TEST(FlutterPlatformNodeDelegateMac, TextFieldUsesFlutterTextField) { - FlutterViewController* viewController = CreateTestViewController(); - FlutterEngine* engine = viewController.engine; + FlutterEngine* engine = CreateTestEngine(); + NSString* fixtures = @(testing::GetFixturesPath()); + FlutterDartProject* project = [[FlutterDartProject alloc] + initWithAssetsPath:fixtures + ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]]; + FlutterViewController* viewController = [[FlutterViewController alloc] initWithProject:project]; [viewController loadView]; + [engine setViewController:viewController]; // Unit test localization is unnecessary. // NOLINTNEXTLINE(clang-analyzer-optin.osx.cocoa.localizability.NonLocalizedStringChecker) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterTextInputPluginTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterTextInputPluginTest.mm index 94c095d354d25..9f4a39d4b3818 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterTextInputPluginTest.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterTextInputPluginTest.mm @@ -4,7 +4,6 @@ #import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterViewController.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterDartProject_Internal.h" -#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/FlutterTextInputPlugin.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputSemanticsObject.h" @@ -38,16 +37,6 @@ @interface NSTextInputContext (Private) - (BOOL)isActive; @end -@interface TextInputTestViewController : FlutterViewController -@end - -@implementation TextInputTestViewController -- (nonnull FlutterView*)createFlutterViewWithMTLDevice:(id)device - commandQueue:(id)commandQueue { - return OCMClassMock([NSView class]); -} -@end - @interface FlutterInputPluginTestObjc : NSObject - (bool)testEmptyCompositionRange; - (bool)testClearClientDuringComposing; @@ -56,7 +45,7 @@ - (bool)testClearClientDuringComposing; @implementation FlutterInputPluginTestObjc - (bool)testEmptyCompositionRange { - id engineMock = flutter::testing::CreateMockFlutterEngine(@""); + id engineMock = OCMClassMock([FlutterEngine class]); id binaryMessengerMock = OCMProtocolMock(@protocol(FlutterBinaryMessenger)); OCMStub( // NOLINT(google-objc-avoid-throwing-exception) [engineMock binaryMessenger]) @@ -121,7 +110,7 @@ - (bool)testEmptyCompositionRange { } - (bool)testSetMarkedTextWithSelectionChange { - id engineMock = flutter::testing::CreateMockFlutterEngine(@""); + id engineMock = OCMClassMock([FlutterEngine class]); id binaryMessengerMock = OCMProtocolMock(@protocol(FlutterBinaryMessenger)); OCMStub( // NOLINT(google-objc-avoid-throwing-exception) [engineMock binaryMessenger]) @@ -189,7 +178,7 @@ - (bool)testSetMarkedTextWithSelectionChange { } - (bool)testSetMarkedTextWithReplacementRange { - id engineMock = flutter::testing::CreateMockFlutterEngine(@""); + id engineMock = OCMClassMock([FlutterEngine class]); id binaryMessengerMock = OCMProtocolMock(@protocol(FlutterBinaryMessenger)); OCMStub( // NOLINT(google-objc-avoid-throwing-exception) [engineMock binaryMessenger]) @@ -257,7 +246,7 @@ - (bool)testSetMarkedTextWithReplacementRange { } - (bool)testComposingRegionRemovedByFramework { - id engineMock = flutter::testing::CreateMockFlutterEngine(@""); + id engineMock = OCMClassMock([FlutterEngine class]); id binaryMessengerMock = OCMProtocolMock(@protocol(FlutterBinaryMessenger)); OCMStub( // NOLINT(google-objc-avoid-throwing-exception) [engineMock binaryMessenger]) @@ -335,7 +324,7 @@ - (bool)testComposingRegionRemovedByFramework { - (bool)testClearClientDuringComposing { // Set up FlutterTextInputPlugin. - id engineMock = flutter::testing::CreateMockFlutterEngine(@""); + id engineMock = OCMClassMock([FlutterEngine class]); id binaryMessengerMock = OCMProtocolMock(@protocol(FlutterBinaryMessenger)); OCMStub( // NOLINT(google-objc-avoid-throwing-exception) [engineMock binaryMessenger]) @@ -388,18 +377,26 @@ - (bool)testClearClientDuringComposing { } - (bool)testFirstRectForCharacterRange { - id engineMock = flutter::testing::CreateMockFlutterEngine(@""); + id engineMock = OCMClassMock([FlutterEngine class]); id binaryMessengerMock = OCMProtocolMock(@protocol(FlutterBinaryMessenger)); OCMStub( // NOLINT(google-objc-avoid-throwing-exception) [engineMock binaryMessenger]) .andReturn(binaryMessengerMock); - FlutterViewController* controllerMock = - [[TextInputTestViewController alloc] initWithEngine:engineMock nibName:nil bundle:nil]; - [controllerMock loadView]; - id viewMock = controllerMock.flutterView; + FlutterViewController* controllerMock = OCMClassMock([FlutterViewController class]); + OCMStub( // NOLINT(google-objc-avoid-throwing-exception) + [controllerMock engine]) + .andReturn(engineMock); + + id viewMock = OCMClassMock([NSView class]); OCMStub( // NOLINT(google-objc-avoid-throwing-exception) [viewMock bounds]) .andReturn(NSMakeRect(0, 0, 200, 200)); + OCMStub( // NOLINT(google-objc-avoid-throwing-exception) + controllerMock.viewLoaded) + .andReturn(YES); + OCMStub( // NOLINT(google-objc-avoid-throwing-exception) + [controllerMock flutterView]) + .andReturn(viewMock); id windowMock = OCMClassMock([NSWindow class]); OCMStub( // NOLINT(google-objc-avoid-throwing-exception) @@ -456,18 +453,26 @@ - (bool)testFirstRectForCharacterRange { } - (bool)testFirstRectForCharacterRangeAtInfinity { - id engineMock = flutter::testing::CreateMockFlutterEngine(@""); + id engineMock = OCMClassMock([FlutterEngine class]); id binaryMessengerMock = OCMProtocolMock(@protocol(FlutterBinaryMessenger)); OCMStub( // NOLINT(google-objc-avoid-throwing-exception) [engineMock binaryMessenger]) .andReturn(binaryMessengerMock); - FlutterViewController* controllerMock = - [[TextInputTestViewController alloc] initWithEngine:engineMock nibName:nil bundle:nil]; - [controllerMock loadView]; - id viewMock = controllerMock.flutterView; + FlutterViewController* controllerMock = OCMClassMock([FlutterViewController class]); + OCMStub( // NOLINT(google-objc-avoid-throwing-exception) + [controllerMock engine]) + .andReturn(engineMock); + + id viewMock = OCMClassMock([NSView class]); OCMStub( // NOLINT(google-objc-avoid-throwing-exception) [viewMock bounds]) .andReturn(NSMakeRect(0, 0, 200, 200)); + OCMStub( // NOLINT(google-objc-avoid-throwing-exception) + controllerMock.viewLoaded) + .andReturn(YES); + OCMStub( // NOLINT(google-objc-avoid-throwing-exception) + [controllerMock flutterView]) + .andReturn(viewMock); id windowMock = OCMClassMock([NSWindow class]); OCMStub( // NOLINT(google-objc-avoid-throwing-exception) @@ -510,18 +515,26 @@ - (bool)testFirstRectForCharacterRangeAtInfinity { } - (bool)testFirstRectForCharacterRangeWithEsotericAffineTransform { - id engineMock = flutter::testing::CreateMockFlutterEngine(@""); + id engineMock = OCMClassMock([FlutterEngine class]); id binaryMessengerMock = OCMProtocolMock(@protocol(FlutterBinaryMessenger)); OCMStub( // NOLINT(google-objc-avoid-throwing-exception) [engineMock binaryMessenger]) .andReturn(binaryMessengerMock); - FlutterViewController* controllerMock = - [[TextInputTestViewController alloc] initWithEngine:engineMock nibName:nil bundle:nil]; - [controllerMock loadView]; - id viewMock = controllerMock.flutterView; + FlutterViewController* controllerMock = OCMClassMock([FlutterViewController class]); + OCMStub( // NOLINT(google-objc-avoid-throwing-exception) + [controllerMock engine]) + .andReturn(engineMock); + + id viewMock = OCMClassMock([NSView class]); OCMStub( // NOLINT(google-objc-avoid-throwing-exception) [viewMock bounds]) .andReturn(NSMakeRect(0, 0, 200, 200)); + OCMStub( // NOLINT(google-objc-avoid-throwing-exception) + controllerMock.viewLoaded) + .andReturn(YES); + OCMStub( // NOLINT(google-objc-avoid-throwing-exception) + [controllerMock flutterView]) + .andReturn(viewMock); id windowMock = OCMClassMock([NSWindow class]); OCMStub( // NOLINT(google-objc-avoid-throwing-exception) @@ -582,7 +595,7 @@ - (bool)testFirstRectForCharacterRangeWithEsotericAffineTransform { } - (bool)testSetEditingStateWithTextEditingDelta { - id engineMock = flutter::testing::CreateMockFlutterEngine(@""); + id engineMock = OCMClassMock([FlutterEngine class]); id binaryMessengerMock = OCMProtocolMock(@protocol(FlutterBinaryMessenger)); OCMStub( // NOLINT(google-objc-avoid-throwing-exception) [engineMock binaryMessenger]) @@ -638,7 +651,7 @@ - (bool)testSetEditingStateWithTextEditingDelta { } - (bool)testOperationsThatTriggerDelta { - id engineMock = flutter::testing::CreateMockFlutterEngine(@""); + id engineMock = OCMClassMock([FlutterEngine class]); id binaryMessengerMock = OCMProtocolMock(@protocol(FlutterBinaryMessenger)); OCMStub( // NOLINT(google-objc-avoid-throwing-exception) [engineMock binaryMessenger]) @@ -755,7 +768,7 @@ - (bool)testOperationsThatTriggerDelta { } - (bool)testComposingWithDelta { - id engineMock = flutter::testing::CreateMockFlutterEngine(@""); + id engineMock = OCMClassMock([FlutterEngine class]); id binaryMessengerMock = OCMProtocolMock(@protocol(FlutterBinaryMessenger)); OCMStub( // NOLINT(google-objc-avoid-throwing-exception) [engineMock binaryMessenger]) @@ -992,7 +1005,7 @@ - (bool)testComposingWithDelta { } - (bool)testComposingWithDeltasWhenSelectionIsActive { - id engineMock = flutter::testing::CreateMockFlutterEngine(@""); + id engineMock = OCMClassMock([FlutterEngine class]); id binaryMessengerMock = OCMProtocolMock(@protocol(FlutterBinaryMessenger)); OCMStub( // NOLINT(google-objc-avoid-throwing-exception) [engineMock binaryMessenger]) @@ -1121,7 +1134,7 @@ - (bool)testPerformKeyEquivalent { } - (bool)unhandledKeyEquivalent { - id engineMock = flutter::testing::CreateMockFlutterEngine(@""); + id engineMock = OCMClassMock([FlutterEngine class]); id binaryMessengerMock = OCMProtocolMock(@protocol(FlutterBinaryMessenger)); OCMStub( // NOLINT(google-objc-avoid-throwing-exception) [engineMock binaryMessenger]) @@ -1197,7 +1210,7 @@ - (bool)unhandledKeyEquivalent { } - (bool)testLocalTextAndSelectionUpdateAfterDelta { - id engineMock = flutter::testing::CreateMockFlutterEngine(@""); + id engineMock = OCMClassMock([FlutterEngine class]); id binaryMessengerMock = OCMProtocolMock(@protocol(FlutterBinaryMessenger)); OCMStub( // NOLINT(google-objc-avoid-throwing-exception) [engineMock binaryMessenger]) @@ -1258,7 +1271,7 @@ - (bool)testLocalTextAndSelectionUpdateAfterDelta { } - (bool)testSelectorsAreForwardedToFramework { - id engineMock = flutter::testing::CreateMockFlutterEngine(@""); + id engineMock = OCMClassMock([FlutterEngine class]); id binaryMessengerMock = OCMProtocolMock(@protocol(FlutterBinaryMessenger)); OCMStub( // NOLINT(google-objc-avoid-throwing-exception) [engineMock binaryMessenger]) @@ -1401,10 +1414,13 @@ - (bool)testSelectorsAreForwardedToFramework { TEST(FlutterTextInputPluginTest, CanWorkWithFlutterTextField) { FlutterEngine* engine = CreateTestEngine(); - FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:engine - nibName:nil - bundle:nil]; + NSString* fixtures = @(testing::GetFixturesPath()); + FlutterDartProject* project = [[FlutterDartProject alloc] + initWithAssetsPath:fixtures + ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]]; + FlutterViewController* viewController = [[FlutterViewController alloc] initWithProject:project]; [viewController loadView]; + [engine setViewController:viewController]; // Create a NSWindow so that the native text field can become first responder. NSWindow* window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 800, 600) styleMask:NSBorderlessWindowMask @@ -1467,10 +1483,13 @@ - (bool)testSelectorsAreForwardedToFramework { TEST(FlutterTextInputPluginTest, CanNotBecomeResponderIfNoViewController) { FlutterEngine* engine = CreateTestEngine(); - FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:engine - nibName:nil - bundle:nil]; + NSString* fixtures = @(testing::GetFixturesPath()); + FlutterDartProject* project = [[FlutterDartProject alloc] + initWithAssetsPath:fixtures + ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]]; + FlutterViewController* viewController = [[FlutterViewController alloc] initWithProject:project]; [viewController loadView]; + [engine setViewController:viewController]; // Creates a NSWindow so that the native text field can become first responder. NSWindow* window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 800, 600) styleMask:NSBorderlessWindowMask @@ -1501,10 +1520,13 @@ - (bool)testSelectorsAreForwardedToFramework { TEST(FlutterTextInputPluginTest, IsAddedAndRemovedFromViewHierarchy) { FlutterEngine* engine = CreateTestEngine(); - FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:engine - nibName:nil - bundle:nil]; + NSString* fixtures = @(testing::GetFixturesPath()); + FlutterDartProject* project = [[FlutterDartProject alloc] + initWithAssetsPath:fixtures + ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]]; + FlutterViewController* viewController = [[FlutterViewController alloc] initWithProject:project]; [viewController loadView]; + [engine setViewController:viewController]; NSWindow* window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 800, 600) styleMask:NSBorderlessWindowMask diff --git a/shell/platform/darwin/macos/framework/Source/FlutterTextInputSemanticsObjectTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterTextInputSemanticsObjectTest.mm index 4e4b821a6115d..287b12713c24a 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterTextInputSemanticsObjectTest.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterTextInputSemanticsObjectTest.mm @@ -28,10 +28,13 @@ TEST(FlutterTextInputSemanticsObjectTest, DoesInitialize) { FlutterEngine* engine = CreateTestEngine(); { - FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:engine - nibName:nil - bundle:nil]; + NSString* fixtures = @(testing::GetFixturesPath()); + FlutterDartProject* project = [[FlutterDartProject alloc] + initWithAssetsPath:fixtures + ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]]; + FlutterViewController* viewController = [[FlutterViewController alloc] initWithProject:project]; [viewController loadView]; + [engine setViewController:viewController]; // Create a NSWindow so that the native text field can become first responder. NSWindow* window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 800, 600) styleMask:NSBorderlessWindowMask diff --git a/shell/platform/darwin/macos/framework/Source/FlutterView.h b/shell/platform/darwin/macos/framework/Source/FlutterView.h index 73425737fbd3a..ba70833b46b76 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterView.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterView.h @@ -9,6 +9,17 @@ #include +/** + * 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/FlutterViewController.mm b/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm index 4d41d066c8fd4..2c9e76e65990d 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm @@ -286,34 +286,21 @@ - (NSArray*)accessibilityChildren { @implementation FlutterViewController { // The project to run in this controller's engine. FlutterDartProject* _project; - - uint64_t _id; } -@dynamic id; @dynamic view; /** * Performs initialization that's common between the different init paths. */ -static void CommonInit(FlutterViewController* controller, FlutterEngine* engine) { - if (!engine) { - engine = [[FlutterEngine alloc] initWithName:@"io.flutter" - project:controller->_project - allowHeadlessExecution:NO]; +static void CommonInit(FlutterViewController* controller) { + if (!controller->_engine) { + controller->_engine = [[FlutterEngine alloc] initWithName:@"io.flutter" + project:controller->_project + allowHeadlessExecution:NO]; } - NSCAssert(controller.engine == nil, - @"The FlutterViewController is unexpectedly attached to " - @"engine %@ before initialization.", - controller.engine); - 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->_mouseTrackingMode = FlutterMouseTrackingModeInKeyWindow; controller->_textInputPlugin = [[FlutterTextInputPlugin alloc] initWithViewController:controller]; - [controller initializeKeyboard]; // macOS fires this message when changing IMEs. CFNotificationCenterRef cfCenter = CFNotificationCenterGetDistributedCenter(); __weak FlutterViewController* weakSelf = controller; @@ -326,7 +313,7 @@ - (instancetype)initWithCoder:(NSCoder*)coder { self = [super initWithCoder:coder]; NSAssert(self, @"Super init cannot be nil"); - CommonInit(self, nil); + CommonInit(self); return self; } @@ -334,7 +321,7 @@ - (instancetype)initWithNibName:(NSString*)nibNameOrNil bundle:(NSBundle*)nibBun self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; NSAssert(self, @"Super init cannot be nil"); - CommonInit(self, nil); + CommonInit(self); return self; } @@ -343,7 +330,7 @@ - (instancetype)initWithProject:(nullable FlutterDartProject*)project { NSAssert(self, @"Super init cannot be nil"); _project = project; - CommonInit(self, nil); + CommonInit(self); return self; } @@ -359,7 +346,13 @@ - (instancetype)initWithEngine:(nonnull FlutterEngine*)engine self = [super initWithNibName:nibName bundle:nibBundle]; if (self) { - CommonInit(self, engine); + _engine = engine; + CommonInit(self); + if (engine.running) { + [self loadView]; + engine.viewController = self; + [self initializeKeyboard]; + } } return self; @@ -377,7 +370,9 @@ - (void)loadView { NSLog(@"Unable to create FlutterView; no MTLDevice or MTLCommandQueue available."); return; } - flutterView = [self createFlutterViewWithMTLDevice:device commandQueue:commandQueue]; + flutterView = [[FlutterView alloc] initWithMTLDevice:device + commandQueue:commandQueue + reshapeListener:self]; if (_backgroundColor != nil) { [flutterView setBackgroundColor:_backgroundColor]; } @@ -428,33 +423,16 @@ - (void)setBackgroundColor:(NSColor*)color { [_flutterView setBackgroundColor:_backgroundColor]; } -- (uint64_t)id { - NSAssert([self attached], @"This view controller is not attched."); - return _id; -} - - (void)onPreEngineRestart { [self initializeKeyboard]; } -- (void)attachToEngine:(nonnull FlutterEngine*)engine withId:(uint64_t)viewId { - NSAssert(_engine == nil, @"Already attached to an engine %@.", _engine); - _engine = engine; - _id = viewId; -} - -- (void)detachFromEngine { - NSAssert(_engine != nil, @"Not attached to any engine."); - _engine = nil; -} - -- (BOOL)attached { - return _engine != nil; -} - #pragma mark - Private methods - (BOOL)launchEngine { + [self initializeKeyboard]; + + _engine.viewController = self; if (![_engine runWithEntrypoint:nil]) { return NO; } @@ -713,13 +691,6 @@ - (void)onAccessibilityStatusChanged:(BOOL)enabled { } } -- (nonnull FlutterView*)createFlutterViewWithMTLDevice:(id)device - commandQueue:(id)commandQueue { - return [[FlutterView alloc] initWithMTLDevice:device - commandQueue:commandQueue - reshapeListener:self]; -} - - (void)onKeyboardLayoutChanged { _keyboardLayoutData = nil; if (_keyboardLayoutNotifier != nil) { diff --git a/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTest.mm index 3dc8028b6aa61..dbc98870a0661 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTest.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTest.mm @@ -11,7 +11,6 @@ #import "flutter/shell/platform/darwin/common/framework/Headers/FlutterBinaryMessenger.h" #import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterEngine.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterDartProject_Internal.h" -#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/FlutterRenderer.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTestUtils.h" @@ -137,9 +136,11 @@ id MockGestureEvent(NSEventType type, NSEventPhase phase, double magnification, TEST(FlutterViewController, ReparentsPluginWhenAccessibilityDisabled) { FlutterEngine* engine = CreateTestEngine(); - FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:engine - nibName:nil - bundle:nil]; + NSString* fixtures = @(testing::GetFixturesPath()); + FlutterDartProject* project = [[FlutterDartProject alloc] + initWithAssetsPath:fixtures + ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]]; + FlutterViewController* viewController = [[FlutterViewController alloc] initWithProject:project]; [viewController loadView]; [engine setViewController:viewController]; // Creates a NSWindow so that sub view can be first responder. @@ -213,7 +214,7 @@ id MockGestureEvent(NSEventType type, NSEventPhase phase, double magnification, @implementation FlutterViewControllerTestObjC - (bool)testKeyEventsAreSentToFramework { - id engineMock = flutter::testing::CreateMockFlutterEngine(@""); + id engineMock = OCMClassMock([FlutterEngine class]); id binaryMessengerMock = OCMProtocolMock(@protocol(FlutterBinaryMessenger)); OCMStub( // NOLINT(google-objc-avoid-throwing-exception) [engineMock binaryMessenger]) @@ -251,7 +252,7 @@ - (bool)testKeyEventsAreSentToFramework { } - (bool)testKeyEventsArePropagatedIfNotHandled { - id engineMock = flutter::testing::CreateMockFlutterEngine(@""); + id engineMock = OCMClassMock([FlutterEngine class]); id binaryMessengerMock = OCMProtocolMock(@protocol(FlutterBinaryMessenger)); OCMStub( // NOLINT(google-objc-avoid-throwing-exception) [engineMock binaryMessenger]) @@ -306,7 +307,7 @@ - (bool)testKeyEventsArePropagatedIfNotHandled { } - (bool)testFlutterViewIsConfigured { - id engineMock = flutter::testing::CreateMockFlutterEngine(@""); + id engineMock = OCMClassMock([FlutterEngine class]); FlutterRenderer* renderer_ = [[FlutterRenderer alloc] initWithFlutterEngine:engineMock]; OCMStub([engineMock renderer]).andReturn(renderer_); @@ -327,7 +328,7 @@ - (bool)testFlutterViewIsConfigured { } - (bool)testFlagsChangedEventsArePropagatedIfNotHandled { - id engineMock = flutter::testing::CreateMockFlutterEngine(@""); + id engineMock = OCMClassMock([FlutterEngine class]); id binaryMessengerMock = OCMProtocolMock(@protocol(FlutterBinaryMessenger)); OCMStub( // NOLINT(google-objc-avoid-throwing-exception) [engineMock binaryMessenger]) @@ -380,7 +381,7 @@ - (bool)testFlagsChangedEventsArePropagatedIfNotHandled { } - (bool)testKeyEventsAreNotPropagatedIfHandled { - id engineMock = flutter::testing::CreateMockFlutterEngine(@""); + id engineMock = OCMClassMock([FlutterEngine class]); id binaryMessengerMock = OCMProtocolMock(@protocol(FlutterBinaryMessenger)); OCMStub( // NOLINT(google-objc-avoid-throwing-exception) [engineMock binaryMessenger]) @@ -435,7 +436,7 @@ - (bool)testKeyEventsAreNotPropagatedIfHandled { } - (bool)testKeyboardIsRestartedOnEngineRestart { - id engineMock = flutter::testing::CreateMockFlutterEngine(@""); + id engineMock = OCMClassMock([FlutterEngine class]); id binaryMessengerMock = OCMProtocolMock(@protocol(FlutterBinaryMessenger)); OCMStub( // NOLINT(google-objc-avoid-throwing-exception) [engineMock binaryMessenger]) @@ -497,7 +498,7 @@ + (void)respondFalseForSendEvent:(const FlutterKeyEvent&)event } - (bool)testTrackpadGesturesAreSentToFramework { - id engineMock = flutter::testing::CreateMockFlutterEngine(@""); + id engineMock = OCMClassMock([FlutterEngine class]); // Need to return a real renderer to allow view controller to load. FlutterRenderer* renderer_ = [[FlutterRenderer alloc] initWithFlutterEngine:engineMock]; OCMStub([engineMock renderer]).andReturn(renderer_); @@ -793,7 +794,7 @@ - (bool)testTrackpadGesturesAreSentToFramework { } - (bool)testViewWillAppearCalledMultipleTimes { - id engineMock = flutter::testing::CreateMockFlutterEngine(@""); + id engineMock = OCMClassMock([FlutterEngine class]); FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:engineMock nibName:@"" bundle:nil]; diff --git a/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h b/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h index c60c5354b8d8e..19870cf5b183b 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h @@ -4,7 +4,6 @@ #import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterViewController.h" -#import "flutter/shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMac.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterKeyboardViewDelegate.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterView.h" @@ -24,27 +23,9 @@ */ - (BOOL)isDispatchingKeyEvent:(nonnull NSEvent*)event; -/** - * Set the `engine` and `id` of this controller. - * - * This method is called by FlutterEngine. - */ -- (void)attachToEngine:(nonnull FlutterEngine*)engine withId:(uint64_t)viewId; - -/** - * Reset the `engine` and `id` of this controller. - * - * This method is called by FlutterEngine. - */ -- (void)detachFromEngine; - @end // Private methods made visible for testing @interface FlutterViewController (TestMethods) - (void)onAccessibilityStatusChanged:(BOOL)enabled; - -- (nonnull FlutterView*)createFlutterViewWithMTLDevice:(nonnull id)device - commandQueue:(nonnull id)commandQueue; - @end diff --git a/shell/platform/darwin/macos/framework/Source/FlutterViewProvider.h b/shell/platform/darwin/macos/framework/Source/FlutterViewProvider.h index f873c72266a95..98aca04055d5b 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterViewProvider.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterViewProvider.h @@ -4,8 +4,6 @@ #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterView.h" -extern const uint64_t kFlutterDefaultViewId; - /** * An interface to query FlutterView. *