Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
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
2 changes: 1 addition & 1 deletion app/src/invites/ios/invites_ios_startup.mm
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ @implementation UIApplication (FIRFBI)
+ (void)load {
// C++ constructors may not be called yet so call NSLog rather than LogDebug.
NSLog(@"Loading UIApplication category for Firebase App");
::firebase::util::ForEachAppDelegateClass(^(Class clazz) {
::firebase::util::RunOnAppDelegate(^(Class clazz) { // Renamed here
::firebase::invites::HookAppDelegateMethods(clazz);
});
}
Expand Down
2 changes: 1 addition & 1 deletion app/src/util_ios.h
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ typedef BOOL (
// Call the given block once for every Objective-C class that exists that
// implements the UIApplicationDelegate protocol (except for those in a
// blacklist we keep).
void ForEachAppDelegateClass(void (^block)(Class));
void RunOnAppDelegate(void (^block)(Class));

// Convert a string array into an NSMutableArray.
NSMutableArray *StringVectorToNSMutableArray(
Expand Down
144 changes: 113 additions & 31 deletions app/src/util_ios.mm
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
#include "app/src/util_ios.h"

#include "app/src/assert.h"
#include "app/src/include/firebase/internal/common.h"
#include "app/src/log.h"

#include <assert.h>
#include <stdlib.h>
Expand All @@ -27,6 +25,98 @@
#import <UIKit/UIKit.h>
#import <objc/runtime.h>

#define MAX_PENDING_APP_DELEGATE_BLOCKS 8
#define MAX_SEEN_DELEGATE_CLASSES 32

static IMP g_original_setDelegate_imp = NULL;
// static Class g_app_delegate_class = nil; // Removed
static void (^g_pending_app_delegate_blocks[MAX_PENDING_APP_DELEGATE_BLOCKS])(Class) = {nil};
static int g_pending_block_count = 0;

static Class g_seen_delegate_classes[MAX_SEEN_DELEGATE_CLASSES] = {nil};
static int g_seen_delegate_classes_count = 0;

// Swizzled implementation of setDelegate:
static void Firebase_setDelegate(id self, SEL _cmd, id<UIApplicationDelegate> delegate) {
Class new_class = nil;
if (delegate) {
new_class = [delegate class];
NSLog(@"Firebase: UIApplication setDelegate: called with class %s (Swizzled)",
class_getName(new_class));
} else {
NSLog(@"Firebase: UIApplication setDelegate: called with nil delegate (Swizzled)");
}

if (new_class) {
bool already_seen = false;
for (int i = 0; i < g_seen_delegate_classes_count; i++) {
if (g_seen_delegate_classes[i] == new_class) {
already_seen = true;
break;
}
}

if (!already_seen) {
if (g_seen_delegate_classes_count < MAX_SEEN_DELEGATE_CLASSES) {
g_seen_delegate_classes[g_seen_delegate_classes_count] = new_class;
g_seen_delegate_classes_count++;
NSLog(@"Firebase: Added new delegate class %s to seen list (total seen: %d).",
class_getName(new_class), g_seen_delegate_classes_count);

if (g_pending_block_count > 0) {
NSLog(@"Firebase: Executing %d pending block(s) for new delegate class: %s.",
g_pending_block_count, class_getName(new_class));
for (int i = 0; i < g_pending_block_count; i++) {
if (g_pending_app_delegate_blocks[i]) {
g_pending_app_delegate_blocks[i](new_class);
// Pending blocks are not cleared here; they persist to be run by RunOnAppDelegate
// for all seen delegate classes, and for any future new delegate classes.
}
}
}
} else {
NSLog(@"Firebase Error: Exceeded MAX_SEEN_DELEGATE_CLASSES (%d). Cannot add new delegate class %s or run pending blocks for it.",
MAX_SEEN_DELEGATE_CLASSES, class_getName(new_class));
}
} else {
NSLog(@"Firebase: Delegate class %s already seen. Not re-processing pending blocks for it here.", class_getName(new_class));
}
}

// Call the original setDelegate: implementation
if (g_original_setDelegate_imp) {
((void (*)(id, SEL, id<UIApplicationDelegate>))g_original_setDelegate_imp)(self, _cmd, delegate);
} else {
NSLog(@"Firebase Error: Original setDelegate: IMP not found, cannot call original method.");
}
}

@implementation UIApplication (FirebaseAppDelegateSwizzling)

+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class uiApplicationClass = [UIApplication class];
SEL originalSelector = @selector(setDelegate:);
Method originalMethod = class_getInstanceMethod(uiApplicationClass, originalSelector);

if (!originalMethod) {
NSLog(@"Firebase Error: Original [UIApplication setDelegate:] method not found for swizzling.");
return;
}

IMP previousImp = method_setImplementation(originalMethod, (IMP)Firebase_setDelegate);
if (previousImp) {
g_original_setDelegate_imp = previousImp;
NSLog(@"Firebase: Successfully swizzled [UIApplication setDelegate:] and stored original IMP.");
} else {
NSLog(@"Firebase Error: Swizzled [UIApplication setDelegate:], but original IMP was NULL (or method_setImplementation failed).");
}
});
}

@end

@implementation FIRSAMAppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
Expand Down Expand Up @@ -79,38 +169,30 @@ - (BOOL)application:(UIApplication *)application
namespace firebase {
namespace util {

void ForEachAppDelegateClass(void (^block)(Class)) {
unsigned int number_of_classes;
Class *classes = objc_copyClassList(&number_of_classes);
for (unsigned int i = 0; i < number_of_classes; i++) {
Class clazz = classes[i];
if (class_conformsToProtocol(clazz, @protocol(UIApplicationDelegate))) {
const char *class_name = class_getName(clazz);
bool blacklisted = false;
static const char *kClassNameBlacklist[] = {
// Declared in Firebase Analytics:
// //googlemac/iPhone/Firebase/Analytics/Sources/ApplicationDelegate/
// FIRAAppDelegateProxy.m
"FIRAAppDelegate",
// Declared here.
"FIRSAMAppDelegate"};
for (size_t i = 0; i < FIREBASE_ARRAYSIZE(kClassNameBlacklist); ++i) {
if (strcmp(class_name, kClassNameBlacklist[i]) == 0) {
blacklisted = true;
break;
}
}
if (!blacklisted) {
if (GetLogLevel() <= kLogLevelDebug) {
// Call NSLog directly because we may be in a +load method,
// and C++ classes may not be constructed yet.
NSLog(@"Firebase: Found UIApplicationDelegate class %s", class_name);
}
block(clazz);
void RunOnAppDelegate(void (^block)(Class)) {
if (g_seen_delegate_classes_count > 0) {
NSLog(@"Firebase: RunOnAppDelegate executing block for %d already seen delegate class(es).",
g_seen_delegate_classes_count);
for (int i = 0; i < g_seen_delegate_classes_count; i++) {
// Assuming classes in g_seen_delegate_classes up to count are non-nil
if (g_seen_delegate_classes[i]) { // Additional safety check
block(g_seen_delegate_classes[i]);
}
}
} else {
NSLog(@"Firebase: RunOnAppDelegate - no delegate classes seen yet. Block will be queued for future delegates.");
}

// Always try to queue the block for any future new delegate classes.
// This block will be executed by Firebase_setDelegate if a new delegate class is set.
if (g_pending_block_count < MAX_PENDING_APP_DELEGATE_BLOCKS) {
g_pending_app_delegate_blocks[g_pending_block_count] = [block copy];
g_pending_block_count++;
NSLog(@"Firebase: RunOnAppDelegate - added block to pending list (total pending: %d). This block will run on future new delegate classes.", g_pending_block_count);
} else {
NSLog(@"Firebase Error: RunOnAppDelegate - pending block queue is full (max %d). Cannot add new block for future execution. Discarding block.", MAX_PENDING_APP_DELEGATE_BLOCKS);
// Block is discarded for future execution.
}
free(classes);
}

NSDictionary *StringMapToNSDictionary(const std::map<std::string, std::string> &string_map) {
Expand Down
2 changes: 1 addition & 1 deletion messaging/src/ios/messaging.mm
Original file line number Diff line number Diff line change
Expand Up @@ -870,7 +870,7 @@ @implementation UIApplication (FIRFCM)
+ (void)load {
// C++ constructors may not be called yet so call NSLog rather than LogInfo.
NSLog(@"FCM: Loading UIApplication FIRFCM category");
::firebase::util::ForEachAppDelegateClass(^(Class clazz) {
::firebase::util::RunOnAppDelegate(^(Class clazz) { // Renamed here
FirebaseMessagingHookAppDelegate(clazz);
});
}
Expand Down
Loading