mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Avoid crashing and display error if the process cannot be prepared for JIT mode Dart VM. (#20980)
This commit is contained in:
parent
af90dd366f
commit
f0fb74b8fb
@ -582,8 +582,8 @@ FILE: ../../../flutter/runtime/embedder_resources.h
|
||||
FILE: ../../../flutter/runtime/fixtures/runtime_test.dart
|
||||
FILE: ../../../flutter/runtime/platform_data.cc
|
||||
FILE: ../../../flutter/runtime/platform_data.h
|
||||
FILE: ../../../flutter/runtime/ptrace_ios.cc
|
||||
FILE: ../../../flutter/runtime/ptrace_ios.h
|
||||
FILE: ../../../flutter/runtime/ptrace_check.cc
|
||||
FILE: ../../../flutter/runtime/ptrace_check.h
|
||||
FILE: ../../../flutter/runtime/runtime_controller.cc
|
||||
FILE: ../../../flutter/runtime/runtime_controller.h
|
||||
FILE: ../../../flutter/runtime/runtime_delegate.cc
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
// 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.
|
||||
// FLUTTER_NOLINT
|
||||
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
@ -129,7 +130,7 @@ void DartCallbackCache::LoadCacheFromDisk() {
|
||||
Document d;
|
||||
d.Parse(cache_contents.c_str());
|
||||
if (d.HasParseError() || !d.IsArray()) {
|
||||
FML_LOG(WARNING) << "Could not parse callback cache, aborting restore";
|
||||
FML_LOG(INFO) << "Could not parse callback cache, aborting restore";
|
||||
// TODO(bkonyi): log and bail (delete cache?)
|
||||
return;
|
||||
}
|
||||
|
||||
@ -55,8 +55,7 @@ source_set("runtime") {
|
||||
"embedder_resources.h",
|
||||
"platform_data.cc",
|
||||
"platform_data.h",
|
||||
"ptrace_ios.cc",
|
||||
"ptrace_ios.h",
|
||||
"ptrace_check.h",
|
||||
"runtime_controller.cc",
|
||||
"runtime_controller.h",
|
||||
"runtime_delegate.cc",
|
||||
@ -67,6 +66,11 @@ source_set("runtime") {
|
||||
"skia_concurrent_executor.h",
|
||||
]
|
||||
|
||||
if (is_ios && flutter_runtime_mode == "debug") {
|
||||
# These contain references to private APIs and this TU must only be compiled in debug runtime modes.
|
||||
sources += [ "ptrace_check.cc" ]
|
||||
}
|
||||
|
||||
public_deps = [ "//third_party/rapidjson" ]
|
||||
|
||||
public_configs = [ "//flutter:config" ]
|
||||
|
||||
@ -24,7 +24,7 @@
|
||||
#include "flutter/lib/ui/dart_ui.h"
|
||||
#include "flutter/runtime/dart_isolate.h"
|
||||
#include "flutter/runtime/dart_service_isolate.h"
|
||||
#include "flutter/runtime/ptrace_ios.h"
|
||||
#include "flutter/runtime/ptrace_check.h"
|
||||
#include "third_party/dart/runtime/include/bin/dart_io_api.h"
|
||||
#include "third_party/skia/include/core/SkExecutor.h"
|
||||
#include "third_party/tonic/converter/dart_converter.h"
|
||||
@ -330,7 +330,12 @@ DartVM::DartVM(std::shared_ptr<const DartVMData> vm_data,
|
||||
PushBackAll(&args, kDartWriteProtectCodeArgs,
|
||||
fml::size(kDartWriteProtectCodeArgs));
|
||||
#else
|
||||
EnsureDebuggedIOS(settings_);
|
||||
const bool tracing_result = EnableTracingIfNecessary(settings_);
|
||||
// This check should only trip if the embedding made no attempts to enable
|
||||
// tracing. At this point, it is too late display user visible messages. Just
|
||||
// log and die.
|
||||
FML_CHECK(tracing_result)
|
||||
<< "Tracing not enabled before attempting to run JIT mode VM.";
|
||||
#if TARGET_CPU_ARM
|
||||
// Tell Dart in JIT mode to not use integer division on armv7
|
||||
// Ideally, this would be detected at runtime by Dart.
|
||||
|
||||
@ -19,28 +19,41 @@
|
||||
// - go/decommissioning-dbc
|
||||
// - go/decommissioning-dbc-engine
|
||||
// - go/decommissioning-dbc-tools
|
||||
#include "flutter/common/settings.h"
|
||||
#include "flutter/fml/build_config.h" // For OS_IOS.
|
||||
|
||||
#if OS_IOS && (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG)
|
||||
#include "flutter/runtime/ptrace_check.h"
|
||||
|
||||
#if TRACING_CHECKS_NECESSARY
|
||||
|
||||
// These headers should only be needed in debug mode.
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include "flutter/fml/build_config.h"
|
||||
|
||||
// Being extra careful and adding additional landmines that will prevent
|
||||
// compilation of this TU in an incorrect runtime mode.
|
||||
static_assert(OS_IOS, "This translation unit is iOS specific.");
|
||||
static_assert(FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG,
|
||||
"This translation unit must only be compiled in the debug "
|
||||
"runtime mode as it "
|
||||
"contains private API usage.");
|
||||
|
||||
#define PT_TRACE_ME 0
|
||||
#define PT_SIGEXC 12
|
||||
extern "C" int ptrace(int request, pid_t pid, caddr_t addr, int data);
|
||||
|
||||
static bool DebuggedIOS(const flutter::Settings& vm_settings) {
|
||||
namespace flutter {
|
||||
|
||||
static bool IsLaunchedByFlutterCLI(const Settings& vm_settings) {
|
||||
// Only the Flutter CLI passes "--enable-checked-mode". Therefore, if the flag
|
||||
// is present, we have been launched by "ios-deploy" via "debugserver".
|
||||
//
|
||||
// We choose this flag because it is always passed to launch debug builds.
|
||||
if (vm_settings.enable_checked_mode) {
|
||||
return true;
|
||||
}
|
||||
return vm_settings.enable_checked_mode;
|
||||
}
|
||||
|
||||
static bool IsLaunchedByXcode() {
|
||||
// Use "sysctl()" to check if we're currently being debugged (e.g. by Xcode).
|
||||
// We could also check "getppid() != 1" (launchd), but this is more direct.
|
||||
const pid_t self = getpid();
|
||||
@ -48,7 +61,7 @@ static bool DebuggedIOS(const flutter::Settings& vm_settings) {
|
||||
|
||||
auto proc = std::make_unique<struct kinfo_proc>();
|
||||
size_t proc_size = sizeof(struct kinfo_proc);
|
||||
if (sysctl(mib, 4, proc.get(), &proc_size, nullptr, 0) < 0) {
|
||||
if (::sysctl(mib, 4, proc.get(), &proc_size, nullptr, 0) < 0) {
|
||||
FML_LOG(ERROR) << "Could not execute sysctl() to get current process info: "
|
||||
<< strerror(errno);
|
||||
return false;
|
||||
@ -57,18 +70,16 @@ static bool DebuggedIOS(const flutter::Settings& vm_settings) {
|
||||
return proc->kp_proc.p_flag & P_TRACED;
|
||||
}
|
||||
|
||||
void EnsureDebuggedIOS(const flutter::Settings& vm_settings) {
|
||||
if (DebuggedIOS(vm_settings)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ptrace(PT_TRACE_ME, 0, nullptr, 0) == -1) {
|
||||
static bool EnableTracingManually(const Settings& vm_settings) {
|
||||
if (::ptrace(PT_TRACE_ME, 0, nullptr, 0) == -1) {
|
||||
FML_LOG(ERROR) << "Could not call ptrace(PT_TRACE_ME): " << strerror(errno);
|
||||
// No use trying PT_SIGEXC -- it's only needed if PT_TRACE_ME succeeds.
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
if (ptrace(PT_SIGEXC, 0, nullptr, 0) == -1) {
|
||||
|
||||
if (::ptrace(PT_SIGEXC, 0, nullptr, 0) == -1) {
|
||||
FML_LOG(ERROR) << "Could not call ptrace(PT_SIGEXC): " << strerror(errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
// The previous operation causes this process to not be reaped after it
|
||||
@ -78,11 +89,12 @@ void EnsureDebuggedIOS(const flutter::Settings& vm_settings) {
|
||||
size_t maxproc = 0;
|
||||
size_t maxproc_size = sizeof(size_t);
|
||||
const int sysctl_result =
|
||||
sysctlbyname("kern.maxproc", &maxproc, &maxproc_size, nullptr, 0);
|
||||
::sysctlbyname("kern.maxproc", &maxproc, &maxproc_size, nullptr, 0);
|
||||
if (sysctl_result < 0) {
|
||||
FML_LOG(ERROR)
|
||||
<< "Could not execute sysctl() to determine process count limit: "
|
||||
<< strerror(errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* warning =
|
||||
@ -98,6 +110,39 @@ void EnsureDebuggedIOS(const flutter::Settings& vm_settings) {
|
||||
{
|
||||
FML_LOG(ERROR) << warning;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // OS_IOS && (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG)
|
||||
static bool EnableTracingIfNecessaryOnce(const Settings& vm_settings) {
|
||||
if (IsLaunchedByFlutterCLI(vm_settings)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (IsLaunchedByXcode()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return EnableTracingManually(vm_settings);
|
||||
}
|
||||
|
||||
static TracingResult sTracingResult = TracingResult::kNotAttempted;
|
||||
|
||||
bool EnableTracingIfNecessaryImpl(const Settings& vm_settings) {
|
||||
static std::once_flag tracing_flag;
|
||||
|
||||
std::call_once(tracing_flag, [&vm_settings]() {
|
||||
sTracingResult = EnableTracingIfNecessaryOnce(vm_settings)
|
||||
? TracingResult::kEnabled
|
||||
: TracingResult::kDisabled;
|
||||
});
|
||||
return sTracingResult != TracingResult::kDisabled;
|
||||
}
|
||||
|
||||
TracingResult GetTracingResultImpl() {
|
||||
return sTracingResult;
|
||||
}
|
||||
|
||||
} // namespace flutter
|
||||
|
||||
#endif // TRACING_CHECKS_NECESSARY
|
||||
72
runtime/ptrace_check.h
Normal file
72
runtime/ptrace_check.h
Normal file
@ -0,0 +1,72 @@
|
||||
// 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.
|
||||
|
||||
#ifndef FLUTTER_RUNTIME_PTRACE_CHECK_H_
|
||||
#define FLUTTER_RUNTIME_PTRACE_CHECK_H_
|
||||
|
||||
#include "flutter/common/settings.h"
|
||||
#include "flutter/fml/build_config.h"
|
||||
|
||||
namespace flutter {
|
||||
|
||||
#define TRACING_CHECKS_NECESSARY \
|
||||
OS_IOS && !TARGET_OS_SIMULATOR && \
|
||||
(FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG)
|
||||
|
||||
enum class TracingResult {
|
||||
kNotAttempted,
|
||||
kEnabled,
|
||||
kNotNecessary = kEnabled,
|
||||
kDisabled,
|
||||
};
|
||||
|
||||
#if TRACING_CHECKS_NECESSARY
|
||||
bool EnableTracingIfNecessaryImpl(const Settings& vm_settings);
|
||||
TracingResult GetTracingResultImpl();
|
||||
#endif // TRACING_CHECKS_NECESSARY
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
/// @brief Enables tracing in the process so that JIT mode VMs may be
|
||||
/// launched. Explicitly enabling tracing is not required on all
|
||||
/// platforms. On platforms where it is not required, calling this
|
||||
/// method will return true. If tracing is required but cannot be
|
||||
/// enabled, it is the responsibility of the caller to display the
|
||||
/// appropriate error message to the user as subsequent attempts to
|
||||
/// launch the VM in JIT mode will cause process termination.
|
||||
///
|
||||
/// This method may be called multiple times and will return the
|
||||
/// same result. There are no threading restrictions.
|
||||
///
|
||||
/// @param[in] vm_settings The settings used to launch the VM.
|
||||
///
|
||||
/// @return If tracing was enabled.
|
||||
///
|
||||
inline bool EnableTracingIfNecessary(const Settings& vm_settings) {
|
||||
#if TRACING_CHECKS_NECESSARY
|
||||
return EnableTracingIfNecessaryImpl(vm_settings);
|
||||
#else // TRACING_CHECKS_NECESSARY
|
||||
return true;
|
||||
#endif // TRACING_CHECKS_NECESSARY
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
/// @brief Returns if a tracing check has been performed and its result. To
|
||||
/// enable tracing, the Settings object used to launch the VM is
|
||||
/// required. Components may want to display messages based on the
|
||||
/// result of a previous tracing check without actually having the
|
||||
/// settings object. This accessor can be used instead.
|
||||
///
|
||||
/// @return The tracing result.
|
||||
///
|
||||
inline TracingResult GetTracingResult() {
|
||||
#if TRACING_CHECKS_NECESSARY
|
||||
return GetTracingResultImpl();
|
||||
#else // TRACING_CHECKS_NECESSARY
|
||||
return TracingResult::kNotNecessary;
|
||||
#endif // TRACING_CHECKS_NECESSARY
|
||||
}
|
||||
|
||||
} // namespace flutter
|
||||
|
||||
#endif // FLUTTER_RUNTIME_PTRACE_CHECK_H_
|
||||
@ -1,18 +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.
|
||||
|
||||
#ifndef FLUTTER_RUNTIME_PTRACE_IOS_H_
|
||||
#define FLUTTER_RUNTIME_PTRACE_IOS_H_
|
||||
|
||||
#include "flutter/common/settings.h"
|
||||
|
||||
#if OS_IOS && (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG)
|
||||
|
||||
// Ensure that the current process is or was ptrace()-d at some point in its
|
||||
// life. Can only be used within debug builds for iOS.
|
||||
void EnsureDebuggedIOS(const flutter::Settings& vm_settings);
|
||||
|
||||
#endif
|
||||
|
||||
#endif // FLUTTER_RUNTIME_PTRACE_IOS_H_
|
||||
@ -11,6 +11,7 @@
|
||||
#include "flutter/fml/message_loop.h"
|
||||
#include "flutter/fml/platform/darwin/platform_version.h"
|
||||
#include "flutter/fml/trace_event.h"
|
||||
#include "flutter/runtime/ptrace_check.h"
|
||||
#include "flutter/shell/common/engine.h"
|
||||
#include "flutter/shell/common/platform_view.h"
|
||||
#include "flutter/shell/common/shell.h"
|
||||
@ -114,6 +115,16 @@ static constexpr int kNumProfilerSamplesPerSec = 5;
|
||||
else
|
||||
_dartProject.reset([project retain]);
|
||||
|
||||
if (!EnableTracingIfNecessary([_dartProject.get() settings])) {
|
||||
NSLog(
|
||||
@"Cannot create a FlutterEngine instance in debug mode without Flutter tooling or "
|
||||
@"Xcode.\n\nTo launch in debug mode in iOS 14+, run flutter run from Flutter tools, run "
|
||||
@"from an IDE with a Flutter IDE plugin or run the iOS project from Xcode.\nAlternatively "
|
||||
@"profile and release mode apps can be launched from the home screen.");
|
||||
[self release];
|
||||
return nil;
|
||||
}
|
||||
|
||||
_pluginPublications = [NSMutableDictionary new];
|
||||
_registrars = [[NSMutableDictionary alloc] init];
|
||||
_platformViewsController.reset(new flutter::FlutterPlatformViewsController());
|
||||
@ -514,6 +525,7 @@ static constexpr int kNumProfilerSamplesPerSec = 5;
|
||||
_threadHost.ui_thread->GetTaskRunner(), // ui
|
||||
_threadHost.io_thread->GetTaskRunner() // io
|
||||
);
|
||||
|
||||
// Create the shell. This is a blocking operation.
|
||||
_shell = flutter::Shell::Create(std::move(task_runners), // task runners
|
||||
std::move(platformData), // window data
|
||||
|
||||
@ -40,7 +40,12 @@
|
||||
}
|
||||
|
||||
- (instancetype)initWithDelegate:(id<FlutterViewEngineDelegate>)delegate opaque:(BOOL)opaque {
|
||||
FML_DCHECK(delegate) << "Delegate must not be nil.";
|
||||
if (delegate == nil) {
|
||||
NSLog(@"FlutterView delegate was nil.");
|
||||
[self release];
|
||||
return nil;
|
||||
}
|
||||
|
||||
self = [super initWithFrame:CGRectNull];
|
||||
|
||||
if (self) {
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
#include "flutter/fml/message_loop.h"
|
||||
#include "flutter/fml/platform/darwin/platform_version.h"
|
||||
#include "flutter/fml/platform/darwin/scoped_nsobject.h"
|
||||
#include "flutter/runtime/ptrace_check.h"
|
||||
#include "flutter/shell/common/thread_host.h"
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterBinaryMessengerRelay.h"
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h"
|
||||
@ -71,7 +72,7 @@ typedef enum UIAccessibilityContrast : NSInteger {
|
||||
BOOL _initialized;
|
||||
BOOL _viewOpaque;
|
||||
BOOL _engineNeedsLaunch;
|
||||
NSMutableSet<NSNumber*>* _ongoingTouches;
|
||||
fml::scoped_nsobject<NSMutableSet<NSNumber*>> _ongoingTouches;
|
||||
// This scroll view is a workaround to accomodate iOS 13 and higher. There isn't a way to get
|
||||
// touches on the status bar to trigger scrolling to the top of a scroll view. We place a
|
||||
// UIScrollView with height zero and a content offset so we can get those events. See also:
|
||||
@ -102,7 +103,7 @@ typedef enum UIAccessibilityContrast : NSInteger {
|
||||
_engineNeedsLaunch = NO;
|
||||
_flutterView.reset([[FlutterView alloc] initWithDelegate:_engine opaque:self.isViewOpaque]);
|
||||
_weakFactory = std::make_unique<fml::WeakPtrFactory<FlutterViewController>>(self);
|
||||
_ongoingTouches = [[NSMutableSet alloc] init];
|
||||
_ongoingTouches.reset([[NSMutableSet alloc] init]);
|
||||
|
||||
[self performCommonViewControllerInitialization];
|
||||
[engine setViewController:self];
|
||||
@ -145,7 +146,7 @@ typedef enum UIAccessibilityContrast : NSInteger {
|
||||
|
||||
- (void)awakeFromNib {
|
||||
[super awakeFromNib];
|
||||
if (!_engine.get()) {
|
||||
if (!_engine) {
|
||||
[self sharedSetupWithProject:nil initialRoute:nil];
|
||||
}
|
||||
}
|
||||
@ -156,15 +157,22 @@ typedef enum UIAccessibilityContrast : NSInteger {
|
||||
|
||||
- (void)sharedSetupWithProject:(nullable FlutterDartProject*)project
|
||||
initialRoute:(nullable NSString*)initialRoute {
|
||||
auto engine = fml::scoped_nsobject<FlutterEngine>{[[FlutterEngine alloc]
|
||||
initWithName:@"io.flutter"
|
||||
project:project
|
||||
allowHeadlessExecution:self.engineAllowHeadlessExecution]};
|
||||
|
||||
if (!engine) {
|
||||
return;
|
||||
}
|
||||
|
||||
_viewOpaque = YES;
|
||||
_weakFactory = std::make_unique<fml::WeakPtrFactory<FlutterViewController>>(self);
|
||||
_engine.reset([[FlutterEngine alloc] initWithName:@"io.flutter"
|
||||
project:project
|
||||
allowHeadlessExecution:self.engineAllowHeadlessExecution]);
|
||||
_engine = std::move(engine);
|
||||
_flutterView.reset([[FlutterView alloc] initWithDelegate:_engine opaque:self.isViewOpaque]);
|
||||
[_engine.get() createShell:nil libraryURI:nil initialRoute:initialRoute];
|
||||
_engineNeedsLaunch = YES;
|
||||
_ongoingTouches = [[NSMutableSet alloc] init];
|
||||
_ongoingTouches.reset([[NSMutableSet alloc] init]);
|
||||
[self loadDefaultSplashScreenView];
|
||||
[self performCommonViewControllerInitialization];
|
||||
}
|
||||
@ -310,8 +318,38 @@ typedef enum UIAccessibilityContrast : NSInteger {
|
||||
|
||||
#pragma mark - Loading the view
|
||||
|
||||
static UIView* GetViewOrPlaceholder(UIView* existing_view) {
|
||||
if (existing_view) {
|
||||
return existing_view;
|
||||
}
|
||||
|
||||
auto placeholder = [[[UIView alloc] init] autorelease];
|
||||
|
||||
placeholder.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
placeholder.backgroundColor = UIColor.whiteColor;
|
||||
placeholder.autoresizesSubviews = YES;
|
||||
|
||||
// Only add the label when we know we have failed to enable tracing (and it was necessary).
|
||||
// Otherwise, a spurious warning will be shown in cases where an engine cannot be initialized for
|
||||
// other reasons.
|
||||
if (flutter::GetTracingResult() == flutter::TracingResult::kDisabled) {
|
||||
auto messageLabel = [[[UILabel alloc] init] autorelease];
|
||||
messageLabel.numberOfLines = 0u;
|
||||
messageLabel.textAlignment = NSTextAlignmentCenter;
|
||||
messageLabel.autoresizingMask =
|
||||
UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
messageLabel.text =
|
||||
@"In iOS 14+, Flutter application in debug mode can only be launched from Flutter tooling, "
|
||||
@"IDEs with Flutter plugins or from Xcode.\n\nAlternatively, build in profile or release "
|
||||
@"modes to enable re-launching from the home screen.";
|
||||
[placeholder addSubview:messageLabel];
|
||||
}
|
||||
|
||||
return placeholder;
|
||||
}
|
||||
|
||||
- (void)loadView {
|
||||
self.view = _flutterView.get();
|
||||
self.view = GetViewOrPlaceholder(_flutterView.get());
|
||||
self.view.multipleTouchEnabled = YES;
|
||||
self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
|
||||
@ -346,6 +384,9 @@ static void sendFakeTouchEvent(FlutterEngine* engine,
|
||||
}
|
||||
|
||||
- (BOOL)scrollViewShouldScrollToTop:(UIScrollView*)scrollView {
|
||||
if (!_engine) {
|
||||
return NO;
|
||||
}
|
||||
CGPoint statusBarPoint = CGPointZero;
|
||||
sendFakeTouchEvent(_engine.get(), statusBarPoint, flutter::PointerData::Change::kDown);
|
||||
sendFakeTouchEvent(_engine.get(), statusBarPoint, flutter::PointerData::Change::kUp);
|
||||
@ -414,6 +455,10 @@ static void sendFakeTouchEvent(FlutterEngine* engine,
|
||||
}
|
||||
|
||||
- (void)installFirstFrameCallback {
|
||||
if (!_engine) {
|
||||
return;
|
||||
}
|
||||
|
||||
fml::WeakPtr<flutter::PlatformViewIOS> weakPlatformView = [_engine.get() platformView];
|
||||
if (!weakPlatformView) {
|
||||
return;
|
||||
@ -516,6 +561,10 @@ static void sendFakeTouchEvent(FlutterEngine* engine,
|
||||
#pragma mark - Surface creation and teardown updates
|
||||
|
||||
- (void)surfaceUpdated:(BOOL)appeared {
|
||||
if (!_engine) {
|
||||
return;
|
||||
}
|
||||
|
||||
// NotifyCreated/NotifyDestroyed are synchronous and require hops between the UI and raster
|
||||
// thread.
|
||||
if (appeared) {
|
||||
@ -536,14 +585,12 @@ static void sendFakeTouchEvent(FlutterEngine* engine,
|
||||
- (void)viewDidLoad {
|
||||
TRACE_EVENT0("flutter", "viewDidLoad");
|
||||
|
||||
if (_engineNeedsLaunch) {
|
||||
if (_engine && _engineNeedsLaunch) {
|
||||
[_engine.get() launchEngine:nil libraryURI:nil];
|
||||
[_engine.get() setViewController:self];
|
||||
_engineNeedsLaunch = NO;
|
||||
}
|
||||
|
||||
FML_DCHECK([_engine.get() viewController] == self)
|
||||
<< "FlutterViewController's view is loaded but is not attached to a FlutterEngine";
|
||||
[_engine.get() attachView];
|
||||
|
||||
[super viewDidLoad];
|
||||
@ -594,12 +641,12 @@ static void sendFakeTouchEvent(FlutterEngine* engine,
|
||||
}
|
||||
|
||||
- (void)flushOngoingTouches {
|
||||
if (_ongoingTouches.count > 0) {
|
||||
auto packet = std::make_unique<flutter::PointerDataPacket>(_ongoingTouches.count);
|
||||
if (_engine && _ongoingTouches.get().count > 0) {
|
||||
auto packet = std::make_unique<flutter::PointerDataPacket>(_ongoingTouches.get().count);
|
||||
size_t pointer_index = 0;
|
||||
// If the view controller is going away, we want to flush cancel all the ongoing
|
||||
// touches to the framework so nothing gets orphaned.
|
||||
for (NSNumber* device in _ongoingTouches) {
|
||||
for (NSNumber* device in _ongoingTouches.get()) {
|
||||
// Create fake PointerData to balance out each previously started one for the framework.
|
||||
flutter::PointerData pointer_data;
|
||||
pointer_data.Clear();
|
||||
@ -633,7 +680,6 @@ static void sendFakeTouchEvent(FlutterEngine* engine,
|
||||
object:self
|
||||
userInfo:nil];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
[_ongoingTouches release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
@ -719,6 +765,10 @@ static flutter::PointerData::DeviceKind DeviceKindFromTouchType(UITouch* touch)
|
||||
// touch is specified in the second argument.
|
||||
- (void)dispatchTouches:(NSSet*)touches
|
||||
pointerDataChangeOverride:(flutter::PointerData::Change*)overridden_change {
|
||||
if (!_engine) {
|
||||
return;
|
||||
}
|
||||
|
||||
const CGFloat scale = [UIScreen mainScreen].scale;
|
||||
auto packet = std::make_unique<flutter::PointerDataPacket>(touches.count);
|
||||
|
||||
@ -887,7 +937,7 @@ static flutter::PointerData::DeviceKind DeviceKindFromTouchType(UITouch* touch)
|
||||
|
||||
// This must run after updateViewportMetrics so that the surface creation tasks are queued after
|
||||
// the viewport metrics update tasks.
|
||||
if (firstViewBoundsUpdate && applicationIsActive) {
|
||||
if (firstViewBoundsUpdate && applicationIsActive && _engine) {
|
||||
[self surfaceUpdated:YES];
|
||||
|
||||
flutter::Shell& shell = [_engine.get() shell];
|
||||
@ -1040,6 +1090,9 @@ static flutter::PointerData::DeviceKind DeviceKindFromTouchType(UITouch* touch)
|
||||
#pragma mark - Accessibility
|
||||
|
||||
- (void)onAccessibilityStatusChanged:(NSNotification*)notification {
|
||||
if (!_engine) {
|
||||
return;
|
||||
}
|
||||
auto platformView = [_engine.get() platformView];
|
||||
int32_t flags = 0;
|
||||
if (UIAccessibilityIsInvertColorsEnabled())
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user