From 9b74fb099946ce4d8f8fc442ed6bdcf39ae640dd Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Thu, 23 Feb 2023 19:30:29 -0800 Subject: [PATCH 01/15] Add platform channel exit support. --- lib/ui/platform_dispatcher.dart | 36 ++++++++ lib/web_ui/lib/platform_dispatcher.dart | 10 +++ shell/platform/darwin/macos/BUILD.gn | 2 + .../framework/Headers/FlutterApplication.h | 54 ++++++++++++ .../macos/framework/Headers/FlutterMacOS.h | 1 + .../framework/Source/FlutterAppDelegate.mm | 36 +++++++- .../Source/FlutterAppDelegate_internal.h | 25 ++++++ .../framework/Source/FlutterApplication.mm | 86 +++++++++++++++++++ .../macos/framework/Source/FlutterEngine.mm | 84 +++++++++++++++++- .../framework/Source/FlutterEngine_Internal.h | 45 +++++++++- .../Source/FlutterTextInputPlugin.mm | 1 - .../framework/Source/FlutterViewController.mm | 1 - 12 files changed, 374 insertions(+), 7 deletions(-) create mode 100644 shell/platform/darwin/macos/framework/Headers/FlutterApplication.h create mode 100644 shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_internal.h create mode 100644 shell/platform/darwin/macos/framework/Source/FlutterApplication.mm diff --git a/lib/ui/platform_dispatcher.dart b/lib/ui/platform_dispatcher.dart index 90112aad40f78..33cf2594f7995 100644 --- a/lib/ui/platform_dispatcher.dart +++ b/lib/ui/platform_dispatcher.dart @@ -1733,6 +1733,42 @@ enum AppLifecycleState { detached, } +/// The possible responses to a request to exit the application. +/// +/// The request is typically responded to by a [WidgetsBindingObserver]. +// TODO(gspencergoog): Insert references here to AppLifecycleListener and to the +// actual function called on WidgetsBindingObserver once those have landed in +// the framework. +enum AppExitResponse { + /// Exiting the application can proceed. + exit, + /// Cancel the exit: do not exit the application. + cancel, +} + +/// The type of application exit to perform when calling +/// `ServicesBinding.exitApplication`. +// TODO(gspencergoog): Insert references here to ServicesBinding.exitApplication +// that has landed in the framework. +enum AppExitType { + /// Requests that the application start an orderly exit, sending a request + /// back to the framework through the [WidgetsBinding], and if that responds + /// with [AppExitResponse.exit], then proceed with the same steps as a + /// [required] exit. if that responds with [AppExitResponse.cancel], then the + /// exit request is canceled and the application continues executing normally. + cancelable, + + /// A non-cancelable orderly exit request. The engine will shut down the + /// engine and call the native UI toolkit's exit API. + /// + /// If you need an even faster and more dangerous exit, then call `dart:io`'s + /// `exit()` directly, and even the native toolkit's exit API won't be called. + /// This is quite dangerous, though, since it's possible that the engine will + /// crash because it hasn't been properly shut down, causing the app to crash + /// on exit. + required, +} + /// A representation of distances for each of the four edges of a rectangle, /// used to encode the view insets and padding that applications should place /// around their user interface, as exposed by [FlutterView.viewInsets] and diff --git a/lib/web_ui/lib/platform_dispatcher.dart b/lib/web_ui/lib/platform_dispatcher.dart index 416361236456a..9c4a11c64cbe3 100644 --- a/lib/web_ui/lib/platform_dispatcher.dart +++ b/lib/web_ui/lib/platform_dispatcher.dart @@ -374,6 +374,16 @@ enum AppLifecycleState { detached, } +enum AppExitResponse { + exit, + cancel, +} + +enum AppExitType { + cancelable, + required, +} + abstract class ViewPadding { const factory ViewPadding._( {required double left, diff --git a/shell/platform/darwin/macos/BUILD.gn b/shell/platform/darwin/macos/BUILD.gn index d672f9551736c..4cbf37b8c0513 100644 --- a/shell/platform/darwin/macos/BUILD.gn +++ b/shell/platform/darwin/macos/BUILD.gn @@ -38,6 +38,7 @@ _framework_binary_subpath = "Versions/A/$_flutter_framework_name" # the Flutter engine source root. _flutter_framework_headers = [ "framework/Headers/FlutterAppDelegate.h", + "framework/Headers/FlutterApplication.h", "framework/Headers/FlutterDartProject.h", "framework/Headers/FlutterEngine.h", "framework/Headers/FlutterMacOS.h", @@ -57,6 +58,7 @@ source_set("flutter_framework_source") { "framework/Source/AccessibilityBridgeMac.h", "framework/Source/AccessibilityBridgeMac.mm", "framework/Source/FlutterAppDelegate.mm", + "framework/Source/FlutterApplication.mm", "framework/Source/FlutterBackingStore.h", "framework/Source/FlutterBackingStore.mm", "framework/Source/FlutterChannelKeyResponder.h", diff --git a/shell/platform/darwin/macos/framework/Headers/FlutterApplication.h b/shell/platform/darwin/macos/framework/Headers/FlutterApplication.h new file mode 100644 index 0000000000000..5c2c0434abcf6 --- /dev/null +++ b/shell/platform/darwin/macos/framework/Headers/FlutterApplication.h @@ -0,0 +1,54 @@ +// 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_FLUTTERAPPLICATION_H_ +#define FLUTTER_FLUTTERAPPLICATION_H_ + +#import + +/** + * A Flutter-specific subclass of NSApplication that overrides |terminate| and + * provides an addition |terminateApplication| method so that Flutter can handle + * requests for termination in an asynchronous fashion. + * + * When a call to |terminate| comes in, either from the OS through a Quit menu + * item, through the Quit item in the dock context menu, or from the application + * itself, a request is sent to the Flutter framework. If that request is + * granted, this subclass will (in |terminateApplication|) call + * |NSApplication|'s version of |terminate| to proceed with terminating the + * application normally by calling |applicationShouldTerminate|, etc. + * + * If the termination request is denied by the framework, then the application + * will continue to execute normally, as if no |terminate| call were made. + * + * The |FlutterAppDelegate| always returns |NSTerminateNow| from + * |applicationShouldTerminate|, since it has already decided by that point that + * it should terminate. + * + * In order for this class to be used in place of |NSApplication|, the + * "NSPrincipalClass" entry in the Info.plist for the application must be set to + * "FlutterApplication". If it is not, then the application will not be given + * the chance to deny a termination request, and calls to requestAppExit on the + * engine (from the framework, typically) will simply exit the application + * without ceremony. + * + * If the |NSApp| global isn't of type |FlutterApplication|, a log message will + * be printed once in debug mode when the application is first accessed through + * the singleton's |sharedApplication|, describing how to fix this. + */ +@interface FlutterApplication : NSApplication + +/** + * FlutterApplication's implementation of |terminate| doesn't terminate the + * application: that is left up to the engine, which will call this function if + * it decides that termination request is granted, which will start the regular + * Cocoa flow for terminating the application, calling + * |applicationShouldTermnate|, etc. + * + * @param(sender) The id of the object requesting the termination, or nil. + */ +- (void)terminateApplication:(id)sender; +@end + +#endif // FLUTTER_FLUTTERAPPLICATION_H_ \ No newline at end of file diff --git a/shell/platform/darwin/macos/framework/Headers/FlutterMacOS.h b/shell/platform/darwin/macos/framework/Headers/FlutterMacOS.h index 5fc794b92995d..13c7f78775ec4 100644 --- a/shell/platform/darwin/macos/framework/Headers/FlutterMacOS.h +++ b/shell/platform/darwin/macos/framework/Headers/FlutterMacOS.h @@ -3,6 +3,7 @@ // found in the LICENSE file. #import "FlutterAppDelegate.h" +#import "FlutterApplication.h" #import "FlutterBinaryMessenger.h" #import "FlutterChannels.h" #import "FlutterCodecs.h" diff --git a/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate.mm b/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate.mm index 0442fdd70232d..d5cb3aae263d7 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate.mm @@ -3,6 +3,10 @@ // found in the LICENSE file. #import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterAppDelegate.h" +#include +#import "flutter/fml/logging.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_internal.h" +#import "flutter/shell/platform/embedder/embedder.h" @interface FlutterAppDelegate () @@ -13,10 +17,16 @@ - (NSString*)applicationName; @end -@implementation FlutterAppDelegate +@implementation FlutterAppDelegate { + FlutterEngineTerminationHandler* _terminationHandler; +} -// TODO(stuartmorgan): Implement application lifecycle forwarding to plugins here, as is done -// on iOS. Currently macOS plugins don't have access to lifecycle messages. +- (instancetype)init { + if (self = [super init]) { + _terminationHandler = nil; + } + return self; +} - (void)applicationWillFinishLaunching:(NSNotification*)notification { // Update UI elements to match the application name. @@ -28,6 +38,13 @@ - (void)applicationWillFinishLaunching:(NSNotification*)notification { } } +// This always returns NSTerminateNow, since by the time we get here, the +// application has already been asked if it should terminate or not, and if not, +// then termination never gets this far. +- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)sender { + return NSTerminateNow; +} + #pragma mark Private Methods - (NSString*)applicationName { @@ -39,4 +56,17 @@ - (NSString*)applicationName { return applicationName; } +- (void)setTerminationRequestHandler:(FlutterEngineTerminationHandler*)handler { + _terminationHandler = handler; +} + +- (void)tryToTerminateApplication:(FlutterApplication*)application + exitType:(FlutterAppExitType)type { + if (_terminationHandler) { + [_terminationHandler tryToTerminateApplication:application exitType:type result:nil]; + } else { + [application terminateApplication:application]; + } +} + @end diff --git a/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_internal.h b/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_internal.h new file mode 100644 index 0000000000000..cd31f8d878bfb --- /dev/null +++ b/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_internal.h @@ -0,0 +1,25 @@ +// 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_FLUTTERAPPDELEGATE_INTERNAL_H_ +#define FLUTTER_FLUTTERAPPDELEGATE_INTERNAL_H_ + +#include "embedder.h" +#import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterAppDelegate.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h" + +@interface FlutterAppDelegate () +/** + * Allows the engine to add a handler for termination requests + */ +- (void)setTerminationRequestHandler:(FlutterEngineTerminationHandler*)handler; + +/** + * Allows the delegate to respond to an attempt to terminate the application. + */ +- (void)tryToTerminateApplication:(FlutterApplication*)application + exitType:(FlutterAppExitType)type; +@end + +#endif // FLUTTER_FLUTTERAPPDELEGATE_INTERNAL_H_ diff --git a/shell/platform/darwin/macos/framework/Source/FlutterApplication.mm b/shell/platform/darwin/macos/framework/Source/FlutterApplication.mm new file mode 100644 index 0000000000000..c963448a65031 --- /dev/null +++ b/shell/platform/darwin/macos/framework/Source/FlutterApplication.mm @@ -0,0 +1,86 @@ +// 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/FlutterApplication.h" +#include "embedder.h" +#include "shell/platform/darwin/macos/framework/Headers/FlutterAppDelegate.h" +#include "shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_internal.h" + +#ifndef FLUTTER_RELEASE +namespace { +static bool warned_about_flutter_application = false; +} // namespace +#endif // !FLUTTER_RELEASE + +// An NSApplication subclass that implements overrides necessary for some +// Flutter features, like application lifecycle handling. +@interface FlutterApplication () { +} +@end + +@implementation FlutterApplication + +// Initialize NSApplication using the custom subclass. Check whether NSApp was +// already initialized using another class, because that would break some +// things. Warn about the mismatch only once, and only in debug builds. ++ (NSApplication*)sharedApplication { + NSApplication* app = [super sharedApplication]; + + // +sharedApplication initializes the global NSApp, so if we're delivering + // something other than a FlutterApplication, warn the developer once. +#ifndef FLUTTER_RELEASE + if (!warned_about_flutter_application && ![NSApp isKindOfClass:self]) { + NSLog(@"NSApp should be of type %s, not %s. " + "Some application lifecycle requests (e.g. ServicesBinding.exitApplication) " + "and notifications will be unavailable.\n" + "Modify the application's NSPrincipleClass to be %s" + "in the Info.plist to fix this.", + [[self className] UTF8String], [[NSApp className] UTF8String], + [[self className] UTF8String]); + warned_about_flutter_application = true; + } +#endif // !FLUTTER_RELEASE + return app; +} + +// |terminate| is the entry point for orderly "quit" operations in Cocoa. This +// includes the application menu's Quit menu item and keyboard equivalent, the +// application's dock icon menu's Quit menu item, "quit" (not "force quit") in +// the Activity Monitor, and quits triggered by user logout and system restart +// and shutdown. +// +// We override the normal |terminate| implementation. Our implementation, which +// is specific to the asyncronous nature of Flutter, works by asking the +// application delegate to terminate using its |tryToTerminateApplication| +// method instead of going through |applicationShouldTerminate|. +// +// The standard |applicationShouldTerminate| is not used because returning +// NSTerminateLater from that function moves the run loop into a modal dialog +// mode (NSModalPanelRunLoopMode), which stops the main run loop from processing +// messages like, for instance, the response to the method channel call, and +// code paths leading to it must be redirected to |tryToTerminateApplication|. +// +// |tryToTerminateApplication| differs from the standard +// |applicationShouldTerminate| in that no special event loop is run in the case +// that immediate termination is not possible (e.g., if dialog boxes allowing +// the user to cancel have to be shown, or data needs to be saved). Instead, +// tryToTerminateApplication sends a method channel call to the framework asking +// it if it is OK to terminate. When that method channel call returns with a +// result, the application either terminates or continues running. +- (void)terminate:(id)sender { + FlutterAppDelegate* appDelegate = static_cast([NSApp delegate]); + + [appDelegate tryToTerminateApplication:self exitType:kFlutterAppExitTypeCancelable]; + // Return, don't exit. The application delegate is responsible for exiting on + // its own by calling |-terminateApplication|. +} + +// Starts the regular Cocoa application termination flow, so that plugins will +// get the appropriate notifications after the application has already decided +// to quit. This is called after the application has decided that +// it's OK to terminate. +- (void)terminateApplication:(id)sender { + [super terminate:sender]; +} +@end diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm index 46a12265b4114..6093ce4073c4a 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm @@ -10,6 +10,11 @@ #include #include "flutter/shell/platform/common/engine_switches.h" +#include "flutter/shell/platform/embedder/embedder.h" + +#import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterAppDelegate.h" +#import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterApplication.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_internal.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterCompositor.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterDartProject_Internal.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterMenuPlugin.h" @@ -18,10 +23,11 @@ #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterRenderer.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewEngineProvider.h" -#include "flutter/shell/platform/embedder/embedder.h" const uint64_t kFlutterDefaultViewId = 0; +NSString* const kFlutterPlatformChannel = @"flutter/platform"; + /** * Constructs and returns a FlutterLocale struct corresponding to |locale|, which must outlive * the returned struct. @@ -152,6 +158,73 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result; #pragma mark - +@implementation FlutterEngineTerminationHandler { + FlutterEngine* _engine; +} + +- (instancetype)initWithEngine:(FlutterEngine*)engine { + self = [super init]; + _engine = engine; + return self; +} + +// This is called by the method call handler in the engine when the application +// requests termination itself. +- (void)requestAppExit:(NSDictionary*)arguments result:(FlutterResult)result { + NSString* type = arguments[@"type"]; + + // Ignore the "exitCode" value in the arguments because AppKit doesn't have + // any good way to set the process exit code other than calling exit(), and + // that bypasses all of the native applicationShouldExit shutdown events, + // etc., which we don't want to skip. + + FlutterAppExitType exitType; + if ([type isEqualTo:@"cancelable"]) { + exitType = kFlutterAppExitTypeCancelable; + } else { + exitType = kFlutterAppExitTypeRequired; + } + + [self tryToTerminateApplication:[FlutterApplication sharedApplication] + exitType:exitType + result:result]; +} + +// This is called by the FlutterAppDelegate whenever any termination request is +// received. +- (void)tryToTerminateApplication:(id)sender + exitType:(FlutterAppExitType)type + result:(nullable FlutterResult)result { + switch (type) { + case kFlutterAppExitTypeCancelable: { + [_engine + sendOnChannel:kFlutterPlatformChannel + message:[[FlutterJSONMethodCodec sharedInstance] + encodeMethodCall:[FlutterMethodCall + methodCallWithMethodName:@"System.requestAppExit" + arguments:@{}]] + binaryReply:^(NSData* _Nullable reply) { + NSDictionary* replyArgs = + [[FlutterJSONMethodCodec sharedInstance] decodeEnvelope:reply]; + if ([replyArgs[@"response"] isEqual:@"exit"]) { + [[FlutterApplication sharedApplication] terminateApplication:sender]; + } + if (result != nil) { + result(replyArgs); + } + }]; + break; + } + case kFlutterAppExitTypeRequired: + [[FlutterApplication sharedApplication] terminateApplication:sender]; + break; + } +} + +@end + +#pragma mark - + /** * `FlutterPluginRegistrar` implementation handling a single plugin. */ @@ -270,6 +343,9 @@ @implementation FlutterEngine { // A method channel for miscellaneous platform functionality. FlutterMethodChannel* _platformChannel; + // A delegate used to receive application termination events. + FlutterEngineTerminationHandler* _terminateHelper; + int _nextViewId; } @@ -290,6 +366,10 @@ - (instancetype)initWithName:(NSString*)labelPrefix _semanticsEnabled = NO; _isResponseValid = [[NSMutableArray alloc] initWithCapacity:1]; [_isResponseValid addObject:@YES]; + _terminateHelper = [[FlutterEngineTerminationHandler alloc] initWithEngine:self]; + FlutterAppDelegate* appDelegate = + (FlutterAppDelegate*)[[FlutterApplication sharedApplication] delegate]; + [appDelegate setTerminationRequestHandler:_terminateHelper]; // kFlutterDefaultViewId is reserved for the default view. // All IDs above it are for regular views. _nextViewId = kFlutterDefaultViewId + 1; @@ -881,6 +961,8 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { result(nil); } else if ([call.method isEqualToString:@"Clipboard.hasStrings"]) { result(@{@"value" : @([self clipboardHasStrings])}); + } else if ([call.method isEqualToString:@"System.exitApplication"]) { + [_terminateHelper requestAppExit:call.arguments result:result]; } else { result(FlutterMethodNotImplemented); } diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h b/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h index c617363c66eb8..3553acf888053 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h @@ -2,17 +2,21 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include #import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterEngine.h" #import #include +#import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterApplication.h" #import "flutter/shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMac.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterCompositor.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterRenderer.h" +NS_ASSUME_NONNULL_BEGIN + @interface FlutterEngine () /** @@ -78,7 +82,7 @@ - (void)removeViewController:(nonnull FlutterViewController*)viewController; /** - * The `FlutterViewController` associated with the given view ID, if any. + * The |FlutterViewController associated with the given view ID, if any. */ - (nullable FlutterViewController*)viewControllerForId:(uint64_t)viewId; @@ -127,3 +131,42 @@ withData:(fml::MallocMapping)data; @end + +#pragma mark - + +/** + * An enum for defining the different responses the framework can give to an + * application exit request from the engine. + * + * Must match the entries in the `AppExitResponse` enum in the Dart code. + */ +typedef enum { + kFlutterAppExitResponseCancel = 0, + kFlutterAppExitResponseExit = 1, +} FlutterAppExitResponse; + +/** + * An enum for defining the different request types allowed when requesting an + * application exit. + * + * Must match the entries in the `AppExitType` enum in the Dart code. + */ +typedef enum { + kFlutterAppExitTypeCancelable = 0, + kFlutterAppExitTypeRequired = 1, +} FlutterAppExitType; + +/** + * A handler interface for handling application termination that the + * FlutterAppDelegate can use to coordinate an application exit by sending + * messages through the platform channel managed by the engine. + */ +@interface FlutterEngineTerminationHandler : NSObject +- (instancetype)initWithEngine:(FlutterEngine*)engine; +- (void)requestAppExit:(NSDictionary*)data result:(FlutterResult)result; +- (void)tryToTerminateApplication:(FlutterApplication*)sender + exitType:(FlutterAppExitType)type + result:(nullable FlutterResult)result; +@end + +NS_ASSUME_NONNULL_END \ No newline at end of file diff --git a/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.mm b/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.mm index 7129b5e7cb193..ebf5057d6e7ae 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.mm @@ -14,7 +14,6 @@ #include "flutter/shell/platform/common/text_editing_delta.h" #include "flutter/shell/platform/common/text_input_model.h" #import "flutter/shell/platform/darwin/common/framework/Headers/FlutterCodecs.h" -#import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterAppDelegate.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputSemanticsObject.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h" diff --git a/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm b/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm index 994cc8f468d69..a921738d6d73a 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm @@ -9,7 +9,6 @@ #import "flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h" #import "flutter/shell/platform/darwin/common/framework/Headers/FlutterCodecs.h" -#import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterAppDelegate.h" #import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterEngine.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterKeyPrimaryResponder.h" From cf66ee45753a0533853a1b640f2fff4dc63c0ad7 Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Fri, 24 Feb 2023 10:30:48 -0800 Subject: [PATCH 02/15] Update license file --- ci/licenses_golden/licenses_flutter | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index fbf31268b6c09..87df382bc0718 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -2550,6 +2550,7 @@ ORIGIN: ../../../flutter/shell/platform/darwin/ios/platform_view_ios.mm + ../../ ORIGIN: ../../../flutter/shell/platform/darwin/ios/rendering_api_selection.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/ios/rendering_api_selection.mm + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterAppDelegate.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterApplication.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterDartProject.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterEngine.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterMacOS.h + ../../../flutter/LICENSE @@ -2561,6 +2562,8 @@ ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/Accessibil ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMac.mm + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMacTest.mm + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate.mm + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_internal.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterApplication.mm + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterBackingStore.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterBackingStore.mm + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterChannelKeyResponder.h + ../../../flutter/LICENSE @@ -5083,6 +5086,7 @@ FILE: ../../../flutter/shell/platform/darwin/ios/platform_view_ios.mm FILE: ../../../flutter/shell/platform/darwin/ios/rendering_api_selection.h FILE: ../../../flutter/shell/platform/darwin/ios/rendering_api_selection.mm FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterAppDelegate.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterApplication.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterDartProject.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterEngine.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterMacOS.h @@ -5095,6 +5099,8 @@ FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/Accessibilit FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMac.mm FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMacTest.mm FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_internal.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterApplication.mm FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterBackingStore.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterBackingStore.mm FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterChannelKeyResponder.h From 72a5d9d6509cc7e8d9f445875b6e0d13c03c96ec Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Fri, 24 Feb 2023 14:46:07 -0800 Subject: [PATCH 03/15] Review Changes --- .../framework/Headers/FlutterApplication.h | 10 ++-- .../framework/Source/FlutterAppDelegate.mm | 21 ++++++--- .../Source/FlutterAppDelegate_internal.h | 3 +- .../framework/Source/FlutterApplication.mm | 47 ++++++++----------- .../macos/framework/Source/FlutterEngine.mm | 20 ++++---- .../framework/Source/FlutterEngineTest.mm | 21 +++++++-- .../framework/Source/FlutterEngine_Internal.h | 45 +++++++++--------- 7 files changed, 90 insertions(+), 77 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Headers/FlutterApplication.h b/shell/platform/darwin/macos/framework/Headers/FlutterApplication.h index 5c2c0434abcf6..54e5305dfec2b 100644 --- a/shell/platform/darwin/macos/framework/Headers/FlutterApplication.h +++ b/shell/platform/darwin/macos/framework/Headers/FlutterApplication.h @@ -9,8 +9,8 @@ /** * A Flutter-specific subclass of NSApplication that overrides |terminate| and - * provides an addition |terminateApplication| method so that Flutter can handle - * requests for termination in an asynchronous fashion. + * provides an additional |terminateApplication| method so that Flutter can + * handle requests for termination in an asynchronous fashion. * * When a call to |terminate| comes in, either from the OS through a Quit menu * item, through the Quit item in the dock context menu, or from the application @@ -36,6 +36,10 @@ * If the |NSApp| global isn't of type |FlutterApplication|, a log message will * be printed once in debug mode when the application is first accessed through * the singleton's |sharedApplication|, describing how to fix this. + * + * Flutter applications are *not* required to inherit from this class. + * Developers of custom |NSApplication| subclasses should copy and paste code as + * necessary from FlutterApplication.mm. */ @interface FlutterApplication : NSApplication @@ -51,4 +55,4 @@ - (void)terminateApplication:(id)sender; @end -#endif // FLUTTER_FLUTTERAPPLICATION_H_ \ No newline at end of file +#endif // FLUTTER_FLUTTERAPPLICATION_H_ diff --git a/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate.mm b/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate.mm index d5cb3aae263d7..10dfc3bbfd7c0 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate.mm @@ -3,10 +3,12 @@ // found in the LICENSE file. #import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterAppDelegate.h" -#include -#import "flutter/fml/logging.h" + +#import + +#include "flutter/fml/logging.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_internal.h" -#import "flutter/shell/platform/embedder/embedder.h" +#include "flutter/shell/platform/embedder/embedder.h" @interface FlutterAppDelegate () @@ -60,12 +62,17 @@ - (void)setTerminationRequestHandler:(FlutterEngineTerminationHandler*)handler { _terminationHandler = handler; } -- (void)tryToTerminateApplication:(FlutterApplication*)application - exitType:(FlutterAppExitType)type { +- (void)requestApplicationTermination:(NSApplication*)application + exitType:(FlutterAppExitType)type { + if (![application isKindOfClass:[FlutterApplication class]]) { + [application terminate:application]; + return; + }; + FlutterApplication* flutterApp = static_cast(application); if (_terminationHandler) { - [_terminationHandler tryToTerminateApplication:application exitType:type result:nil]; + [_terminationHandler requestApplicationTermination:flutterApp exitType:type result:nil]; } else { - [application terminateApplication:application]; + [flutterApp terminateApplication:application]; } } diff --git a/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_internal.h b/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_internal.h index cd31f8d878bfb..c5df69696cc1f 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_internal.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_internal.h @@ -18,8 +18,7 @@ /** * Allows the delegate to respond to an attempt to terminate the application. */ -- (void)tryToTerminateApplication:(FlutterApplication*)application - exitType:(FlutterAppExitType)type; +- (void)requestApplicationTermination:(NSApplication*)application exitType:(FlutterAppExitType)type; @end #endif // FLUTTER_FLUTTERAPPDELEGATE_INTERNAL_H_ diff --git a/shell/platform/darwin/macos/framework/Source/FlutterApplication.mm b/shell/platform/darwin/macos/framework/Source/FlutterApplication.mm index c963448a65031..df99c0246e202 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterApplication.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterApplication.mm @@ -3,22 +3,13 @@ // found in the LICENSE file. #import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterApplication.h" -#include "embedder.h" -#include "shell/platform/darwin/macos/framework/Headers/FlutterAppDelegate.h" -#include "shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_internal.h" -#ifndef FLUTTER_RELEASE -namespace { -static bool warned_about_flutter_application = false; -} // namespace -#endif // !FLUTTER_RELEASE +#include "flutter/shell/platform/embedder/embedder.h" +#import "shell/platform/darwin/macos/framework/Headers/FlutterAppDelegate.h" +#import "shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_internal.h" // An NSApplication subclass that implements overrides necessary for some // Flutter features, like application lifecycle handling. -@interface FlutterApplication () { -} -@end - @implementation FlutterApplication // Initialize NSApplication using the custom subclass. Check whether NSApp was @@ -30,16 +21,18 @@ + (NSApplication*)sharedApplication { // +sharedApplication initializes the global NSApp, so if we're delivering // something other than a FlutterApplication, warn the developer once. #ifndef FLUTTER_RELEASE - if (!warned_about_flutter_application && ![NSApp isKindOfClass:self]) { - NSLog(@"NSApp should be of type %s, not %s. " - "Some application lifecycle requests (e.g. ServicesBinding.exitApplication) " - "and notifications will be unavailable.\n" - "Modify the application's NSPrincipleClass to be %s" - "in the Info.plist to fix this.", - [[self className] UTF8String], [[NSApp className] UTF8String], - [[self className] UTF8String]); - warned_about_flutter_application = true; - } + static dispatch_once_t onceToken = 0; + dispatch_once(&onceToken, ^{ + if (![NSApp isKindOfClass:[FlutterApplication class]]) { + NSLog(@"NSApp should be of type %s, not %s. " + "Some application lifecycle requests (e.g. ServicesBinding.exitApplication) " + "and notifications will be unavailable.\n" + "Modify the application's NSPrincipleClass to be %s" + "in the Info.plist to fix this.", + [[self className] UTF8String], [[NSApp className] UTF8String], + [[self className] UTF8String]); + } + }); #endif // !FLUTTER_RELEASE return app; } @@ -52,26 +45,26 @@ + (NSApplication*)sharedApplication { // // We override the normal |terminate| implementation. Our implementation, which // is specific to the asyncronous nature of Flutter, works by asking the -// application delegate to terminate using its |tryToTerminateApplication| +// application delegate to terminate using its |requestApplicationTermination| // method instead of going through |applicationShouldTerminate|. // // The standard |applicationShouldTerminate| is not used because returning // NSTerminateLater from that function moves the run loop into a modal dialog // mode (NSModalPanelRunLoopMode), which stops the main run loop from processing // messages like, for instance, the response to the method channel call, and -// code paths leading to it must be redirected to |tryToTerminateApplication|. +// code paths leading to it must be redirected to |requestApplicationTermination|. // -// |tryToTerminateApplication| differs from the standard +// |requestApplicationTermination| differs from the standard // |applicationShouldTerminate| in that no special event loop is run in the case // that immediate termination is not possible (e.g., if dialog boxes allowing // the user to cancel have to be shown, or data needs to be saved). Instead, -// tryToTerminateApplication sends a method channel call to the framework asking +// requestApplicationTermination sends a method channel call to the framework asking // it if it is OK to terminate. When that method channel call returns with a // result, the application either terminates or continues running. - (void)terminate:(id)sender { FlutterAppDelegate* appDelegate = static_cast([NSApp delegate]); - [appDelegate tryToTerminateApplication:self exitType:kFlutterAppExitTypeCancelable]; + [appDelegate requestApplicationTermination:self exitType:kFlutterAppExitTypeCancelable]; // Return, don't exit. The application delegate is responsible for exiting on // its own by calling |-terminateApplication|. } diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm index 6093ce4073c4a..fe470db3b3300 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm @@ -178,23 +178,19 @@ - (void)requestAppExit:(NSDictionary*)arguments result:(FlutterRe // that bypasses all of the native applicationShouldExit shutdown events, // etc., which we don't want to skip. - FlutterAppExitType exitType; - if ([type isEqualTo:@"cancelable"]) { - exitType = kFlutterAppExitTypeCancelable; - } else { - exitType = kFlutterAppExitTypeRequired; - } + FlutterAppExitType exitType = + [type isEqualTo:@"cancelable"] ? kFlutterAppExitTypeCancelable : kFlutterAppExitTypeRequired; - [self tryToTerminateApplication:[FlutterApplication sharedApplication] - exitType:exitType - result:result]; + [self requestApplicationTermination:[FlutterApplication sharedApplication] + exitType:exitType + result:result]; } // This is called by the FlutterAppDelegate whenever any termination request is // received. -- (void)tryToTerminateApplication:(id)sender - exitType:(FlutterAppExitType)type - result:(nullable FlutterResult)result { +- (void)requestApplicationTermination:(id)sender + exitType:(FlutterAppExitType)type + result:(nullable FlutterResult)result { switch (type) { case kFlutterAppExitTypeCancelable: { [_engine diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm index fd5f27d1f5c34..0f449ac6a4aad 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm @@ -407,7 +407,7 @@ - (nonnull NSView*)createWithViewIdentifier:(int64_t)viewId arguments:(nullable ASSERT_TRUE(latch_called); } -TEST(FlutterEngine, Compositor) { +TEST(FlutterEngineTest, Compositor) { NSString* fixtures = @(flutter::testing::GetFixturesPath()); FlutterDartProject* project = [[FlutterDartProject alloc] initWithAssetsPath:fixtures @@ -446,7 +446,7 @@ - (nonnull NSView*)createWithViewIdentifier:(int64_t)viewId arguments:(nullable [engine shutDownEngine]; } // namespace flutter::testing -TEST(FlutterEngine, DartEntrypointArguments) { +TEST(FlutterEngineTest, DartEntrypointArguments) { NSString* fixtures = @(flutter::testing::GetFixturesPath()); FlutterDartProject* project = [[FlutterDartProject alloc] initWithAssetsPath:fixtures @@ -541,7 +541,7 @@ - (nonnull NSView*)createWithViewIdentifier:(int64_t)viewId arguments:(nullable EXPECT_EQ(record, 21); } -TEST(FlutterEngine, HasStringsWhenPasteboardEmpty) { +TEST(FlutterEngineTest, HasStringsWhenPasteboardEmpty) { id engineMock = CreateMockFlutterEngine(nil); // Call hasStrings and expect it to be false. @@ -559,7 +559,7 @@ - (nonnull NSView*)createWithViewIdentifier:(int64_t)viewId arguments:(nullable EXPECT_FALSE(valueAfterClear); } -TEST(FlutterEngine, HasStringsWhenPasteboardFull) { +TEST(FlutterEngineTest, HasStringsWhenPasteboardFull) { id engineMock = CreateMockFlutterEngine(@"some string"); // Call hasStrings and expect it to be true. @@ -695,6 +695,19 @@ - (nonnull NSView*)createWithViewIdentifier:(int64_t)viewId arguments:(nullable EXPECT_EQ(viewController1.id, 0ull); } +TEST(FlutterEngineTerminationHandlerTest, HandlesTerminationRequest) { + id engineMock = CreateMockFlutterEngine(nil); + + __block FlutterAppExitResponse calledAfterTerminate = kFlutterAppExitResponseCancel; + FlutterResult appExitResult = ^(id result) { + NSLog(@"Response: %@", result); + }; + FlutterMethodCall* methodCallAfterClear = + [FlutterMethodCall methodCallWithMethodName:@"System.exitApplication" arguments:nil]; + [engineMock handleMethodCall:methodCallAfterClear result:appExitResult]; + EXPECT_EQ(kFlutterAppExitResponseCancel, calledAfterTerminate); +} + } // namespace flutter::testing // NOLINTEND(clang-analyzer-core.StackAddressEscape) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h b/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h index 3553acf888053..90f87694dd879 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include #import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterEngine.h" #import @@ -68,7 +67,7 @@ NS_ASSUME_NONNULL_BEGIN * If the given view controller is already attached to an engine, this call * throws an assertion. */ -- (void)addViewController:(nonnull FlutterViewController*)viewController; +- (void)addViewController:(FlutterViewController*)viewController; /** * Dissociate the given view controller from this engine. @@ -79,17 +78,17 @@ NS_ASSUME_NONNULL_BEGIN * If the view controller is not associated with this engine, this call throws an * assertion. */ -- (void)removeViewController:(nonnull FlutterViewController*)viewController; +- (void)removeViewController:(FlutterViewController*)viewController; /** - * The |FlutterViewController associated with the given view ID, if any. + * The |FlutterViewController| associated with the given view ID, if any. */ - (nullable FlutterViewController*)viewControllerForId:(uint64_t)viewId; /** * Informs the engine that the specified view controller's window metrics have changed. */ -- (void)updateWindowMetricsForViewController:(nonnull FlutterViewController*)viewController; +- (void)updateWindowMetricsForViewController:(FlutterViewController*)viewController; /** * Dispatches the given pointer event data to engine. @@ -132,7 +131,18 @@ NS_ASSUME_NONNULL_BEGIN @end -#pragma mark - +#pragma mark - Enumerations + +/** + * An enum for defining the different request types allowed when requesting an + * application exit. + * + * Must match the entries in the `AppExitType` enum in the Dart code. + */ +typedef NS_ENUM(NSInteger, FlutterAppExitType) { + kFlutterAppExitTypeCancelable = 0, + kFlutterAppExitTypeRequired = 1, +}; /** * An enum for defining the different responses the framework can give to an @@ -140,21 +150,12 @@ NS_ASSUME_NONNULL_BEGIN * * Must match the entries in the `AppExitResponse` enum in the Dart code. */ -typedef enum { +typedef NS_ENUM(NSInteger, FlutterAppExitResponse) { kFlutterAppExitResponseCancel = 0, kFlutterAppExitResponseExit = 1, -} FlutterAppExitResponse; +}; -/** - * An enum for defining the different request types allowed when requesting an - * application exit. - * - * Must match the entries in the `AppExitType` enum in the Dart code. - */ -typedef enum { - kFlutterAppExitTypeCancelable = 0, - kFlutterAppExitTypeRequired = 1, -} FlutterAppExitType; +#pragma mark - FlutterEngineTerminationHandler /** * A handler interface for handling application termination that the @@ -164,9 +165,9 @@ typedef enum { @interface FlutterEngineTerminationHandler : NSObject - (instancetype)initWithEngine:(FlutterEngine*)engine; - (void)requestAppExit:(NSDictionary*)data result:(FlutterResult)result; -- (void)tryToTerminateApplication:(FlutterApplication*)sender - exitType:(FlutterAppExitType)type - result:(nullable FlutterResult)result; +- (void)requestApplicationTermination:(FlutterApplication*)sender + exitType:(FlutterAppExitType)type + result:(nullable FlutterResult)result; @end -NS_ASSUME_NONNULL_END \ No newline at end of file +NS_ASSUME_NONNULL_END From a0bde037f424e86a0db28fca4e247bc3870b9cd9 Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Mon, 27 Feb 2023 15:06:17 -0800 Subject: [PATCH 04/15] Consolidate functionality, fix test --- .../framework/Source/FlutterAppDelegate.mm | 24 +---- .../Source/FlutterAppDelegate_internal.h | 10 +-- .../framework/Source/FlutterApplication.mm | 36 ++++---- .../macos/framework/Source/FlutterEngine.mm | 61 +++++++------ .../framework/Source/FlutterEngineTest.mm | 66 +++++++++++--- .../framework/Source/FlutterEngine_Internal.h | 88 +++++++++++-------- 6 files changed, 161 insertions(+), 124 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate.mm b/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate.mm index 10dfc3bbfd7c0..03a3f904dd7d5 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate.mm @@ -7,7 +7,7 @@ #import #include "flutter/fml/logging.h" -#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_internal.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_Internal.h" #include "flutter/shell/platform/embedder/embedder.h" @interface FlutterAppDelegate () @@ -19,9 +19,7 @@ - (NSString*)applicationName; @end -@implementation FlutterAppDelegate { - FlutterEngineTerminationHandler* _terminationHandler; -} +@implementation FlutterAppDelegate - (instancetype)init { if (self = [super init]) { @@ -58,22 +56,4 @@ - (NSString*)applicationName { return applicationName; } -- (void)setTerminationRequestHandler:(FlutterEngineTerminationHandler*)handler { - _terminationHandler = handler; -} - -- (void)requestApplicationTermination:(NSApplication*)application - exitType:(FlutterAppExitType)type { - if (![application isKindOfClass:[FlutterApplication class]]) { - [application terminate:application]; - return; - }; - FlutterApplication* flutterApp = static_cast(application); - if (_terminationHandler) { - [_terminationHandler requestApplicationTermination:flutterApp exitType:type result:nil]; - } else { - [flutterApp terminateApplication:application]; - } -} - @end diff --git a/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_internal.h b/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_internal.h index c5df69696cc1f..8234db058feb5 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_internal.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_internal.h @@ -10,15 +10,13 @@ #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h" @interface FlutterAppDelegate () -/** - * Allows the engine to add a handler for termination requests - */ -- (void)setTerminationRequestHandler:(FlutterEngineTerminationHandler*)handler; /** - * Allows the delegate to respond to an attempt to terminate the application. + * Holds a weak reference to the termination handler owned by the engine. + * Called by the |FlutterApplication| when termination is requested by the OS. */ -- (void)requestApplicationTermination:(NSApplication*)application exitType:(FlutterAppExitType)type; +@property(readwrite, weak) FlutterEngineTerminationHandler* terminationHandler; + @end #endif // FLUTTER_FLUTTERAPPDELEGATE_INTERNAL_H_ diff --git a/shell/platform/darwin/macos/framework/Source/FlutterApplication.mm b/shell/platform/darwin/macos/framework/Source/FlutterApplication.mm index df99c0246e202..ac94443fc0cce 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterApplication.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterApplication.mm @@ -6,7 +6,8 @@ #include "flutter/shell/platform/embedder/embedder.h" #import "shell/platform/darwin/macos/framework/Headers/FlutterAppDelegate.h" -#import "shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_internal.h" +#import "shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_Internal.h" +#import "shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h" // An NSApplication subclass that implements overrides necessary for some // Flutter features, like application lifecycle handling. @@ -21,18 +22,17 @@ + (NSApplication*)sharedApplication { // +sharedApplication initializes the global NSApp, so if we're delivering // something other than a FlutterApplication, warn the developer once. #ifndef FLUTTER_RELEASE - static dispatch_once_t onceToken = 0; - dispatch_once(&onceToken, ^{ - if (![NSApp isKindOfClass:[FlutterApplication class]]) { - NSLog(@"NSApp should be of type %s, not %s. " - "Some application lifecycle requests (e.g. ServicesBinding.exitApplication) " - "and notifications will be unavailable.\n" - "Modify the application's NSPrincipleClass to be %s" - "in the Info.plist to fix this.", - [[self className] UTF8String], [[NSApp className] UTF8String], - [[self className] UTF8String]); - } - }); + static bool notified = false; + if (!notified && ![NSApp isKindOfClass:[FlutterApplication class]]) { + NSLog(@"NSApp should be of type %s, not %s. " + "Some application lifecycle requests (e.g. ServicesBinding.exitApplication) " + "and notifications will be unavailable.\n" + "Modify the application's NSPrincipleClass to be %s" + "in the Info.plist to fix this.", + [[self className] UTF8String], [[NSApp className] UTF8String], + [[self className] UTF8String]); + notified = true; + } #endif // !FLUTTER_RELEASE return app; } @@ -62,9 +62,13 @@ + (NSApplication*)sharedApplication { // it if it is OK to terminate. When that method channel call returns with a // result, the application either terminates or continues running. - (void)terminate:(id)sender { - FlutterAppDelegate* appDelegate = static_cast([NSApp delegate]); - - [appDelegate requestApplicationTermination:self exitType:kFlutterAppExitTypeCancelable]; + FlutterEngineTerminationHandler* terminationHandler = [static_cast([NSApp delegate]) terminationHandler]; + if (terminationHandler) { + [terminationHandler requestApplicationTermination:self exitType:kFlutterAppExitTypeCancelable result:nil]; + } else { + // If there's no termination handler, then just terminate. + [super terminate:sender]; + } // Return, don't exit. The application delegate is responsible for exiting on // its own by calling |-terminateApplication|. } diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm index fe470db3b3300..d1cf633a373f0 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm @@ -3,6 +3,7 @@ // found in the LICENSE file. #import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterEngine.h" +#include "FlutterCodecs.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h" #include @@ -14,7 +15,7 @@ #import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterAppDelegate.h" #import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterApplication.h" -#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_internal.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_Internal.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterCompositor.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterDartProject_Internal.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterMenuPlugin.h" @@ -160,11 +161,21 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result; @implementation FlutterEngineTerminationHandler { FlutterEngine* _engine; + FlutterTerminationCallback _terminator; } -- (instancetype)initWithEngine:(FlutterEngine*)engine { +- (instancetype)initWithEngine:(FlutterEngine*)engine + terminator:(FlutterTerminationCallback)terminator { self = [super init]; _engine = engine; + _terminator = terminator ? terminator : ^(id sender) { + // Default to actually terminating the application. The terminator exists to + // allow tests to override it so that an actual exit doesn't occur. + [[FlutterApplication sharedApplication] terminateApplication:sender]; + }; + FlutterAppDelegate* appDelegate = + (FlutterAppDelegate*)[[FlutterApplication sharedApplication] delegate]; + appDelegate.terminationHandler = self; return self; } @@ -172,7 +183,6 @@ - (instancetype)initWithEngine:(FlutterEngine*)engine { // requests termination itself. - (void)requestAppExit:(NSDictionary*)arguments result:(FlutterResult)result { NSString* type = arguments[@"type"]; - // Ignore the "exitCode" value in the arguments because AppKit doesn't have // any good way to set the process exit code other than calling exit(), and // that bypasses all of the native applicationShouldExit shutdown events, @@ -193,26 +203,26 @@ - (void)requestApplicationTermination:(id)sender result:(nullable FlutterResult)result { switch (type) { case kFlutterAppExitTypeCancelable: { - [_engine - sendOnChannel:kFlutterPlatformChannel - message:[[FlutterJSONMethodCodec sharedInstance] - encodeMethodCall:[FlutterMethodCall - methodCallWithMethodName:@"System.requestAppExit" - arguments:@{}]] - binaryReply:^(NSData* _Nullable reply) { - NSDictionary* replyArgs = - [[FlutterJSONMethodCodec sharedInstance] decodeEnvelope:reply]; - if ([replyArgs[@"response"] isEqual:@"exit"]) { - [[FlutterApplication sharedApplication] terminateApplication:sender]; - } - if (result != nil) { - result(replyArgs); - } - }]; + FlutterJSONMethodCodec* codec = [FlutterJSONMethodCodec sharedInstance]; + FlutterMethodCall* methodCall = + [FlutterMethodCall methodCallWithMethodName:@"System.requestAppExit" arguments:nil]; + [_engine sendOnChannel:kFlutterPlatformChannel + message:[codec encodeMethodCall:methodCall] + binaryReply:^(NSData* _Nullable reply) { + NSDictionary* replyArgs = [codec decodeEnvelope:reply]; + if ([replyArgs[@"response"] isEqual:@"exit"]) { + NSAssert(_terminator, @"terminator shouldn't be nil"); + _terminator(sender); + } + if (result != nil) { + result(replyArgs); + } + }]; break; } case kFlutterAppExitTypeRequired: - [[FlutterApplication sharedApplication] terminateApplication:sender]; + NSAssert(_terminator, @"terminator shouldn't be nil"); + _terminator(sender); break; } } @@ -339,9 +349,6 @@ @implementation FlutterEngine { // A method channel for miscellaneous platform functionality. FlutterMethodChannel* _platformChannel; - // A delegate used to receive application termination events. - FlutterEngineTerminationHandler* _terminateHelper; - int _nextViewId; } @@ -362,10 +369,8 @@ - (instancetype)initWithName:(NSString*)labelPrefix _semanticsEnabled = NO; _isResponseValid = [[NSMutableArray alloc] initWithCapacity:1]; [_isResponseValid addObject:@YES]; - _terminateHelper = [[FlutterEngineTerminationHandler alloc] initWithEngine:self]; - FlutterAppDelegate* appDelegate = - (FlutterAppDelegate*)[[FlutterApplication sharedApplication] delegate]; - [appDelegate setTerminationRequestHandler:_terminateHelper]; + _terminationHandler = [[FlutterEngineTerminationHandler alloc] initWithEngine:self + terminator:nil]; // kFlutterDefaultViewId is reserved for the default view. // All IDs above it are for regular views. _nextViewId = kFlutterDefaultViewId + 1; @@ -958,7 +963,7 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { } else if ([call.method isEqualToString:@"Clipboard.hasStrings"]) { result(@{@"value" : @([self clipboardHasStrings])}); } else if ([call.method isEqualToString:@"System.exitApplication"]) { - [_terminateHelper requestAppExit:call.arguments result:result]; + [[self terminationHandler] requestAppExit:call.arguments result:result]; } else { result(FlutterMethodNotImplemented); } diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm index 0f449ac6a4aad..6fe0ecac0c2c8 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm @@ -2,8 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "OCMock/OCMArg.h" +#include "OCMock/OCMock.h" #import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterEngine.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h" +#include "gtest/gtest.h" #include #include @@ -407,7 +410,7 @@ - (nonnull NSView*)createWithViewIdentifier:(int64_t)viewId arguments:(nullable ASSERT_TRUE(latch_called); } -TEST(FlutterEngineTest, Compositor) { +TEST_F(FlutterEngineTest, Compositor) { NSString* fixtures = @(flutter::testing::GetFixturesPath()); FlutterDartProject* project = [[FlutterDartProject alloc] initWithAssetsPath:fixtures @@ -446,7 +449,7 @@ - (nonnull NSView*)createWithViewIdentifier:(int64_t)viewId arguments:(nullable [engine shutDownEngine]; } // namespace flutter::testing -TEST(FlutterEngineTest, DartEntrypointArguments) { +TEST_F(FlutterEngineTest, DartEntrypointArguments) { NSString* fixtures = @(flutter::testing::GetFixturesPath()); FlutterDartProject* project = [[FlutterDartProject alloc] initWithAssetsPath:fixtures @@ -541,7 +544,7 @@ - (nonnull NSView*)createWithViewIdentifier:(int64_t)viewId arguments:(nullable EXPECT_EQ(record, 21); } -TEST(FlutterEngineTest, HasStringsWhenPasteboardEmpty) { +TEST_F(FlutterEngineTest, HasStringsWhenPasteboardEmpty) { id engineMock = CreateMockFlutterEngine(nil); // Call hasStrings and expect it to be false. @@ -559,7 +562,7 @@ - (nonnull NSView*)createWithViewIdentifier:(int64_t)viewId arguments:(nullable EXPECT_FALSE(valueAfterClear); } -TEST(FlutterEngineTest, HasStringsWhenPasteboardFull) { +TEST_F(FlutterEngineTest, HasStringsWhenPasteboardFull) { id engineMock = CreateMockFlutterEngine(@"some string"); // Call hasStrings and expect it to be true. @@ -619,7 +622,7 @@ - (nonnull NSView*)createWithViewIdentifier:(int64_t)viewId arguments:(nullable } } -TEST(EngineTest, ThreadSynchronizerNotBlockingRasterThreadAfterShutdown) { +TEST_F(FlutterEngineTest, ThreadSynchronizerNotBlockingRasterThreadAfterShutdown) { FlutterThreadSynchronizer* threadSynchronizer = [[FlutterThreadSynchronizer alloc] init]; [threadSynchronizer shutdown]; @@ -695,17 +698,54 @@ - (nonnull NSView*)createWithViewIdentifier:(int64_t)viewId arguments:(nullable EXPECT_EQ(viewController1.id, 0ull); } -TEST(FlutterEngineTerminationHandlerTest, HandlesTerminationRequest) { +TEST_F(FlutterEngineTest, HandlesTerminationRequest) { id engineMock = CreateMockFlutterEngine(nil); - - __block FlutterAppExitResponse calledAfterTerminate = kFlutterAppExitResponseCancel; + __block NSString* nextResponse = @"exit"; + __block BOOL triedToTerminate = FALSE; + FlutterEngineTerminationHandler* terminationHandler = + [[FlutterEngineTerminationHandler alloc] initWithEngine:engineMock + terminator:^(id sender) { + triedToTerminate = TRUE; + // Don't actually terminate, of course. + }]; + OCMStub([engineMock terminationHandler]).andReturn(terminationHandler); + id binaryMessengerMock = OCMProtocolMock(@protocol(FlutterBinaryMessenger)); + OCMStub( // NOLINT(google-objc-avoid-throwing-exception) + [engineMock binaryMessenger]) + .andReturn(binaryMessengerMock); + OCMStub([engineMock sendOnChannel:@"flutter/platform" + message:[OCMArg any] + binaryReply:[OCMArg any]]) + .andDo((^(NSInvocation* invocation) { + [invocation retainArguments]; + FlutterBinaryReply callback; + [invocation getArgument:&callback atIndex:4]; + NSDictionary* responseDict = @{@"response" : nextResponse}; + NSData* returnedMessage = + [[FlutterJSONMethodCodec sharedInstance] encodeSuccessEnvelope:responseDict]; + callback(returnedMessage); + })); + __block NSString* calledAfterTerminate = @""; FlutterResult appExitResult = ^(id result) { - NSLog(@"Response: %@", result); + NSDictionary* resultDict = result; + calledAfterTerminate = resultDict[@"response"]; }; - FlutterMethodCall* methodCallAfterClear = - [FlutterMethodCall methodCallWithMethodName:@"System.exitApplication" arguments:nil]; - [engineMock handleMethodCall:methodCallAfterClear result:appExitResult]; - EXPECT_EQ(kFlutterAppExitResponseCancel, calledAfterTerminate); + FlutterMethodCall* methodExitApplication = + [FlutterMethodCall methodCallWithMethodName:@"System.exitApplication" + arguments:@{@"type" : @"cancelable"}]; + + [engineMock handleMethodCall:methodExitApplication result:appExitResult]; + + triedToTerminate = FALSE; + nextResponse = @"exit"; + EXPECT_STREQ([calledAfterTerminate UTF8String], "exit"); + EXPECT_TRUE(triedToTerminate); + + triedToTerminate = FALSE; + nextResponse = @"cancel"; + [engineMock handleMethodCall:methodExitApplication result:appExitResult]; + EXPECT_STREQ([calledAfterTerminate UTF8String], "cancel"); + EXPECT_FALSE(triedToTerminate); } } // namespace 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 90f87694dd879..c48ed7150239d 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h @@ -16,6 +16,50 @@ NS_ASSUME_NONNULL_BEGIN +#pragma mark - Typedefs + +typedef void (^FlutterTerminationCallback)(id _Nullable sender); + +#pragma mark - Enumerations + +/** + * An enum for defining the different request types allowed when requesting an + * application exit. + * + * Must match the entries in the `AppExitType` enum in the Dart code. + */ +typedef NS_ENUM(NSInteger, FlutterAppExitType) { + kFlutterAppExitTypeCancelable = 0, + kFlutterAppExitTypeRequired = 1, +}; + +/** + * An enum for defining the different responses the framework can give to an + * application exit request from the engine. + * + * Must match the entries in the `AppExitResponse` enum in the Dart code. + */ +typedef NS_ENUM(NSInteger, FlutterAppExitResponse) { + kFlutterAppExitResponseCancel = 0, + kFlutterAppExitResponseExit = 1, +}; + +#pragma mark - FlutterEngineTerminationHandler + +/** + * A handler interface for handling application termination that the + * FlutterAppDelegate can use to coordinate an application exit by sending + * messages through the platform channel managed by the engine. + */ +@interface FlutterEngineTerminationHandler : NSObject +- (instancetype)initWithEngine:(FlutterEngine*)engine + terminator:(nullable FlutterTerminationCallback)terminator; +- (void)requestAppExit:(NSDictionary*)data result:(FlutterResult)result; +- (void)requestApplicationTermination:(FlutterApplication*)sender + exitType:(FlutterAppExitType)type + result:(nullable FlutterResult)result; +@end + @interface FlutterEngine () /** @@ -55,6 +99,11 @@ NS_ASSUME_NONNULL_BEGIN */ @property(nonatomic, readonly) std::vector switches; +/** + * Provides the |FlutterEngineTerminationHandler| to be used for this engine. + */ +@property(nonatomic, readonly) FlutterEngineTerminationHandler* terminationHandler; + /** * Attach a view controller to the engine as its default controller. * @@ -131,43 +180,4 @@ NS_ASSUME_NONNULL_BEGIN @end -#pragma mark - Enumerations - -/** - * An enum for defining the different request types allowed when requesting an - * application exit. - * - * Must match the entries in the `AppExitType` enum in the Dart code. - */ -typedef NS_ENUM(NSInteger, FlutterAppExitType) { - kFlutterAppExitTypeCancelable = 0, - kFlutterAppExitTypeRequired = 1, -}; - -/** - * An enum for defining the different responses the framework can give to an - * application exit request from the engine. - * - * Must match the entries in the `AppExitResponse` enum in the Dart code. - */ -typedef NS_ENUM(NSInteger, FlutterAppExitResponse) { - kFlutterAppExitResponseCancel = 0, - kFlutterAppExitResponseExit = 1, -}; - -#pragma mark - FlutterEngineTerminationHandler - -/** - * A handler interface for handling application termination that the - * FlutterAppDelegate can use to coordinate an application exit by sending - * messages through the platform channel managed by the engine. - */ -@interface FlutterEngineTerminationHandler : NSObject -- (instancetype)initWithEngine:(FlutterEngine*)engine; -- (void)requestAppExit:(NSDictionary*)data result:(FlutterResult)result; -- (void)requestApplicationTermination:(FlutterApplication*)sender - exitType:(FlutterAppExitType)type - result:(nullable FlutterResult)result; -@end - NS_ASSUME_NONNULL_END From 7e8f7444a672bbeb8a9a2374630902184bb053d5 Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Mon, 27 Feb 2023 15:07:27 -0800 Subject: [PATCH 05/15] Rename FlutterAppDelegate_internal.h --- ...ppDelegate_internal.h => FlutterAppDelegate_Internal.h} | 0 .../darwin/macos/framework/Source/FlutterApplication.mm | 7 +++++-- 2 files changed, 5 insertions(+), 2 deletions(-) rename shell/platform/darwin/macos/framework/Source/{FlutterAppDelegate_internal.h => FlutterAppDelegate_Internal.h} (100%) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_internal.h b/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_Internal.h similarity index 100% rename from shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_internal.h rename to shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_Internal.h diff --git a/shell/platform/darwin/macos/framework/Source/FlutterApplication.mm b/shell/platform/darwin/macos/framework/Source/FlutterApplication.mm index ac94443fc0cce..36d125fd6aef3 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterApplication.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterApplication.mm @@ -62,9 +62,12 @@ + (NSApplication*)sharedApplication { // it if it is OK to terminate. When that method channel call returns with a // result, the application either terminates or continues running. - (void)terminate:(id)sender { - FlutterEngineTerminationHandler* terminationHandler = [static_cast([NSApp delegate]) terminationHandler]; + FlutterEngineTerminationHandler* terminationHandler = + [static_cast([NSApp delegate]) terminationHandler]; if (terminationHandler) { - [terminationHandler requestApplicationTermination:self exitType:kFlutterAppExitTypeCancelable result:nil]; + [terminationHandler requestApplicationTermination:self + exitType:kFlutterAppExitTypeCancelable + result:nil]; } else { // If there's no termination handler, then just terminate. [super terminate:sender]; From ba8562afcc398cf3bf3b87bb2c0de3a14730673f Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Mon, 27 Feb 2023 15:26:20 -0800 Subject: [PATCH 06/15] Fix test --- .../macos/framework/Source/FlutterAppDelegate_Internal.h | 2 +- .../darwin/macos/framework/Source/FlutterEngineTest.mm | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_Internal.h b/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_Internal.h index 8234db058feb5..42ae1afa6b30a 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_Internal.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_Internal.h @@ -15,7 +15,7 @@ * Holds a weak reference to the termination handler owned by the engine. * Called by the |FlutterApplication| when termination is requested by the OS. */ -@property(readwrite, weak) FlutterEngineTerminationHandler* terminationHandler; +@property(readwrite, nullable, weak) FlutterEngineTerminationHandler* terminationHandler; @end diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm index 6fe0ecac0c2c8..adbb01b11296d 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm @@ -7,6 +7,7 @@ #import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterEngine.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h" #include "gtest/gtest.h" +#include "shell/platform/darwin/macos/framework/Headers/FlutterApplication.h" #include #include @@ -734,10 +735,9 @@ - (nonnull NSView*)createWithViewIdentifier:(int64_t)viewId arguments:(nullable [FlutterMethodCall methodCallWithMethodName:@"System.exitApplication" arguments:@{@"type" : @"cancelable"}]; - [engineMock handleMethodCall:methodExitApplication result:appExitResult]; - triedToTerminate = FALSE; nextResponse = @"exit"; + [engineMock handleMethodCall:methodExitApplication result:appExitResult]; EXPECT_STREQ([calledAfterTerminate UTF8String], "exit"); EXPECT_TRUE(triedToTerminate); From 7d41ab4e725274442a13d939d42062e3346bc2af Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Mon, 27 Feb 2023 15:47:23 -0800 Subject: [PATCH 07/15] Adjust name --- .../platform/darwin/macos/framework/Source/FlutterEngine.mm | 5 +++-- .../darwin/macos/framework/Source/FlutterEngine_Internal.h | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm index d1cf633a373f0..205f89c97c014 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm @@ -181,7 +181,8 @@ - (instancetype)initWithEngine:(FlutterEngine*)engine // This is called by the method call handler in the engine when the application // requests termination itself. -- (void)requestAppExit:(NSDictionary*)arguments result:(FlutterResult)result { +- (void)handleRequestAppExitMethodCall:(NSDictionary*)arguments + result:(FlutterResult)result { NSString* type = arguments[@"type"]; // Ignore the "exitCode" value in the arguments because AppKit doesn't have // any good way to set the process exit code other than calling exit(), and @@ -963,7 +964,7 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { } else if ([call.method isEqualToString:@"Clipboard.hasStrings"]) { result(@{@"value" : @([self clipboardHasStrings])}); } else if ([call.method isEqualToString:@"System.exitApplication"]) { - [[self terminationHandler] requestAppExit:call.arguments result:result]; + [[self terminationHandler] handleRequestAppExitMethodCall:call.arguments result:result]; } else { result(FlutterMethodNotImplemented); } diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h b/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h index c48ed7150239d..bbc4527543c9a 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h @@ -54,7 +54,8 @@ typedef NS_ENUM(NSInteger, FlutterAppExitResponse) { @interface FlutterEngineTerminationHandler : NSObject - (instancetype)initWithEngine:(FlutterEngine*)engine terminator:(nullable FlutterTerminationCallback)terminator; -- (void)requestAppExit:(NSDictionary*)data result:(FlutterResult)result; +- (void)handleRequestAppExitMethodCall:(NSDictionary*)data + result:(FlutterResult)result; - (void)requestApplicationTermination:(FlutterApplication*)sender exitType:(FlutterAppExitType)type result:(nullable FlutterResult)result; From 8aa06d2b6278fde7ab3eb14c6c8fbbbf9c509ba2 Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Mon, 27 Feb 2023 16:07:54 -0800 Subject: [PATCH 08/15] Fix license filename case --- ci/licenses_golden/licenses_flutter | 4 ++-- .../darwin/macos/framework/Source/FlutterAppDelegate.mm | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 87df382bc0718..fed5dec2eabc6 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -2562,7 +2562,7 @@ ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/Accessibil ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMac.mm + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMacTest.mm + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate.mm + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_internal.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_Internal.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterApplication.mm + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterBackingStore.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterBackingStore.mm + ../../../flutter/LICENSE @@ -5099,7 +5099,7 @@ FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/Accessibilit FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMac.mm FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMacTest.mm FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate.mm -FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_internal.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_Internal.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterApplication.mm FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterBackingStore.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterBackingStore.mm diff --git a/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate.mm b/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate.mm index 03a3f904dd7d5..48ffdcbec794c 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate.mm @@ -21,6 +21,9 @@ - (NSString*)applicationName; @implementation FlutterAppDelegate +// TODO(stuartmorgan): Implement application lifecycle forwarding to plugins here, as is done +// on iOS. Currently macOS plugins don't have access to lifecycle messages. + - (instancetype)init { if (self = [super init]) { _terminationHandler = nil; From 651b0a9c0cd29582a86a5da8d44572a32c596d9c Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Mon, 27 Feb 2023 17:09:00 -0800 Subject: [PATCH 09/15] Clean up crash when NSApp wasn't a FlutterApplication --- .../framework/Source/FlutterApplication.mm | 39 ++++++++++--------- .../macos/framework/Source/FlutterEngine.mm | 7 +++- 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterApplication.mm b/shell/platform/darwin/macos/framework/Source/FlutterApplication.mm index 36d125fd6aef3..411e57dfbd288 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterApplication.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterApplication.mm @@ -22,17 +22,19 @@ + (NSApplication*)sharedApplication { // +sharedApplication initializes the global NSApp, so if we're delivering // something other than a FlutterApplication, warn the developer once. #ifndef FLUTTER_RELEASE - static bool notified = false; - if (!notified && ![NSApp isKindOfClass:[FlutterApplication class]]) { - NSLog(@"NSApp should be of type %s, not %s. " - "Some application lifecycle requests (e.g. ServicesBinding.exitApplication) " - "and notifications will be unavailable.\n" - "Modify the application's NSPrincipleClass to be %s" - "in the Info.plist to fix this.", - [[self className] UTF8String], [[NSApp className] UTF8String], - [[self className] UTF8String]); - notified = true; - } + static dispatch_once_t onceToken = 0; + dispatch_once(&onceToken, ^{ + if (![app respondsToSelector:@selector(terminateApplication:)]) { + NSLog(@"NSApp should be of type %s, not %s.\n" + "System requests for the application to terminate will not be sent to " + "the Flutter framework, so the framework will be unable to cancel " + "those requests.\n" + "Modify the application's NSPrincipleClass to be %s in the " + "Info.plist to fix this.", + [[self className] UTF8String], [[NSApp className] UTF8String], + [[self className] UTF8String]); + } + }); #endif // !FLUTTER_RELEASE return app; } @@ -62,16 +64,17 @@ + (NSApplication*)sharedApplication { // it if it is OK to terminate. When that method channel call returns with a // result, the application either terminates or continues running. - (void)terminate:(id)sender { - FlutterEngineTerminationHandler* terminationHandler = - [static_cast([NSApp delegate]) terminationHandler]; - if (terminationHandler) { - [terminationHandler requestApplicationTermination:self - exitType:kFlutterAppExitTypeCancelable - result:nil]; - } else { + FlutterAppDelegate* delegate = [self delegate]; + if (!delegate || ![delegate respondsToSelector:@selector(terminationHandler)] || + [delegate terminationHandler] == nil) { // If there's no termination handler, then just terminate. [super terminate:sender]; } + FlutterEngineTerminationHandler* terminationHandler = + [static_cast([self delegate]) terminationHandler]; + [terminationHandler requestApplicationTermination:sender + exitType:kFlutterAppExitTypeCancelable + result:nil]; // Return, don't exit. The application delegate is responsible for exiting on // its own by calling |-terminateApplication|. } diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm index 205f89c97c014..4f433fca5a8bf 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm @@ -171,7 +171,12 @@ - (instancetype)initWithEngine:(FlutterEngine*)engine _terminator = terminator ? terminator : ^(id sender) { // Default to actually terminating the application. The terminator exists to // allow tests to override it so that an actual exit doesn't occur. - [[FlutterApplication sharedApplication] terminateApplication:sender]; + FlutterApplication* flutterApp = [FlutterApplication sharedApplication]; + if (flutterApp && [flutterApp respondsToSelector:@selector(terminateApplication:)]) { + [[FlutterApplication sharedApplication] terminateApplication:sender]; + } else if (flutterApp) { + [flutterApp terminate:sender]; + } }; FlutterAppDelegate* appDelegate = (FlutterAppDelegate*)[[FlutterApplication sharedApplication] delegate]; From 9930fc11b418ee47f885dc71489515316f52d732 Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Wed, 1 Mar 2023 09:55:34 -0800 Subject: [PATCH 10/15] Review Changes --- lib/ui/platform_dispatcher.dart | 13 +++++++------ .../macos/framework/Headers/FlutterApplication.h | 2 +- .../macos/framework/Source/FlutterAppDelegate.mm | 3 ++- .../darwin/macos/framework/Source/FlutterEngine.mm | 1 - .../macos/framework/Source/FlutterEngineTest.mm | 4 +--- 5 files changed, 11 insertions(+), 12 deletions(-) diff --git a/lib/ui/platform_dispatcher.dart b/lib/ui/platform_dispatcher.dart index 33cf2594f7995..81c959452ffa3 100644 --- a/lib/ui/platform_dispatcher.dart +++ b/lib/ui/platform_dispatcher.dart @@ -1736,9 +1736,9 @@ enum AppLifecycleState { /// The possible responses to a request to exit the application. /// /// The request is typically responded to by a [WidgetsBindingObserver]. -// TODO(gspencergoog): Insert references here to AppLifecycleListener and to the -// actual function called on WidgetsBindingObserver once those have landed in -// the framework. +// TODO(gspencergoog): Insert doc references here to AppLifecycleListener and to +// the actual function called on WidgetsBindingObserver once those have landed +// in the framework. https://github.com/flutter/flutter/issues/121721 enum AppExitResponse { /// Exiting the application can proceed. exit, @@ -1748,13 +1748,14 @@ enum AppExitResponse { /// The type of application exit to perform when calling /// `ServicesBinding.exitApplication`. -// TODO(gspencergoog): Insert references here to ServicesBinding.exitApplication -// that has landed in the framework. +// TODO(gspencergoog): Insert doc references here to +// ServicesBinding.exitApplication that has landed in the framework. +// https://github.com/flutter/flutter/issues/121721 enum AppExitType { /// Requests that the application start an orderly exit, sending a request /// back to the framework through the [WidgetsBinding], and if that responds /// with [AppExitResponse.exit], then proceed with the same steps as a - /// [required] exit. if that responds with [AppExitResponse.cancel], then the + /// [required] exit. If that responds with [AppExitResponse.cancel], then the /// exit request is canceled and the application continues executing normally. cancelable, diff --git a/shell/platform/darwin/macos/framework/Headers/FlutterApplication.h b/shell/platform/darwin/macos/framework/Headers/FlutterApplication.h index 54e5305dfec2b..31083f4e7fc0c 100644 --- a/shell/platform/darwin/macos/framework/Headers/FlutterApplication.h +++ b/shell/platform/darwin/macos/framework/Headers/FlutterApplication.h @@ -48,7 +48,7 @@ * application: that is left up to the engine, which will call this function if * it decides that termination request is granted, which will start the regular * Cocoa flow for terminating the application, calling - * |applicationShouldTermnate|, etc. + * |applicationShouldTerminate|, etc. * * @param(sender) The id of the object requesting the termination, or nil. */ diff --git a/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate.mm b/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate.mm index 48ffdcbec794c..e9d1060913c71 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate.mm @@ -21,8 +21,9 @@ - (NSString*)applicationName; @implementation FlutterAppDelegate -// TODO(stuartmorgan): Implement application lifecycle forwarding to plugins here, as is done +// TODO(gspencergoog): Implement application lifecycle forwarding to plugins here, as is done // on iOS. Currently macOS plugins don't have access to lifecycle messages. +// https://github.com/flutter/flutter/issues/30735 - (instancetype)init { if (self = [super init]) { diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm index 4f433fca5a8bf..cb1d4b6732a82 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm @@ -3,7 +3,6 @@ // found in the LICENSE file. #import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterEngine.h" -#include "FlutterCodecs.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h" #include diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm index adbb01b11296d..73596f896bc3d 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm @@ -2,12 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "OCMock/OCMArg.h" -#include "OCMock/OCMock.h" #import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterEngine.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h" #include "gtest/gtest.h" -#include "shell/platform/darwin/macos/framework/Headers/FlutterApplication.h" #include #include @@ -15,6 +12,7 @@ #include "flutter/fml/synchronization/waitable_event.h" #include "flutter/lib/ui/window/platform_message.h" #include "flutter/shell/platform/common/accessibility_bridge.h" +#import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterApplication.h" #import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterAppDelegate.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterEngineTestUtils.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTestUtils.h" From 27902b386ac63c12c9b90edbf1253e11dfda8399 Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Wed, 1 Mar 2023 09:55:34 -0800 Subject: [PATCH 11/15] Review Changes --- .../platform/darwin/macos/framework/Source/FlutterEngineTest.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm index 73596f896bc3d..fc105dd710bdd 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm @@ -12,8 +12,8 @@ #include "flutter/fml/synchronization/waitable_event.h" #include "flutter/lib/ui/window/platform_message.h" #include "flutter/shell/platform/common/accessibility_bridge.h" -#import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterApplication.h" #import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterAppDelegate.h" +#import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterApplication.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterEngineTestUtils.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTestUtils.h" #include "flutter/shell/platform/embedder/embedder.h" From 311ee9e62db99566c2f9ccec00bf13c233779715 Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Wed, 1 Mar 2023 13:03:27 -0800 Subject: [PATCH 12/15] Fix includes --- .../darwin/macos/framework/Source/FlutterAppDelegate.mm | 2 +- .../darwin/macos/framework/Source/FlutterAppDelegate_Internal.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate.mm b/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate.mm index e9d1060913c71..269d58f71a86b 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate.mm @@ -3,11 +3,11 @@ // found in the LICENSE file. #import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterAppDelegate.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_Internal.h" #import #include "flutter/fml/logging.h" -#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_Internal.h" #include "flutter/shell/platform/embedder/embedder.h" @interface FlutterAppDelegate () diff --git a/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_Internal.h b/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_Internal.h index 42ae1afa6b30a..b811b9995a2c2 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_Internal.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_Internal.h @@ -5,7 +5,6 @@ #ifndef FLUTTER_FLUTTERAPPDELEGATE_INTERNAL_H_ #define FLUTTER_FLUTTERAPPDELEGATE_INTERNAL_H_ -#include "embedder.h" #import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterAppDelegate.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h" From 2a565c934c526f871ced7a7162541ef261afa520 Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Wed, 1 Mar 2023 13:14:16 -0800 Subject: [PATCH 13/15] Move terminateApplication to an internal header --- .../framework/Headers/FlutterApplication.h | 11 -------- .../framework/Source/FlutterApplication.mm | 1 + .../Source/FlutterApplication_Internal.h | 27 +++++++++++++++++++ .../macos/framework/Source/FlutterEngine.mm | 1 + 4 files changed, 29 insertions(+), 11 deletions(-) create mode 100644 shell/platform/darwin/macos/framework/Source/FlutterApplication_Internal.h diff --git a/shell/platform/darwin/macos/framework/Headers/FlutterApplication.h b/shell/platform/darwin/macos/framework/Headers/FlutterApplication.h index 31083f4e7fc0c..38b5439ede7e8 100644 --- a/shell/platform/darwin/macos/framework/Headers/FlutterApplication.h +++ b/shell/platform/darwin/macos/framework/Headers/FlutterApplication.h @@ -42,17 +42,6 @@ * necessary from FlutterApplication.mm. */ @interface FlutterApplication : NSApplication - -/** - * FlutterApplication's implementation of |terminate| doesn't terminate the - * application: that is left up to the engine, which will call this function if - * it decides that termination request is granted, which will start the regular - * Cocoa flow for terminating the application, calling - * |applicationShouldTerminate|, etc. - * - * @param(sender) The id of the object requesting the termination, or nil. - */ -- (void)terminateApplication:(id)sender; @end #endif // FLUTTER_FLUTTERAPPLICATION_H_ diff --git a/shell/platform/darwin/macos/framework/Source/FlutterApplication.mm b/shell/platform/darwin/macos/framework/Source/FlutterApplication.mm index 411e57dfbd288..6aa4628e6f05c 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterApplication.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterApplication.mm @@ -3,6 +3,7 @@ // found in the LICENSE file. #import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterApplication.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterApplication_Internal.h" #include "flutter/shell/platform/embedder/embedder.h" #import "shell/platform/darwin/macos/framework/Headers/FlutterAppDelegate.h" diff --git a/shell/platform/darwin/macos/framework/Source/FlutterApplication_Internal.h b/shell/platform/darwin/macos/framework/Source/FlutterApplication_Internal.h new file mode 100644 index 0000000000000..0a79b225ba144 --- /dev/null +++ b/shell/platform/darwin/macos/framework/Source/FlutterApplication_Internal.h @@ -0,0 +1,27 @@ +// 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_FLUTTERAPPLICATION_INTERNAL_H_ +#define FLUTTER_FLUTTERAPPLICATION_INTERNAL_H_ + +#import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterApplication.h" + +/** + * Define |terminateApplication| for internal use. + */ +@interface FlutterApplication () + +/** + * FlutterApplication's implementation of |terminate| doesn't terminate the + * application: that is left up to the engine, which will call this function if + * it decides that termination request is granted, which will start the regular + * Cocoa flow for terminating the application, calling + * |applicationShouldTerminate|, etc. + * + * @param(sender) The id of the object requesting the termination, or nil. + */ +- (void)terminateApplication:(id)sender; +@end + +#endif // FLUTTER_FLUTTERAPPLICATION_INTERNAL_H_ diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm index cb1d4b6732a82..6c33c3dd372eb 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm @@ -15,6 +15,7 @@ #import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterAppDelegate.h" #import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterApplication.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_Internal.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterApplication_Internal.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterCompositor.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterDartProject_Internal.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterMenuPlugin.h" From d35a916a824ded99c274a3348415f87f19e9bdcf Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Wed, 1 Mar 2023 13:31:53 -0800 Subject: [PATCH 14/15] Update license file --- ci/licenses_golden/licenses_flutter | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index fed5dec2eabc6..c7359b0f560d2 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -2564,6 +2564,7 @@ ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/Accessibil ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate.mm + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_Internal.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterApplication.mm + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterApplication_Internal.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterBackingStore.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterBackingStore.mm + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterChannelKeyResponder.h + ../../../flutter/LICENSE @@ -5101,6 +5102,7 @@ FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/Accessibilit FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate.mm FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_Internal.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterApplication.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterApplication_Internal.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterBackingStore.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterBackingStore.mm FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterChannelKeyResponder.h From c5727b0803dda2f2f630c95a014a05e2ed1513b4 Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Thu, 2 Mar 2023 08:21:46 -0800 Subject: [PATCH 15/15] Comment fixes --- lib/ui/platform_dispatcher.dart | 2 +- .../darwin/macos/framework/Source/FlutterApplication.mm | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/ui/platform_dispatcher.dart b/lib/ui/platform_dispatcher.dart index 81c959452ffa3..cac65e9858d7e 100644 --- a/lib/ui/platform_dispatcher.dart +++ b/lib/ui/platform_dispatcher.dart @@ -1753,7 +1753,7 @@ enum AppExitResponse { // https://github.com/flutter/flutter/issues/121721 enum AppExitType { /// Requests that the application start an orderly exit, sending a request - /// back to the framework through the [WidgetsBinding], and if that responds + /// back to the framework through the [WidgetsBinding]. If that responds /// with [AppExitResponse.exit], then proceed with the same steps as a /// [required] exit. If that responds with [AppExitResponse.cancel], then the /// exit request is canceled and the application continues executing normally. diff --git a/shell/platform/darwin/macos/framework/Source/FlutterApplication.mm b/shell/platform/darwin/macos/framework/Source/FlutterApplication.mm index 6aa4628e6f05c..5667dfa1f5a37 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterApplication.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterApplication.mm @@ -14,7 +14,7 @@ // Flutter features, like application lifecycle handling. @implementation FlutterApplication -// Initialize NSApplication using the custom subclass. Check whether NSApp was +// Initialize NSApplication using the custom subclass. Check whether NSApp was // already initialized using another class, because that would break some // things. Warn about the mismatch only once, and only in debug builds. + (NSApplication*)sharedApplication { @@ -47,7 +47,7 @@ + (NSApplication*)sharedApplication { // and shutdown. // // We override the normal |terminate| implementation. Our implementation, which -// is specific to the asyncronous nature of Flutter, works by asking the +// is specific to the asynchronous nature of Flutter, works by asking the // application delegate to terminate using its |requestApplicationTermination| // method instead of going through |applicationShouldTerminate|. // @@ -77,7 +77,7 @@ - (void)terminate:(id)sender { exitType:kFlutterAppExitTypeCancelable result:nil]; // Return, don't exit. The application delegate is responsible for exiting on - // its own by calling |-terminateApplication|. + // its own by calling |terminateApplication|. } // Starts the regular Cocoa application termination flow, so that plugins will