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#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h" 8 9#include "flutter/common/task_runners.h" 10#include "flutter/fml/message_loop.h" 11#include "flutter/fml/platform/darwin/scoped_nsobject.h" 12#include "flutter/runtime/dart_vm.h" 13#include "flutter/shell/common/shell.h" 14#include "flutter/shell/common/switches.h" 15#include "flutter/shell/platform/darwin/common/command_line.h" 16#include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h" 17 18extern "C" { 19#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG 20// Used for debugging dart:* sources. 21extern const uint8_t kPlatformStrongDill[]; 22extern const intptr_t kPlatformStrongDillSize; 23#endif 24} 25 26static const char* kApplicationKernelSnapshotFileName = "kernel_blob.bin"; 27 28static flutter::Settings DefaultSettingsForProcess(NSBundle* bundle = nil) { 29 auto command_line = flutter::CommandLineFromNSProcessInfo(); 30 31 // Precedence: 32 // 1. Settings from the specified NSBundle. 33 // 2. Settings passed explicitly via command-line arguments. 34 // 3. Settings from the NSBundle with the default bundle ID. 35 // 4. Settings from the main NSBundle and default values. 36 37 NSBundle* mainBundle = [NSBundle mainBundle]; 38 NSBundle* engineBundle = [NSBundle bundleForClass:[FlutterViewController class]]; 39 40 bool hasExplicitBundle = bundle != nil; 41 if (bundle == nil) { 42 bundle = [NSBundle bundleWithIdentifier:[FlutterDartProject defaultBundleIdentifier]]; 43 } 44 if (bundle == nil) { 45 bundle = mainBundle; 46 } 47 48 auto settings = flutter::SettingsFromCommandLine(command_line); 49 50 settings.task_observer_add = [](intptr_t key, fml::closure callback) { 51 fml::MessageLoop::GetCurrent().AddTaskObserver(key, std::move(callback)); 52 }; 53 54 settings.task_observer_remove = [](intptr_t key) { 55 fml::MessageLoop::GetCurrent().RemoveTaskObserver(key); 56 }; 57 58 // The command line arguments may not always be complete. If they aren't, attempt to fill in 59 // defaults. 60 61 // Flutter ships the ICU data file in the the bundle of the engine. Look for it there. 62 if (settings.icu_data_path.size() == 0) { 63 NSString* icuDataPath = [engineBundle pathForResource:@"icudtl" ofType:@"dat"]; 64 if (icuDataPath.length > 0) { 65 settings.icu_data_path = icuDataPath.UTF8String; 66 } 67 } 68 69 if (flutter::DartVM::IsRunningPrecompiledCode()) { 70 if (hasExplicitBundle) { 71 NSString* executablePath = bundle.executablePath; 72 if ([[NSFileManager defaultManager] fileExistsAtPath:executablePath]) { 73 settings.application_library_path.push_back(executablePath.UTF8String); 74 } 75 } 76 77 // No application bundle specified. Try a known location from the main bundle's Info.plist. 78 if (settings.application_library_path.size() == 0) { 79 NSString* libraryName = [mainBundle objectForInfoDictionaryKey:@"FLTLibraryPath"]; 80 NSString* libraryPath = [mainBundle pathForResource:libraryName ofType:@""]; 81 if (libraryPath.length > 0) { 82 NSString* executablePath = [NSBundle bundleWithPath:libraryPath].executablePath; 83 if (executablePath.length > 0) { 84 settings.application_library_path.push_back(executablePath.UTF8String); 85 } 86 } 87 } 88 89 // In case the application bundle is still not specified, look for the App.framework in the 90 // Frameworks directory. 91 if (settings.application_library_path.size() == 0) { 92 NSString* applicationFrameworkPath = [mainBundle pathForResource:@"Frameworks/App.framework" 93 ofType:@""]; 94 if (applicationFrameworkPath.length > 0) { 95 NSString* executablePath = 96 [NSBundle bundleWithPath:applicationFrameworkPath].executablePath; 97 if (executablePath.length > 0) { 98 settings.application_library_path.push_back(executablePath.UTF8String); 99 } 100 } 101 } 102 } 103 104 // Checks to see if the flutter assets directory is already present. 105 if (settings.assets_path.size() == 0) { 106 NSString* assetsName = [FlutterDartProject flutterAssetsName:bundle]; 107 NSString* assetsPath = [bundle pathForResource:assetsName ofType:@""]; 108 109 if (assetsPath.length == 0) { 110 assetsPath = [mainBundle pathForResource:assetsName ofType:@""]; 111 } 112 113 if (assetsPath.length == 0) { 114 NSLog(@"Failed to find assets path for \"%@\"", assetsName); 115 } else { 116 settings.assets_path = assetsPath.UTF8String; 117 118 // Check if there is an application kernel snapshot in the assets directory we could 119 // potentially use. Looking for the snapshot makes sense only if we have a VM that can use 120 // it. 121 if (!flutter::DartVM::IsRunningPrecompiledCode()) { 122 NSURL* applicationKernelSnapshotURL = 123 [NSURL URLWithString:@(kApplicationKernelSnapshotFileName) 124 relativeToURL:[NSURL fileURLWithPath:assetsPath]]; 125 if ([[NSFileManager defaultManager] fileExistsAtPath:applicationKernelSnapshotURL.path]) { 126 settings.application_kernel_asset = applicationKernelSnapshotURL.path.UTF8String; 127 } else { 128 NSLog(@"Failed to find snapshot: %@", applicationKernelSnapshotURL.path); 129 } 130 } 131 } 132 } 133 134#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG 135 // There are no ownership concerns here as all mappings are owned by the 136 // embedder and not the engine. 137 auto make_mapping_callback = [](const uint8_t* mapping, size_t size) { 138 return [mapping, size]() { return std::make_unique<fml::NonOwnedMapping>(mapping, size); }; 139 }; 140 141 settings.dart_library_sources_kernel = 142 make_mapping_callback(kPlatformStrongDill, kPlatformStrongDillSize); 143#endif // FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG 144 145 return settings; 146} 147 148@implementation FlutterDartProject { 149 fml::scoped_nsobject<NSBundle> _precompiledDartBundle; 150 flutter::Settings _settings; 151} 152 153#pragma mark - Override base class designated initializers 154 155- (instancetype)init { 156 return [self initWithPrecompiledDartBundle:nil]; 157} 158 159#pragma mark - Designated initializers 160 161- (instancetype)initWithPrecompiledDartBundle:(NSBundle*)bundle { 162 self = [super init]; 163 164 if (self) { 165 _precompiledDartBundle.reset([bundle retain]); 166 _settings = DefaultSettingsForProcess(bundle); 167 } 168 169 return self; 170} 171 172#pragma mark - Settings accessors 173 174- (const flutter::Settings&)settings { 175 return _settings; 176} 177 178- (flutter::RunConfiguration)runConfiguration { 179 return [self runConfigurationForEntrypoint:nil]; 180} 181 182- (flutter::RunConfiguration)runConfigurationForEntrypoint:(NSString*)entrypointOrNil { 183 return [self runConfigurationForEntrypoint:entrypointOrNil libraryOrNil:nil]; 184} 185 186- (flutter::RunConfiguration)runConfigurationForEntrypoint:(NSString*)entrypointOrNil 187 libraryOrNil:(NSString*)dartLibraryOrNil { 188 auto config = flutter::RunConfiguration::InferFromSettings(_settings); 189 if (dartLibraryOrNil && entrypointOrNil) { 190 config.SetEntrypointAndLibrary(std::string([entrypointOrNil UTF8String]), 191 std::string([dartLibraryOrNil UTF8String])); 192 193 } else if (entrypointOrNil) { 194 config.SetEntrypoint(std::string([entrypointOrNil UTF8String])); 195 } 196 return config; 197} 198 199#pragma mark - Assets-related utilities 200 201+ (NSString*)flutterAssetsName:(NSBundle*)bundle { 202 if (bundle == nil) { 203 bundle = [NSBundle bundleWithIdentifier:[FlutterDartProject defaultBundleIdentifier]]; 204 } 205 if (bundle == nil) { 206 bundle = [NSBundle mainBundle]; 207 } 208 NSString* flutterAssetsName = [bundle objectForInfoDictionaryKey:@"FLTAssetsPath"]; 209 if (flutterAssetsName == nil) { 210 flutterAssetsName = @"Frameworks/App.framework/flutter_assets"; 211 } 212 return flutterAssetsName; 213} 214 215+ (NSString*)lookupKeyForAsset:(NSString*)asset { 216 return [self lookupKeyForAsset:asset fromBundle:nil]; 217} 218 219+ (NSString*)lookupKeyForAsset:(NSString*)asset fromBundle:(NSBundle*)bundle { 220 NSString* flutterAssetsName = [FlutterDartProject flutterAssetsName:bundle]; 221 return [NSString stringWithFormat:@"%@/%@", flutterAssetsName, asset]; 222} 223 224+ (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package { 225 return [self lookupKeyForAsset:asset fromPackage:package fromBundle:nil]; 226} 227 228+ (NSString*)lookupKeyForAsset:(NSString*)asset 229 fromPackage:(NSString*)package 230 fromBundle:(NSBundle*)bundle { 231 return [self lookupKeyForAsset:[NSString stringWithFormat:@"packages/%@/%@", package, asset] 232 fromBundle:bundle]; 233} 234 235+ (NSString*)defaultBundleIdentifier { 236 return @"io.flutter.flutter.app"; 237} 238 239@end 240