1// Copyright 2013 The Flutter Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#define FML_USED_ON_EMBEDDER 6 7#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h" 8 9#include <memory> 10 11#include "flutter/fml/message_loop.h" 12#include "flutter/fml/platform/darwin/platform_version.h" 13#include "flutter/fml/trace_event.h" 14#include "flutter/shell/common/engine.h" 15#include "flutter/shell/common/platform_view.h" 16#include "flutter/shell/common/shell.h" 17#include "flutter/shell/common/switches.h" 18#include "flutter/shell/common/thread_host.h" 19#include "flutter/shell/platform/darwin/common/command_line.h" 20#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterBinaryMessengerRelay.h" 21#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.h" 22#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h" 23#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h" 24#import "flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.h" 25#import "flutter/shell/platform/darwin/ios/ios_surface.h" 26#import "flutter/shell/platform/darwin/ios/platform_view_ios.h" 27 28@interface FlutterEngine () <FlutterTextInputDelegate, FlutterBinaryMessenger> 29// Maintains a dictionary of plugin names that have registered with the engine. Used by 30// FlutterEngineRegistrar to implement a FlutterPluginRegistrar. 31@property(nonatomic, readonly) NSMutableDictionary* pluginPublications; 32 33@property(nonatomic, readwrite, copy) NSString* isolateId; 34@end 35 36@interface FlutterEngineRegistrar : NSObject <FlutterPluginRegistrar> 37- (instancetype)initWithPlugin:(NSString*)pluginKey flutterEngine:(FlutterEngine*)flutterEngine; 38@end 39 40@implementation FlutterEngine { 41 flutter::ThreadHost _threadHost; 42 std::unique_ptr<flutter::Shell> _shell; 43 NSString* _labelPrefix; 44 std::unique_ptr<fml::WeakPtrFactory<FlutterEngine>> _weakFactory; 45 46 fml::WeakPtr<FlutterViewController> _viewController; 47 48 std::unique_ptr<flutter::FlutterPlatformViewsController> _platformViewsController; 49 50 // Channels 51 fml::scoped_nsobject<FlutterPlatformPlugin> _platformPlugin; 52 fml::scoped_nsobject<FlutterTextInputPlugin> _textInputPlugin; 53 fml::scoped_nsobject<FlutterMethodChannel> _localizationChannel; 54 fml::scoped_nsobject<FlutterMethodChannel> _navigationChannel; 55 fml::scoped_nsobject<FlutterMethodChannel> _platformChannel; 56 fml::scoped_nsobject<FlutterMethodChannel> _platformViewsChannel; 57 fml::scoped_nsobject<FlutterMethodChannel> _textInputChannel; 58 fml::scoped_nsobject<FlutterBasicMessageChannel> _lifecycleChannel; 59 fml::scoped_nsobject<FlutterBasicMessageChannel> _systemChannel; 60 fml::scoped_nsobject<FlutterBasicMessageChannel> _settingsChannel; 61 62 int64_t _nextTextureId; 63 64 BOOL _allowHeadlessExecution; 65 FlutterBinaryMessengerRelay* _binaryMessenger; 66} 67 68- (instancetype)initWithName:(NSString*)labelPrefix { 69 return [self initWithName:labelPrefix allowHeadlessExecution:YES]; 70} 71 72- (instancetype)initWithName:(NSString*)labelPrefix 73 allowHeadlessExecution:(BOOL)allowHeadlessExecution { 74 self = [super init]; 75 NSAssert(self, @"Super init cannot be nil"); 76 NSAssert(labelPrefix, @"labelPrefix is required"); 77 78 _allowHeadlessExecution = allowHeadlessExecution; 79 _labelPrefix = [labelPrefix copy]; 80 81 _weakFactory = std::make_unique<fml::WeakPtrFactory<FlutterEngine>>(self); 82 83 _pluginPublications = [NSMutableDictionary new]; 84 _platformViewsController.reset(new flutter::FlutterPlatformViewsController()); 85 86 [self setupChannels]; 87 _binaryMessenger = [[FlutterBinaryMessengerRelay alloc] initWithParent:self]; 88 89 NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; 90 [center addObserver:self 91 selector:@selector(onMemoryWarning:) 92 name:UIApplicationDidReceiveMemoryWarningNotification 93 object:nil]; 94 95 return self; 96} 97 98- (void)dealloc { 99 [_pluginPublications release]; 100 _binaryMessenger.parent = nil; 101 [_binaryMessenger release]; 102 103 NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; 104 [center removeObserver:self name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; 105 106 [super dealloc]; 107} 108 109- (flutter::Shell&)shell { 110 FML_DCHECK(_shell); 111 return *_shell; 112} 113 114- (fml::WeakPtr<FlutterEngine>)getWeakPtr { 115 return _weakFactory->GetWeakPtr(); 116} 117 118- (void)updateViewportMetrics:(flutter::ViewportMetrics)viewportMetrics { 119 if (!self.platformView) { 120 return; 121 } 122 self.platformView->SetViewportMetrics(std::move(viewportMetrics)); 123} 124 125- (void)dispatchPointerDataPacket:(std::unique_ptr<flutter::PointerDataPacket>)packet { 126 if (!self.platformView) { 127 return; 128 } 129 self.platformView->DispatchPointerDataPacket(std::move(packet)); 130} 131 132- (fml::WeakPtr<flutter::PlatformView>)platformView { 133 FML_DCHECK(_shell); 134 return _shell->GetPlatformView(); 135} 136 137- (flutter::PlatformViewIOS*)iosPlatformView { 138 FML_DCHECK(_shell); 139 return static_cast<flutter::PlatformViewIOS*>(_shell->GetPlatformView().get()); 140} 141 142- (fml::RefPtr<fml::TaskRunner>)platformTaskRunner { 143 FML_DCHECK(_shell); 144 return _shell->GetTaskRunners().GetPlatformTaskRunner(); 145} 146 147- (fml::RefPtr<fml::TaskRunner>)GPUTaskRunner { 148 FML_DCHECK(_shell); 149 return _shell->GetTaskRunners().GetGPUTaskRunner(); 150} 151 152- (void)setViewController:(FlutterViewController*)viewController { 153 FML_DCHECK(self.iosPlatformView); 154 _viewController = [viewController getWeakPtr]; 155 self.iosPlatformView->SetOwnerViewController(_viewController); 156 [self maybeSetupPlatformViewChannels]; 157} 158 159- (void)notifyViewControllerDeallocated { 160 if (!_allowHeadlessExecution) { 161 [self destroyContext]; 162 } 163} 164 165- (void)destroyContext { 166 [self resetChannels]; 167 self.isolateId = nil; 168 _shell.reset(); 169 _threadHost.Reset(); 170 _platformViewsController.reset(); 171} 172 173- (FlutterViewController*)viewController { 174 if (!_viewController) { 175 return nil; 176 } 177 return _viewController.get(); 178} 179 180- (FlutterPlatformPlugin*)platformPlugin { 181 return _platformPlugin.get(); 182} 183- (flutter::FlutterPlatformViewsController*)platformViewsController { 184 return _platformViewsController.get(); 185} 186- (FlutterTextInputPlugin*)textInputPlugin { 187 return _textInputPlugin.get(); 188} 189- (FlutterMethodChannel*)localizationChannel { 190 return _localizationChannel.get(); 191} 192- (FlutterMethodChannel*)navigationChannel { 193 return _navigationChannel.get(); 194} 195- (FlutterMethodChannel*)platformChannel { 196 return _platformChannel.get(); 197} 198- (FlutterMethodChannel*)textInputChannel { 199 return _textInputChannel.get(); 200} 201- (FlutterBasicMessageChannel*)lifecycleChannel { 202 return _lifecycleChannel.get(); 203} 204- (FlutterBasicMessageChannel*)systemChannel { 205 return _systemChannel.get(); 206} 207- (FlutterBasicMessageChannel*)settingsChannel { 208 return _settingsChannel.get(); 209} 210 211- (NSURL*)observatoryUrl { 212 return nil; 213} 214 215- (void)resetChannels { 216 _localizationChannel.reset(); 217 _navigationChannel.reset(); 218 _platformChannel.reset(); 219 _platformViewsChannel.reset(); 220 _textInputChannel.reset(); 221 _lifecycleChannel.reset(); 222 _systemChannel.reset(); 223 _settingsChannel.reset(); 224} 225 226// If you add a channel, be sure to also update `resetChannels`. 227// Channels get a reference to the engine, and therefore need manual 228// cleanup for proper collection. 229 - (void)setupChannels { 230 231 } 232 233- (void)maybeSetupPlatformViewChannels { 234 if (_shell && self.shell.IsSetup()) { 235 [_platformChannel.get() setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { 236 [_platformPlugin.get() handleMethodCall:call result:result]; 237 }]; 238 239 [_platformViewsChannel.get() 240 setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { 241 _platformViewsController->OnMethodCall(call, result); 242 }]; 243 244 [_textInputChannel.get() setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { 245 [_textInputPlugin.get() handleMethodCall:call result:result]; 246 }]; 247 } 248} 249 250- (flutter::Rasterizer::Screenshot)screenshot:(flutter::Rasterizer::ScreenshotType)type 251 base64Encode:(bool)base64Encode { 252 return self.shell.Screenshot(type, base64Encode); 253} 254 255- (void)launchEngine:(NSString*)entrypoint libraryURI:(NSString*)libraryOrNil { 256 257} 258 259- (BOOL)createShell:(NSString*)entrypoint libraryURI:(NSString*)libraryURI { 260 if (_shell != nullptr) { 261 FML_LOG(WARNING) << "This FlutterEngine was already invoked."; 262 return NO; 263 } 264 265 static size_t shellCount = 1; 266 267 flutter::Settings settings = {};;// = [_dartProject.get() settings]; 268 269 if (libraryURI) { 270 FML_DCHECK(entrypoint) << "Must specify entrypoint if specifying library"; 271 settings.assets_path = entrypoint.UTF8String; 272 } else if (entrypoint) { 273 settings.assets_path = entrypoint.UTF8String; 274 } else { 275 settings.assets_path = std::string("main"); 276 } 277 278 const auto threadLabel = [NSString stringWithFormat:@"%@.%zu", _labelPrefix, shellCount++]; 279 FML_DLOG(INFO) << "Creating threadHost for " << threadLabel.UTF8String; 280 // The current thread will be used as the platform thread. Ensure that the message loop is 281 // initialized. 282 fml::MessageLoop::EnsureInitializedForCurrentThread(); 283 284 _threadHost = {threadLabel.UTF8String, // label 285 flutter::ThreadHost::Type::UI | flutter::ThreadHost::Type::GPU | 286 flutter::ThreadHost::Type::IO}; 287 288 // Lambda captures by pointers to ObjC objects are fine here because the 289 // create call is 290 // synchronous. 291 flutter::Shell::CreateCallback<flutter::PlatformView> on_create_platform_view = 292 [](flutter::Shell& shell) { 293 return std::make_unique<flutter::PlatformViewIOS>(shell, shell.GetTaskRunners()); 294 }; 295 296 flutter::Shell::CreateCallback<flutter::Rasterizer> on_create_rasterizer = 297 [](flutter::Shell& shell) { 298 return std::make_unique<flutter::Rasterizer>(shell, shell.GetTaskRunners()); 299 }; 300 301 if (flutter::IsIosEmbeddedViewsPreviewEnabled()) { 302 // Embedded views requires the gpu and the platform views to be the same. 303 // The plan is to eventually dynamically merge the threads when there's a 304 // platform view in the layer tree. 305 // For now we use a fixed thread configuration with the same thread used as the 306 // gpu and platform task runner. 307 // TODO(amirh/chinmaygarde): remove this, and dynamically change the thread configuration. 308 // https://github.com/flutter/flutter/issues/23975 309 310 flutter::TaskRunners task_runners(threadLabel.UTF8String, // label 311 fml::MessageLoop::GetCurrent().GetTaskRunner(), // platform 312 fml::MessageLoop::GetCurrent().GetTaskRunner(), // gpu 313 _threadHost.ui_thread->GetTaskRunner(), // ui 314 _threadHost.io_thread->GetTaskRunner() // io 315 ); 316 // Create the shell. This is a blocking operation. 317 _shell = flutter::Shell::Create(std::move(task_runners), // task runners 318 std::move(settings), // settings 319 on_create_platform_view, // platform view creation 320 on_create_rasterizer // rasterzier creation 321 ); 322 } else { 323 flutter::TaskRunners task_runners(threadLabel.UTF8String, // label 324 fml::MessageLoop::GetCurrent().GetTaskRunner(), // platform 325 _threadHost.gpu_thread->GetTaskRunner(), // gpu 326 _threadHost.ui_thread->GetTaskRunner(), // ui 327 _threadHost.io_thread->GetTaskRunner() // io 328 ); 329 // Create the shell. This is a blocking operation. 330 _shell = flutter::Shell::Create(std::move(task_runners), // task runners 331 std::move(settings), // settings 332 on_create_platform_view, // platform view creation 333 on_create_rasterizer // rasterzier creation 334 ); 335 } 336 337 if (_shell == nullptr) { 338 FML_LOG(ERROR) << "Could not start a shell FlutterEngine with entrypoint: " 339 << entrypoint.UTF8String; 340 } else { 341 [self setupChannels]; 342 if (!_platformViewsController) { 343 _platformViewsController.reset(new flutter::FlutterPlatformViewsController()); 344 } 345 346 [self maybeSetupPlatformViewChannels]; 347 } 348 349 return _shell != nullptr; 350} 351 352- (BOOL)runWithEntrypoint:(NSString*)entrypoint libraryURI:(NSString*)libraryURI { 353 if ([self createShell:entrypoint libraryURI:libraryURI]) { 354 [self launchEngine:entrypoint libraryURI:libraryURI]; 355 } 356 357 return _shell != nullptr; 358} 359 360- (BOOL)runWithEntrypoint:(NSString*)entrypoint { 361 return [self runWithEntrypoint:entrypoint libraryURI:nil]; 362} 363 364#pragma mark - Text input delegate 365 366- (void)updateEditingClient:(int)client withState:(NSDictionary*)state { 367 [_textInputChannel.get() invokeMethod:@"TextInputClient.updateEditingState" 368 arguments:@[ @(client), state ]]; 369} 370 371- (void)updateFloatingCursor:(FlutterFloatingCursorDragState)state 372 withClient:(int)client 373 withPosition:(NSDictionary*)position { 374 NSString* stateString; 375 switch (state) { 376 case FlutterFloatingCursorDragStateStart: 377 stateString = @"FloatingCursorDragState.start"; 378 break; 379 case FlutterFloatingCursorDragStateUpdate: 380 stateString = @"FloatingCursorDragState.update"; 381 break; 382 case FlutterFloatingCursorDragStateEnd: 383 stateString = @"FloatingCursorDragState.end"; 384 break; 385 } 386 [_textInputChannel.get() invokeMethod:@"TextInputClient.updateFloatingCursor" 387 arguments:@[ @(client), stateString, position ]]; 388} 389 390- (void)performAction:(FlutterTextInputAction)action withClient:(int)client { 391 NSString* actionString; 392 switch (action) { 393 case FlutterTextInputActionUnspecified: 394 // Where did the term "unspecified" come from? iOS has a "default" and Android 395 // has "unspecified." These 2 terms seem to mean the same thing but we need 396 // to pick just one. "unspecified" was chosen because "default" is often a 397 // reserved word in languages with switch statements (dart, java, etc). 398 actionString = @"TextInputAction.unspecified"; 399 break; 400 case FlutterTextInputActionDone: 401 actionString = @"TextInputAction.done"; 402 break; 403 case FlutterTextInputActionGo: 404 actionString = @"TextInputAction.go"; 405 break; 406 case FlutterTextInputActionSend: 407 actionString = @"TextInputAction.send"; 408 break; 409 case FlutterTextInputActionSearch: 410 actionString = @"TextInputAction.search"; 411 break; 412 case FlutterTextInputActionNext: 413 actionString = @"TextInputAction.next"; 414 break; 415 case FlutterTextInputActionContinue: 416 actionString = @"TextInputAction.continue"; 417 break; 418 case FlutterTextInputActionJoin: 419 actionString = @"TextInputAction.join"; 420 break; 421 case FlutterTextInputActionRoute: 422 actionString = @"TextInputAction.route"; 423 break; 424 case FlutterTextInputActionEmergencyCall: 425 actionString = @"TextInputAction.emergencyCall"; 426 break; 427 case FlutterTextInputActionNewline: 428 actionString = @"TextInputAction.newline"; 429 break; 430 } 431 [_textInputChannel.get() invokeMethod:@"TextInputClient.performAction" 432 arguments:@[ @(client), actionString ]]; 433} 434 435#pragma mark - Screenshot Delegate 436 437- (flutter::Rasterizer::Screenshot)takeScreenshot:(flutter::Rasterizer::ScreenshotType)type 438 asBase64Encoded:(BOOL)base64Encode { 439 FML_DCHECK(_shell) << "Cannot takeScreenshot without a shell"; 440 return _shell->Screenshot(type, base64Encode); 441} 442 443- (NSObject<FlutterBinaryMessenger>*)binaryMessenger { 444 return _binaryMessenger; 445} 446 447#pragma mark - FlutterBinaryMessenger 448 449- (void)sendOnChannel:(NSString*)channel message:(NSData*)message { 450 [self sendOnChannel:channel message:message binaryReply:nil]; 451} 452 453- (void)sendOnChannel:(NSString*)channel 454 message:(NSData*)message 455 binaryReply:(FlutterBinaryReply)callback { 456 NSAssert(channel, @"The channel must not be null"); 457 fml::RefPtr<flutter::PlatformMessageResponseDarwin> response = 458 (callback == nil) ? nullptr 459 : fml::MakeRefCounted<flutter::PlatformMessageResponseDarwin>( 460 ^(NSData* reply) { 461 callback(reply); 462 }, 463 _shell->GetTaskRunners().GetPlatformTaskRunner()); 464 fml::RefPtr<flutter::PlatformMessage> platformMessage = 465 (message == nil) ? fml::MakeRefCounted<flutter::PlatformMessage>(channel.UTF8String, response) 466 : fml::MakeRefCounted<flutter::PlatformMessage>( 467 channel.UTF8String, flutter::GetVectorFromNSData(message), response); 468 469 _shell->GetPlatformView()->DispatchPlatformMessage(platformMessage); 470} 471 472- (void)setMessageHandlerOnChannel:(NSString*)channel 473 binaryMessageHandler:(FlutterBinaryMessageHandler)handler { 474 NSAssert(channel, @"The channel must not be null"); 475 FML_DCHECK(_shell && _shell->IsSetup()); 476 self.iosPlatformView->GetPlatformMessageRouter().SetMessageHandler(channel.UTF8String, handler); 477} 478 479#pragma mark - FlutterTextureRegistry 480 481- (int64_t)registerTexture:(NSObject<FlutterTexture>*)texture { 482 int64_t textureId = _nextTextureId++; 483 self.iosPlatformView->RegisterExternalTexture(textureId, texture); 484 return textureId; 485} 486 487- (void)unregisterTexture:(int64_t)textureId { 488 _shell->GetPlatformView()->UnregisterTexture(textureId); 489} 490 491- (void)textureFrameAvailable:(int64_t)textureId { 492 _shell->GetPlatformView()->MarkTextureFrameAvailable(textureId); 493} 494 495- (NSString*)lookupKeyForAsset:(NSString*)asset { 496 return nil; 497} 498 499- (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package { 500 return nil; 501} 502 503- (id<FlutterPluginRegistry>)pluginRegistry { 504 return self; 505} 506 507#pragma mark - FlutterPluginRegistry 508 509- (NSObject<FlutterPluginRegistrar>*)registrarForPlugin:(NSString*)pluginKey { 510 NSAssert(self.pluginPublications[pluginKey] == nil, @"Duplicate plugin key: %@", pluginKey); 511 self.pluginPublications[pluginKey] = [NSNull null]; 512 return [[[FlutterEngineRegistrar alloc] initWithPlugin:pluginKey flutterEngine:self] autorelease]; 513} 514 515- (BOOL)hasPlugin:(NSString*)pluginKey { 516 return _pluginPublications[pluginKey] != nil; 517} 518 519- (NSObject*)valuePublishedByPlugin:(NSString*)pluginKey { 520 return _pluginPublications[pluginKey]; 521} 522 523#pragma mark - Memory Notifications 524 525- (void)onMemoryWarning:(NSNotification*)notification { 526 if (_shell) { 527 _shell->NotifyLowMemoryWarning(); 528 } 529 [_systemChannel sendMessage:@{@"type" : @"memoryPressure"}]; 530} 531 532@end 533 534@implementation FlutterEngineRegistrar { 535 NSString* _pluginKey; 536 FlutterEngine* _flutterEngine; 537} 538 539- (instancetype)initWithPlugin:(NSString*)pluginKey flutterEngine:(FlutterEngine*)flutterEngine { 540 self = [super init]; 541 NSAssert(self, @"Super init cannot be nil"); 542 _pluginKey = [pluginKey retain]; 543 _flutterEngine = [flutterEngine retain]; 544 return self; 545} 546 547- (void)dealloc { 548 [_pluginKey release]; 549 [_flutterEngine release]; 550 [super dealloc]; 551} 552 553- (NSObject<FlutterBinaryMessenger>*)messenger { 554 return _flutterEngine.binaryMessenger; 555} 556 557- (NSObject<FlutterTextureRegistry>*)textures { 558 return _flutterEngine; 559} 560 561- (void)publish:(NSObject*)value { 562 _flutterEngine.pluginPublications[_pluginKey] = value; 563} 564 565- (void)addMethodCallDelegate:(NSObject<FlutterPlugin>*)delegate 566 channel:(FlutterMethodChannel*)channel { 567 [channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { 568 [delegate handleMethodCall:call result:result]; 569 }]; 570} 571 572- (void)addApplicationDelegate:(NSObject<FlutterPlugin>*)delegate { 573 id<UIApplicationDelegate> appDelegate = [[UIApplication sharedApplication] delegate]; 574 if ([appDelegate conformsToProtocol:@protocol(FlutterAppLifeCycleProvider)]) { 575 id<FlutterAppLifeCycleProvider> lifeCycleProvider = 576 (id<FlutterAppLifeCycleProvider>)appDelegate; 577 [lifeCycleProvider addApplicationLifeCycleDelegate:delegate]; 578 } 579} 580 581- (NSString*)lookupKeyForAsset:(NSString*)asset { 582 return [_flutterEngine lookupKeyForAsset:asset]; 583} 584 585- (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package { 586 return [_flutterEngine lookupKeyForAsset:asset fromPackage:package]; 587} 588 589- (void)registerViewFactory:(NSObject<FlutterPlatformViewFactory>*)factory 590 withId:(NSString*)factoryId { 591 [_flutterEngine platformViewsController] -> RegisterViewFactory(factory, factoryId); 592} 593 594@end 595