diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index f13271aa91373..d4b2c65b71969 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1048,7 +1048,7 @@ FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterAppDe FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterDartProject.mm FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterDartProject_Internal.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm -FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterEngineUnittests.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterExternalTextureGL.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterExternalTextureGL.mm diff --git a/shell/platform/darwin/macos/BUILD.gn b/shell/platform/darwin/macos/BUILD.gn index c844d92413f3c..36dd46512deaa 100644 --- a/shell/platform/darwin/macos/BUILD.gn +++ b/shell/platform/darwin/macos/BUILD.gn @@ -82,7 +82,10 @@ source_set("flutter_framework_source") { public_configs = [ "//flutter:config" ] - defines = [ "FLUTTER_FRAMEWORK" ] + defines = [ + "FLUTTER_FRAMEWORK", + "FLUTTER_ENGINE_NO_PROTOTYPES", + ] cflags_objcc = [ "-fobjc-arc" ] @@ -113,7 +116,7 @@ executable("flutter_desktop_darwin_unittests") { testonly = true sources = [ - "framework/Source/FlutterEngineUnittests.mm", + "framework/Source/FlutterEngineTest.mm", "framework/Source/FlutterViewControllerTest.mm", ] @@ -126,6 +129,7 @@ executable("flutter_desktop_darwin_unittests") { ":flutter_framework_source", "//flutter/shell/platform/darwin/common:framework_shared", "//flutter/shell/platform/embedder:embedder_as_internal_library", + "//flutter/shell/platform/embedder:embedder_test_utils", "//flutter/testing", "//flutter/testing:dart", "//flutter/testing:skia", diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm index 0ef5ad9654ef6..23794ed354da9 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm @@ -27,16 +27,6 @@ static FlutterLocale FlutterLocaleFromNSLocale(NSLocale* locale) { return flutterLocale; } -namespace { - -struct AotDataDeleter { - void operator()(FlutterEngineAOTData aot_data) { FlutterEngineCollectAOTData(aot_data); } -}; - -using UniqueAotDataPtr = std::unique_ptr<_FlutterEngineAOTData, AotDataDeleter>; - -} - /** * Private interface declaration for FlutterEngine. */ @@ -85,10 +75,10 @@ - (BOOL)populateTextureWithIdentifier:(int64_t)textureID - (void)postMainThreadTask:(FlutterTask)task targetTimeInNanoseconds:(uint64_t)targetTime; /** - * Loads the AOT snapshots and instructions from the elf bundle (app_elf_snapshot.so) if it is - * present in the assets directory. + * Loads the AOT snapshots and instructions from the elf bundle (app_elf_snapshot.so) into _aotData, + * if it is present in the assets directory. */ -- (UniqueAotDataPtr)loadAOTData:(NSString*)assetsDir; +- (void)loadAOTData:(NSString*)assetsDir; @end @@ -105,6 +95,7 @@ - (instancetype)initWithPlugin:(nonnull NSString*)pluginKey @implementation FlutterEngineRegistrar { NSString* _pluginKey; FlutterEngine* _flutterEngine; + FlutterEngineProcTable _embedderAPI; } - (instancetype)initWithPlugin:(NSString*)pluginKey flutterEngine:(FlutterEngine*)flutterEngine { @@ -201,7 +192,7 @@ @implementation FlutterEngine { NSMutableDictionary* _textures; // Pointer to the Dart AOT snapshot and instruction data. - UniqueAotDataPtr _aotData; + _FlutterEngineAOTData* _aotData; } - (instancetype)initWithName:(NSString*)labelPrefix project:(FlutterDartProject*)project { @@ -218,6 +209,8 @@ - (instancetype)initWithName:(NSString*)labelPrefix _messageHandlers = [[NSMutableDictionary alloc] init]; _textures = [[NSMutableDictionary alloc] init]; _allowHeadlessExecution = allowHeadlessExecution; + _embedderAPI.struct_size = sizeof(FlutterEngineProcTable); + FlutterEngineGetProcAddresses(&_embedderAPI); NSNotificationCenter* notificationCenter = [NSNotificationCenter defaultCenter]; [notificationCenter addObserver:self @@ -230,6 +223,9 @@ - (instancetype)initWithName:(NSString*)labelPrefix - (void)dealloc { [self shutDownEngine]; + if (_aotData) { + _embedderAPI.CollectAOTData(_aotData); + } } - (BOOL)runWithEntrypoint:(NSString*)entrypoint { @@ -301,19 +297,19 @@ - (BOOL)runWithEntrypoint:(NSString*)entrypoint { }; flutterArguments.custom_task_runners = &custom_task_runners; - _aotData = [self loadAOTData:_project.assetsPath]; + [self loadAOTData:_project.assetsPath]; if (_aotData) { - flutterArguments.aot_data = _aotData.get(); + flutterArguments.aot_data = _aotData; } - FlutterEngineResult result = FlutterEngineInitialize( + FlutterEngineResult result = _embedderAPI.Initialize( FLUTTER_ENGINE_VERSION, &rendererConfig, &flutterArguments, (__bridge void*)(self), &_engine); if (result != kSuccess) { NSLog(@"Failed to initialize Flutter engine: error %d", result); return NO; } - result = FlutterEngineRunInitialized(_engine); + result = _embedderAPI.RunInitialized(_engine); if (result != kSuccess) { NSLog(@"Failed to run an initialized engine: error %d", result); return NO; @@ -326,9 +322,9 @@ - (BOOL)runWithEntrypoint:(NSString*)entrypoint { return YES; } -- (UniqueAotDataPtr)loadAOTData:(NSString*)assetsDir { - if (!FlutterEngineRunsAOTCompiledDartCode()) { - return nullptr; +- (void)loadAOTData:(NSString*)assetsDir { + if (!_embedderAPI.RunsAOTCompiledDartCode()) { + return; } BOOL isDirOut = false; // required for NSFileManager fileExistsAtPath. @@ -339,21 +335,17 @@ - (UniqueAotDataPtr)loadAOTData:(NSString*)assetsDir { NSString* elfPath = [NSString pathWithComponents:@[ assetsDir, @"app_elf_snapshot.so" ]]; if (![fileManager fileExistsAtPath:elfPath isDirectory:&isDirOut]) { - return nullptr; + return; } FlutterEngineAOTDataSource source = {}; source.type = kFlutterEngineAOTDataSourceTypeElfPath; source.elf_path = [elfPath cStringUsingEncoding:NSUTF8StringEncoding]; - FlutterEngineAOTData data = nullptr; - auto result = FlutterEngineCreateAOTData(&source, &data); + auto result = _embedderAPI.CreateAOTData(&source, &_aotData); if (result != kSuccess) { NSLog(@"Failed to load AOT data from: %@", elfPath); - return nullptr; } - - return UniqueAotDataPtr(data); } - (void)setViewController:(FlutterViewController*)controller { @@ -409,13 +401,17 @@ - (void)updateDisplayConfig { display.refresh_rate = round(refreshRate); std::vector displays = {display}; - FlutterEngineNotifyDisplayUpdate(_engine, kFlutterEngineDisplaysUpdateTypeStartup, + _embedderAPI.NotifyDisplayUpdate(_engine, kFlutterEngineDisplaysUpdateTypeStartup, displays.data(), displays.size()); } CVDisplayLinkRelease(displayLinkRef); } +- (FlutterEngineProcTable&)embedderAPI { + return _embedderAPI; +} + - (void)updateWindowMetrics { if (!_engine) { return; @@ -433,11 +429,11 @@ - (void)updateWindowMetrics { .left = static_cast(scaledBounds.origin.x), .top = static_cast(scaledBounds.origin.y), }; - FlutterEngineSendWindowMetricsEvent(_engine, &windowMetricsEvent); + _embedderAPI.SendWindowMetricsEvent(_engine, &windowMetricsEvent); } - (void)sendPointerEvent:(const FlutterPointerEvent&)event { - FlutterEngineSendPointerEvent(_engine, &event, 1); + _embedderAPI.SendPointerEvent(_engine, &event, 1); } #pragma mark - Private methods @@ -462,7 +458,7 @@ - (void)sendUserLocales { std::transform( flutterLocales.begin(), flutterLocales.end(), std::back_inserter(flutterLocaleList), [](const auto& arg) -> const auto* { return &arg; }); - FlutterEngineUpdateLocales(_engine, flutterLocaleList.data(), flutterLocaleList.size()); + _embedderAPI.UpdateLocales(_engine, flutterLocaleList.data(), flutterLocaleList.size()); } - (bool)engineCallbackOnMakeCurrent { @@ -500,7 +496,7 @@ - (void)engineCallbackOnPlatformMessage:(const FlutterPlatformMessage*)message { FlutterBinaryReply binaryResponseHandler = ^(NSData* response) { if (responseHandle) { - FlutterEngineSendPlatformMessageResponse(self->_engine, responseHandle, + _embedderAPI.SendPlatformMessageResponse(self->_engine, responseHandle, static_cast(response.bytes), response.length); responseHandle = NULL; @@ -527,7 +523,7 @@ - (void)shutDownEngine { return; } - FlutterEngineResult result = FlutterEngineDeinitialize(_engine); + FlutterEngineResult result = _embedderAPI.Deinitialize(_engine); if (result != kSuccess) { NSLog(@"Could not de-initialize the Flutter engine: error %d", result); } @@ -535,7 +531,7 @@ - (void)shutDownEngine { // Balancing release for the retain in the task runner dispatch table. CFRelease((CFTypeRef)self); - result = FlutterEngineShutdown(_engine); + result = _embedderAPI.Shutdown(_engine); if (result != kSuccess) { NSLog(@"Failed to shut down Flutter engine: error %d", result); } @@ -568,7 +564,7 @@ - (void)sendOnChannel:(NSString*)channel delete captures; }; - FlutterEngineResult create_result = FlutterPlatformMessageCreateResponseHandle( + FlutterEngineResult create_result = _embedderAPI.PlatformMessageCreateResponseHandle( _engine, message_reply, captures.get(), &response_handle); if (create_result != kSuccess) { NSLog(@"Failed to create a FlutterPlatformMessageResponseHandle (%d)", create_result); @@ -585,7 +581,7 @@ - (void)sendOnChannel:(NSString*)channel .response_handle = response_handle, }; - FlutterEngineResult message_result = FlutterEngineSendPlatformMessage(_engine, &platformMessage); + FlutterEngineResult message_result = _embedderAPI.SendPlatformMessage(_engine, &platformMessage); if (message_result != kSuccess) { NSLog(@"Failed to send message to Flutter engine on channel '%@' (%d).", channel, message_result); @@ -593,7 +589,7 @@ - (void)sendOnChannel:(NSString*)channel if (response_handle != nullptr) { FlutterEngineResult release_result = - FlutterPlatformMessageReleaseResponseHandle(_engine, response_handle); + _embedderAPI.PlatformMessageReleaseResponseHandle(_engine, response_handle); if (release_result != kSuccess) { NSLog(@"Failed to release the response handle (%d).", release_result); }; @@ -628,30 +624,30 @@ - (int64_t)registerTexture:(id)texture { FlutterExternalTextureGL* FlutterTexture = [[FlutterExternalTextureGL alloc] initWithFlutterTexture:texture]; int64_t textureID = [FlutterTexture textureID]; - FlutterEngineRegisterExternalTexture(_engine, textureID); + _embedderAPI.RegisterExternalTexture(_engine, textureID); _textures[@(textureID)] = FlutterTexture; return textureID; } - (void)textureFrameAvailable:(int64_t)textureID { - FlutterEngineMarkExternalTextureFrameAvailable(_engine, textureID); + _embedderAPI.MarkExternalTextureFrameAvailable(_engine, textureID); } - (void)unregisterTexture:(int64_t)textureID { - FlutterEngineUnregisterExternalTexture(_engine, textureID); + _embedderAPI.UnregisterExternalTexture(_engine, textureID); [_textures removeObjectForKey:@(textureID)]; } #pragma mark - Task runner integration - (void)postMainThreadTask:(FlutterTask)task targetTimeInNanoseconds:(uint64_t)targetTime { - const auto engine_time = FlutterEngineGetCurrentTime(); + const auto engine_time = _embedderAPI.GetCurrentTime(); __weak FlutterEngine* weak_self = self; auto worker = ^{ FlutterEngine* strong_self = weak_self; if (strong_self && strong_self->_engine) { - auto result = FlutterEngineRunTask(strong_self->_engine, &task); + auto result = _embedderAPI.RunTask(strong_self->_engine, &task); if (result != kSuccess) { NSLog(@"Could not post a task to the Flutter engine."); } diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm new file mode 100644 index 0000000000000..a3f602a18432a --- /dev/null +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm @@ -0,0 +1,53 @@ +// 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/Headers/FlutterEngine.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterDartProject_Internal.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_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" + +namespace flutter::testing { + +namespace { +// 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 [[FlutterEngine alloc] initWithName:@"test" project:project allowHeadlessExecution:true]; +} +} // namespace + +TEST(FlutterEngine, CanLaunch) { + FlutterEngine* engine = CreateTestEngine(); + EXPECT_TRUE([engine runWithEntrypoint:@"main"]); + EXPECT_TRUE(engine.running); + [engine shutDownEngine]; +} + +TEST(FlutterEngine, MessengerSend) { + FlutterEngine* engine = CreateTestEngine(); + EXPECT_TRUE([engine runWithEntrypoint:@"main"]); + + NSData* test_message = [@"a message" dataUsingEncoding:NSUTF8StringEncoding]; + bool called = false; + + engine.embedderAPI.SendPlatformMessage = MOCK_ENGINE_PROC( + SendPlatformMessage, ([&called, test_message](auto engine, auto message) { + called = true; + EXPECT_STREQ(message->channel, "test"); + EXPECT_EQ(memcmp(message->message, test_message.bytes, message->message_size), 0); + return kSuccess; + })); + + [engine.binaryMessenger sendOnChannel:@"test" message:test_message]; + EXPECT_TRUE(called); + + [engine shutDownEngine]; +} + +} // flutter::testing diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngineUnittests.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngineUnittests.mm deleted file mode 100644 index 1ad6c564e7daf..0000000000000 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngineUnittests.mm +++ /dev/null @@ -1,25 +0,0 @@ -// 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/Headers/FlutterEngine.h" -#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterDartProject_Internal.h" -#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h" -#include "flutter/testing/testing.h" - -namespace flutter::testing { - -TEST(FlutterEngineTest, FlutterEngineCanLaunch) { - NSString* fixtures = @(testing::GetFixturesPath()); - FlutterDartProject* project = [[FlutterDartProject alloc] - initWithAssetsPath:fixtures - ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]]; - FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"test" - project:project - allowHeadlessExecution:true]; - ASSERT_TRUE([engine runWithEntrypoint:@"main"]); - ASSERT_TRUE(engine.running); - [engine shutDownEngine]; -} - -} // flutter::testing diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h b/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h index 2a01586efa27e..48a27da52c5c7 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h @@ -6,7 +6,7 @@ #import -#import "flutter/shell/platform/embedder/embedder.h" +#include "flutter/shell/platform/embedder/embedder.h" @interface FlutterEngine () @@ -21,6 +21,11 @@ */ @property(nonatomic, readonly, nullable) NSOpenGLContext* resourceContext; +/** + * Function pointers for interacting with the embedder.h API. + */ +@property(nonatomic) FlutterEngineProcTable& embedderAPI; + /** * Informs the engine that the associated view controller's view size has changed. */ diff --git a/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTest.mm index e696393b56bf7..ab73278d9c5bf 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTest.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTest.mm @@ -34,7 +34,7 @@ id mockViewController(NSString* pasteboardString) { return viewControllerMock; } -TEST(FlutterViewControllerTest, HasStringsWhenPasteboardEmpty) { +TEST(FlutterViewController, HasStringsWhenPasteboardEmpty) { // Mock FlutterViewController so that it behaves like the pasteboard is empty. id viewControllerMock = mockViewController(nil); @@ -49,11 +49,11 @@ id mockViewController(NSString* pasteboardString) { FlutterMethodCall* methodCallAfterClear = [FlutterMethodCall methodCallWithMethodName:@"Clipboard.hasStrings" arguments:nil]; [viewControllerMock handleMethodCall:methodCallAfterClear result:resultAfterClear]; - ASSERT_TRUE(calledAfterClear); - ASSERT_FALSE(valueAfterClear); + EXPECT_TRUE(calledAfterClear); + EXPECT_FALSE(valueAfterClear); } -TEST(FlutterViewControllerTest, HasStringsWhenPasteboardFull) { +TEST(FlutterViewController, HasStringsWhenPasteboardFull) { // Mock FlutterViewController so that it behaves like the pasteboard has a // valid string. id viewControllerMock = mockViewController(@"some string"); @@ -69,8 +69,8 @@ id mockViewController(NSString* pasteboardString) { FlutterMethodCall* methodCall = [FlutterMethodCall methodCallWithMethodName:@"Clipboard.hasStrings" arguments:nil]; [viewControllerMock handleMethodCall:methodCall result:result]; - ASSERT_TRUE(called); - ASSERT_TRUE(value); + EXPECT_TRUE(called); + EXPECT_TRUE(value); } } // flutter::testing