Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -2744,9 +2744,11 @@ 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/FlutterAppDelegateTest.mm + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_Internal.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterAppLifecycleDelegate.mm + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterAppLifecycleDelegateTest.mm + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterAppLifecycleDelegate_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
Expand Down Expand Up @@ -5488,9 +5490,11 @@ 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/FlutterAppDelegateTest.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_Internal.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterAppLifecycleDelegate.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterAppLifecycleDelegateTest.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterAppLifecycleDelegate_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
Expand Down
1 change: 1 addition & 0 deletions shell/platform/darwin/macos/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ executable("flutter_desktop_darwin_unittests") {

sources = [
"framework/Source/AccessibilityBridgeMacTest.mm",
"framework/Source/FlutterAppDelegateTest.mm",
"framework/Source/FlutterAppLifecycleDelegateTest.mm",
"framework/Source/FlutterChannelKeyResponderTest.mm",
"framework/Source/FlutterCompositorTest.mm",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,13 @@ FLUTTER_DARWIN_EXPORT
/**
* The application menu in the menu bar.
*/
@property(weak, nonatomic) IBOutlet NSMenu* applicationMenu;
@property(weak, nonatomic, nullable) IBOutlet NSMenu* applicationMenu;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I missed adding these when I added nullability annotations to the split-out delegate above, which causes warnings about not having full nullability annotation.


/**
* The primary application window containing a FlutterViewController. This is
* primarily intended for use in single-window applications.
*/
@property(weak, nonatomic) IBOutlet NSWindow* mainFlutterWindow;
@property(weak, nonatomic, nullable) IBOutlet NSWindow* mainFlutterWindow;

@end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,17 @@ FLUTTER_DARWIN_EXPORT
* Called when the |FlutterAppDelegate| gets the applicationDidUnhide
* notification.
*/
- (void)handleDidChangeOcclusionState:(NSNotification*)notification API_AVAILABLE(macos(10.9));
- (void)handleDidChangeOcclusionState:(NSNotification*)notification;

/**
* Called when the |FlutterAppDelegate| gets the application:openURLs:
* callback.
*
* Implementers should return YES if they handle the URLs, otherwise NO.
* Delegates will be called in order of registration, and once a delegate
* returns YES, no further delegates will reiceve this callback.
*/
- (BOOL)handleOpenURLs:(NSArray<NSURL*>*)urls;

/**
* Called when the |FlutterAppDelegate| gets the applicationWillTerminate
Expand Down
16 changes: 14 additions & 2 deletions shell/platform/darwin/macos/framework/Source/FlutterAppDelegate.mm
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#include "flutter/fml/logging.h"
#import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterAppLifecycleDelegate.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterAppLifecycleDelegate_Internal.h"
#include "flutter/shell/platform/embedder/embedder.h"

@interface FlutterAppDelegate ()
Expand Down Expand Up @@ -44,11 +45,11 @@ - (void)applicationWillFinishLaunching:(NSNotification*)notification {
#pragma mark - Delegate handling

- (void)addApplicationLifecycleDelegate:(NSObject<FlutterAppLifecycleDelegate>*)delegate {
[[self lifecycleRegistrar] addDelegate:delegate];
[self.lifecycleRegistrar addDelegate:delegate];
}

- (void)removeApplicationLifecycleDelegate:(NSObject<FlutterAppLifecycleDelegate>*)delegate {
[[self lifecycleRegistrar] removeDelegate:delegate];
[self.lifecycleRegistrar removeDelegate:delegate];
}

#pragma mark Private Methods
Expand All @@ -62,6 +63,17 @@ - (NSString*)applicationName {
return applicationName;
}

#pragma mark NSApplicationDelegate

- (void)application:(NSApplication*)application openURLs:(NSArray<NSURL*>*)urls {
for (NSObject<FlutterAppLifecycleDelegate>* delegate in self.lifecycleRegistrar.delegates) {
if ([delegate respondsToSelector:@selector(handleOpenURLs:)] &&
[delegate handleOpenURLs:urls]) {
return;
}
}
}

- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication* _Nonnull)sender {
// If the framework has already told us to terminate, terminate immediately.
if ([self terminationHandler] == nil || [[self terminationHandler] shouldTerminate]) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// 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/FlutterAppDelegate.h"

#import "flutter/testing/testing.h"
#include "third_party/googletest/googletest/include/gtest/gtest.h"

@interface AppDelegateNoopFlutterAppLifecycleDelegate : NSObject <FlutterAppLifecycleDelegate>
@property(nonatomic, copy, nullable) NSArray<NSURL*>* receivedURLs;
@end

@implementation AppDelegateNoopFlutterAppLifecycleDelegate
@end

@interface AppDelegateTestFlutterAppLifecycleDelegate : NSObject <FlutterAppLifecycleDelegate>
@property(nonatomic, copy, nullable) NSArray<NSURL*>* receivedURLs;
@end

@implementation AppDelegateTestFlutterAppLifecycleDelegate

- (BOOL)handleOpenURLs:(NSArray<NSURL*>*)urls {
self.receivedURLs = [urls copy];
return YES;
}

@end

namespace flutter::testing {

TEST(FlutterAppDelegateTest, DoesNotCallDelegatesWithoutHandler) {
FlutterAppDelegate* appDelegate = [[FlutterAppDelegate alloc] init];
AppDelegateNoopFlutterAppLifecycleDelegate* noopDelegate =
[[AppDelegateNoopFlutterAppLifecycleDelegate alloc] init];
[appDelegate addApplicationLifecycleDelegate:noopDelegate];

[appDelegate application:NSApplication.sharedApplication openURLs:@[]];
// No EXPECT, since the test is that the call doesn't throw due to calling without checking that
// the method is implemented.
}

TEST(FlutterAppDelegateTest, ReceivesOpenURLs) {
FlutterAppDelegate* appDelegate = [[FlutterAppDelegate alloc] init];
AppDelegateTestFlutterAppLifecycleDelegate* delegate =
[[AppDelegateTestFlutterAppLifecycleDelegate alloc] init];
[appDelegate addApplicationLifecycleDelegate:delegate];

NSArray<NSURL*>* URLs = @[ [NSURL URLWithString:@"https://flutter.dev"] ];
[appDelegate application:NSApplication.sharedApplication openURLs:URLs];

EXPECT_EQ([delegate receivedURLs], URLs);
}

TEST(FlutterAppDelegateTest, OperURLsStopsAfterHandled) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice - thanks for adding this one.

FlutterAppDelegate* appDelegate = [[FlutterAppDelegate alloc] init];
AppDelegateTestFlutterAppLifecycleDelegate* firstDelegate =
[[AppDelegateTestFlutterAppLifecycleDelegate alloc] init];
AppDelegateTestFlutterAppLifecycleDelegate* secondDelegate =
[[AppDelegateTestFlutterAppLifecycleDelegate alloc] init];
[appDelegate addApplicationLifecycleDelegate:firstDelegate];
[appDelegate addApplicationLifecycleDelegate:secondDelegate];

NSArray<NSURL*>* URLs = @[ [NSURL URLWithString:@"https://flutter.dev"] ];
[appDelegate application:NSApplication.sharedApplication openURLs:URLs];

EXPECT_EQ([firstDelegate receivedURLs], URLs);
EXPECT_EQ([secondDelegate receivedURLs], nil);
}

} // namespace flutter::testing
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// found in the LICENSE file.

#import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterAppLifecycleDelegate.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterAppLifecycleDelegate_Internal.h"

#include <AppKit/AppKit.h>
#include <AppKit/NSApplication.h>
Expand All @@ -12,14 +13,8 @@
#include "flutter/fml/logging.h"
#include "flutter/fml/paths.h"

@interface FlutterAppLifecycleRegistrar ()
@end

@implementation FlutterAppLifecycleRegistrar {
NSMutableArray* _notificationUnsubscribers;

// Weak references to registered plugins.
NSPointerArray* _delegates;
}

- (void)addObserverFor:(NSString*)name selector:(SEL)selector {
Expand Down Expand Up @@ -87,7 +82,7 @@ - (void)addDelegate:(NSObject<FlutterAppLifecycleDelegate>*)delegate {

- (void)removeDelegate:(NSObject<FlutterAppLifecycleDelegate>*)delegate {
NSUInteger index = [[_delegates allObjects] indexOfObject:delegate];
if (index >= 0) {
if (index != NSNotFound) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noticed while I was looking at the existing structure; the previous code was wrong since index is unsigned. NSNotFound is the unsigned version of -1, but not actually negative.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch.

[_delegates removePointerAtIndex:index];
}
}
Expand All @@ -101,9 +96,6 @@ - (void)removeDelegate:(NSObject<FlutterAppLifecycleDelegate>*)delegate {
#define DISTRIBUTE_NOTIFICATION(SELECTOR) \
-(void)handle##SELECTOR : (NSNotification*)notification { \
for (NSObject<FlutterAppLifecycleDelegate> * delegate in _delegates) { \
if (!delegate) { \
continue; \
} \
if ([delegate respondsToSelector:@selector(handle##SELECTOR:)]) { \
[delegate handle##SELECTOR:notification]; \
} \
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// 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 SHELL_PLATFORM_DARWIN_MACOS_FRAMEWORK_SOURCE_FLUTTERAPPLIFECYCLEDELEGATE_INTERNAL_H_
#define SHELL_PLATFORM_DARWIN_MACOS_FRAMEWORK_SOURCE_FLUTTERAPPLIFECYCLEDELEGATE_INTERNAL_H_

#import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterAppLifecycleDelegate.h"

@interface FlutterAppLifecycleRegistrar ()
/**
* Registered delegates. Exposed to allow FlutterAppDelegate to share the delegate list for
* handling non-notification delegation.
*/
@property(nonatomic, strong) NSPointerArray* delegates;
@end

#endif // SHELL_PLATFORM_DARWIN_MACOS_FRAMEWORK_SOURCE_FLUTTERAPPLIFECYCLEDELEGATE_INTERNAL_H_
Original file line number Diff line number Diff line change
Expand Up @@ -1232,7 +1232,7 @@ - (void)handleWillResignActive:(NSNotification*)notification {
* Called when the |FlutterAppDelegate| gets the applicationDidUnhide
* notification.
*/
- (void)handleDidChangeOcclusionState:(NSNotification*)notification API_AVAILABLE(macos(10.9)) {
- (void)handleDidChangeOcclusionState:(NSNotification*)notification {
NSApplicationOcclusionState occlusionState = [[NSApplication sharedApplication] occlusionState];
if (occlusionState & NSApplicationOcclusionStateVisible) {
_visible = YES;
Expand Down