• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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